Repository: 3ddelano/epic-online-services-godot Branch: main Commit: 6f5368bf027b Files: 276 Total size: 1.7 MB Directory structure: gitextract_ngm7nid4/ ├── .clang-format ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── build.yml │ └── deploy-docs.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .vscode/ │ ├── c_cpp_properties.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── LICENSE.md ├── README.md ├── SConstruct ├── build-ios.sh ├── debug-entitlements.plist ├── debug.cmd ├── debug.sh ├── docs/ │ ├── .gitignore │ ├── README.md │ ├── bun.lockb │ ├── docs/ │ │ ├── installation.md │ │ ├── introduction.md │ │ ├── sample-project.md │ │ ├── topics/ │ │ │ ├── authentication.md │ │ │ └── initialization.md │ │ └── update-guide.md │ ├── docusaurus.config.ts │ ├── package.json │ ├── postcss.config.js │ ├── sidebars.ts │ ├── src/ │ │ ├── components/ │ │ │ ├── ExampleSection.tsx │ │ │ ├── FeaturesSection.tsx │ │ │ ├── HeroSection.tsx │ │ │ ├── Icons.tsx │ │ │ ├── InfoSections.tsx │ │ │ ├── Layout.tsx │ │ │ ├── MainSystemsSection.tsx │ │ │ ├── MediaSections.tsx │ │ │ └── SupportSection.tsx │ │ ├── css/ │ │ │ └── custom.css │ │ ├── pages/ │ │ │ ├── index.tsx │ │ │ └── showcase.md │ │ └── plugins/ │ │ └── tailwind-config.ts │ ├── static/ │ │ └── .nojekyll │ └── tsconfig.json ├── function_analyzer.py ├── sample/ │ ├── .gitignore │ ├── AntiCheatServerMain.gd │ ├── AntiCheatServerMain.gd.uid │ ├── Main.gd │ ├── Main.gd.uid │ ├── Main.tscn │ ├── addons/ │ │ └── epic-online-services-godot/ │ │ ├── base_class.gd │ │ ├── base_class.gd.uid │ │ ├── bin/ │ │ │ └── .gitignore │ │ ├── dataclass.gd │ │ ├── dataclass.gd.uid │ │ ├── eos.gd │ │ ├── eos.gd.uid │ │ ├── eosg.gdextension │ │ ├── eosg.gdextension.uid │ │ ├── export_plugin.gd │ │ ├── export_plugin.gd.uid │ │ ├── heos/ │ │ │ ├── hachievement_data.gd │ │ │ ├── hachievement_data.gd.uid │ │ │ ├── hachievements.gd │ │ │ ├── hachievements.gd.uid │ │ │ ├── hauth.gd │ │ │ ├── hauth.gd.uid │ │ │ ├── hcredentials.gd │ │ │ ├── hcredentials.gd.uid │ │ │ ├── hfriends.gd │ │ │ ├── hfriends.gd.uid │ │ │ ├── hleaderboards.gd │ │ │ ├── hleaderboards.gd.uid │ │ │ ├── hlobbies.gd │ │ │ ├── hlobbies.gd.uid │ │ │ ├── hlobby.gd │ │ │ ├── hlobby.gd.uid │ │ │ ├── hlobbymember.gd │ │ │ ├── hlobbymember.gd.uid │ │ │ ├── hlog.gd │ │ │ ├── hlog.gd.uid │ │ │ ├── hp2p.gd │ │ │ ├── hp2p.gd.uid │ │ │ ├── hplatform.gd │ │ │ ├── hplatform.gd.uid │ │ │ ├── hsessions.gd │ │ │ ├── hsessions.gd.uid │ │ │ ├── hstats.gd │ │ │ └── hstats.gd.uid │ │ ├── plugin.cfg │ │ ├── plugin.gd │ │ ├── plugin.gd.uid │ │ ├── runtime.gd │ │ └── runtime.gd.uid │ ├── components/ │ │ └── StyledPopupWindow/ │ │ ├── StyledPopupWindow.gd │ │ ├── StyledPopupWindow.gd.uid │ │ ├── StyledPopupWindow.tscn │ │ └── StyledPopupWindowTheme.tres │ ├── dedicated_server_example/ │ │ ├── client_main.gd │ │ ├── client_main.gd.uid │ │ ├── client_main.tscn │ │ ├── server_main.gd │ │ ├── server_main.gd.uid │ │ └── server_main.tscn │ ├── default_bus_layout.tres │ ├── default_env.tres │ ├── fonts/ │ │ ├── roboto-13r.tres │ │ ├── roboto-16b.tres │ │ ├── roboto-16r.tres │ │ └── roboto-32b.tres │ ├── game/ │ │ ├── entities/ │ │ │ ├── bullet/ │ │ │ │ ├── bullet.gd │ │ │ │ ├── bullet.gd.uid │ │ │ │ └── bullet.tscn │ │ │ ├── player/ │ │ │ │ ├── player.gd │ │ │ │ ├── player.gd.uid │ │ │ │ └── player.tscn │ │ │ └── wall/ │ │ │ └── wall.tscn │ │ └── maps/ │ │ ├── map_bellandur.tscn │ │ ├── map_margao.tscn │ │ └── map_new_york.tscn │ ├── project.godot │ ├── scenes/ │ │ ├── AchievementsView/ │ │ │ ├── AchievementPopup.gd │ │ │ ├── AchievementPopup.gd.uid │ │ │ ├── AchievementUnlockNotification.gd │ │ │ ├── AchievementUnlockNotification.gd.uid │ │ │ ├── AchievementUnlockNotification.tscn │ │ │ ├── AchievementsList.gd │ │ │ ├── AchievementsList.gd.uid │ │ │ ├── AchievementsListAchievement.gd │ │ │ ├── AchievementsListAchievement.gd.uid │ │ │ ├── AchievementsListAchievement.tscn │ │ │ ├── AchievementsView.gd │ │ │ ├── AchievementsView.gd.uid │ │ │ └── AchievementsView.tscn │ │ ├── CustomInvitesView/ │ │ │ ├── CustomInvitesView.gd │ │ │ ├── CustomInvitesView.gd.uid │ │ │ └── CustomInvitesView.tscn │ │ ├── FriendsView/ │ │ │ ├── FriendsView.gd │ │ │ ├── FriendsView.gd.uid │ │ │ └── FriendsView.tscn │ │ ├── LeaderboardsView/ │ │ │ ├── LeaderboardsView.gd │ │ │ ├── LeaderboardsView.gd.uid │ │ │ └── LeaderboardsView.tscn │ │ ├── LobbiesView/ │ │ │ ├── CreateLobbyPopup.gd │ │ │ ├── CreateLobbyPopup.gd.uid │ │ │ ├── CreateLobbyPopup.tscn │ │ │ ├── CurrentLobby.gd │ │ │ ├── CurrentLobby.gd.uid │ │ │ ├── LobbiesView.gd │ │ │ ├── LobbiesView.gd.uid │ │ │ ├── LobbiesView.tscn │ │ │ ├── SearchLobby.gd │ │ │ ├── SearchLobby.gd.uid │ │ │ ├── SearchLobbyResults.gd │ │ │ └── SearchLobbyResults.gd.uid │ │ ├── LoginView/ │ │ │ ├── EnterCredentials.gd │ │ │ ├── EnterCredentials.gd.uid │ │ │ ├── LoginView.gd │ │ │ ├── LoginView.gd.uid │ │ │ └── LoginView.tscn │ │ ├── LogsView/ │ │ │ ├── LogsView.gd │ │ │ ├── LogsView.gd.uid │ │ │ └── LogsView.tscn │ │ ├── MetricsView/ │ │ │ ├── MetricsView.gd │ │ │ ├── MetricsView.gd.uid │ │ │ └── MetricsView.tscn │ │ ├── NotificationsView/ │ │ │ ├── NotificationsView.gd │ │ │ └── NotificationsView.gd.uid │ │ ├── StatsView/ │ │ │ ├── StatsView.gd │ │ │ ├── StatsView.gd.uid │ │ │ └── StatsView.tscn │ │ ├── UI/ │ │ │ ├── NetworkImage.gd │ │ │ ├── NetworkImage.gd.uid │ │ │ ├── NetworkImage.tscn │ │ │ ├── PrimaryButton.tscn │ │ │ ├── joysticks.gd │ │ │ ├── joysticks.gd.uid │ │ │ ├── nat_type.gd │ │ │ ├── nat_type.gd.uid │ │ │ ├── nat_type.tscn │ │ │ ├── ping.gd │ │ │ ├── ping.gd.uid │ │ │ ├── ping.tscn │ │ │ ├── players_score.gd │ │ │ ├── players_score.gd.uid │ │ │ ├── players_score.tscn │ │ │ ├── touch_screen_joystick.gd │ │ │ └── touch_screen_joystick.gd.uid │ │ └── UIView/ │ │ ├── UIView.gd │ │ ├── UIView.gd.uid │ │ └── UIView.tscn │ ├── scripts/ │ │ ├── Env.gd │ │ ├── Env.gd.uid │ │ ├── Store.gd │ │ ├── Store.gd.uid │ │ ├── ViewManager.gd │ │ ├── ViewManager.gd.uid │ │ ├── network.gd │ │ └── network.gd.uid │ ├── styles/ │ │ └── ViewTitleLabelSettings.tres │ ├── test.gd │ ├── test.gd.uid │ ├── test_manual_audio_output.gd │ └── test_manual_audio_output.gd.uid ├── src/ │ ├── achievements_interface.cpp │ ├── anticheat_client_interface.cpp │ ├── anticheat_server_interface.cpp │ ├── auth_interface.cpp │ ├── connect_interface.cpp │ ├── custom_invites_interface.cpp │ ├── ecom_interface.cpp │ ├── eosg_active_session.cpp │ ├── eosg_active_session.h │ ├── eosg_continuance_token.h │ ├── eosg_file_transfer_request.h │ ├── eosg_lobby_details.cpp │ ├── eosg_lobby_details.h │ ├── eosg_lobby_modification.cpp │ ├── eosg_lobby_modification.h │ ├── eosg_lobby_search.cpp │ ├── eosg_lobby_search.h │ ├── eosg_multiplayer_peer.cpp │ ├── eosg_multiplayer_peer.h │ ├── eosg_packet_peer_mediator.cpp │ ├── eosg_packet_peer_mediator.h │ ├── eosg_playerdatastorage_file_transfer_request.cpp │ ├── eosg_playerdatastorage_file_transfer_request.h │ ├── eosg_presence_modification.cpp │ ├── eosg_presence_modification.h │ ├── eosg_session_details.cpp │ ├── eosg_session_details.h │ ├── eosg_session_modification.cpp │ ├── eosg_session_modification.h │ ├── eosg_session_search.cpp │ ├── eosg_session_search.h │ ├── eosg_titlestorage_file_transfer_request.cpp │ ├── eosg_titlestorage_file_transfer_request.h │ ├── eosg_transaction.cpp │ ├── eosg_transaction.h │ ├── friends_interface.cpp │ ├── ieos.cpp │ ├── ieos.h │ ├── kws_interface.cpp │ ├── leaderboards_interface.cpp │ ├── lobby_interface.cpp │ ├── logging_interface.cpp │ ├── metrics_interface.cpp │ ├── mods_interface.cpp │ ├── p2p_interface.cpp │ ├── platform_interface.cpp │ ├── playerdatastorage_interface.cpp │ ├── presence_interface.cpp │ ├── progression_snapshot_interface.cpp │ ├── register_types.cpp │ ├── register_types.h │ ├── reports_interface.cpp │ ├── rtc_audio_interface.cpp │ ├── rtc_data_interface.cpp │ ├── rtc_interface.cpp │ ├── sanctions_interface.cpp │ ├── sessions_interface.cpp │ ├── stats_interface.cpp │ ├── titlestorage_interface.cpp │ ├── ui_interface.cpp │ ├── user_info_interface.cpp │ ├── utils.cpp │ ├── utils.h │ └── version_interface.cpp └── thirdparty/ └── Paste the EOS C SDK here as eos-sdk ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ # Commented out parameters are those with the same value as base LLVM style. # We can uncomment them if we want to change their value, or enforce the # chosen value in case the base style changes (last sync: Clang 14.0). --- ### General config, applies to all languages ### BasedOnStyle: LLVM AccessModifierOffset: -4 AlignAfterOpenBracket: DontAlign AlignOperands: DontAlign AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false BreakConstructorInitializers: AfterColon ColumnLimit: 0 ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 Cpp11BracedListStyle: false ConstructorInitializerAllOnOneLineOrOnePerLine: true IncludeCategories: - Regex: '".*"' Priority: 1 - Regex: '^<.*\.h>' Priority: 2 - Regex: '^<.*' Priority: 3 IndentCaseLabels: true IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false SpacesInLineCommentPrefix: Minimum: 0 Maximum: -1 TabWidth: 4 UseTab: false --- ### C++ specific config ### Language: Cpp Standard: c++17 --- ### ObjC specific config ### Language: ObjC ObjCBlockIndentWidth: 4 --- ### Java specific config ### Language: Java JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] ... ================================================ FILE: .editorconfig ================================================ # EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = false insert_final_newline = false [*.{h,hpp,cpp,gd}] indent_style = tab indent_size = 4 ================================================ FILE: .gitattributes ================================================ # Normalize EOL for all files that Git considers text files. * text=auto eol=lf ================================================ FILE: .github/FUNDING.yml ================================================ github: 3ddelano custom: https://www.buymeacoffee.com/3ddelano ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: 3ddelano --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Minimal steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error (or provide link to minimal reproducible project) **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Operating System:** - Version: [e.g. iOS 18.2, macOS arm64 15.2] **Plugin version:** - Version [e.g. v2.1.5] **Godot version:** - Version [e.g. v4.2] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/build.yml ================================================ name: 🛠️ Builds on: workflow_dispatch: push: branches: - "*" tags: - "*" jobs: build: runs-on: ${{ matrix.runner }} name: ${{ matrix.name }} strategy: fail-fast: false matrix: include: - identifier: linux-x86-64-debug name: 🐧 Linux x86_64 Debug runner: ubuntu-24.04 target: template_debug dev_build: yes platform: linux arch: x86_64 - identifier: linux-x86-64-release name: 🐧Linux x86_64 Release runner: ubuntu-24.04 target: template_release dev_build: no platform: linux arch: x86_64 - identifier: linux-arm64-debug name: 🐧 Linux arm64 Debug runner: ubuntu-24.04-arm target: template_debug dev_build: yes platform: linux arch: arm64 - identifier: linux-arm64-release name: 🐧Linux arm64 Release runner: ubuntu-24.04-arm target: template_release dev_build: no platform: linux arch: arm64 - identifier: macos-debug name: 🍎 macOS (universal) Debug runner: macos-latest target: template_debug dev_build: yes platform: macos arch: universal - identifier: macos-release name: 🍎 macOS (universal) Release runner: macos-latest target: template_release dev_build: no platform: macos arch: universal - identifier: windows-debug name: 🏁 Windows Debug runner: windows-latest target: template_debug dev_build: yes platform: windows arch: x86_64 - identifier: windows-release name: 🏁 Windows Release runner: windows-latest target: template_release dev_build: no platform: windows arch: x86_64 - identifier: android-arm64-debug name: 🤖 Android arm64 Debug runner: ubuntu-24.04 target: template_debug dev_build: yes platform: android arch: arm64 - identifier: android-arm64-release name: 🤖 Android arm64 Release runner: ubuntu-24.04 target: template_release dev_build: no platform: android arch: arm64 - identifier: android-x86_64-debug name: 🤖 Android x86_64 Debug runner: ubuntu-24.04 target: template_debug dev_build: yes platform: android arch: x86_64 - identifier: android-x86_64-release name: 🤖 Android x86_64 Release runner: ubuntu-24.04 target: template_release dev_build: no platform: android arch: x86_64 - identifier: ios-arm64-all name: 🍏 iOS (arm64) All runner: macos-latest platform: ios steps: - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.x" - name: Set up SCons shell: bash run: | python -c "import sys; print(sys.version)" python -m pip install scons scons --version - name: Checkout project uses: actions/checkout@v4 with: submodules: false - name: Checkout godot-cpp uses: actions/checkout@v4 with: repository: godotengine/godot-cpp path: godot-cpp submodules: recursive ref: "godot-4.2-stable" - name: Checkout private EOS SDK mirror repo uses: actions/checkout@v4 with: token: "${{secrets.EOS_SDK_MIRROR_PAT}}" repository: 3ddelano/eos-sdk-mirror path: thirdparty/eos-sdk ref: "dd294d8d5d175840f61775a527332560ba81afa4" - name: (Windows) Install mingw64 if: ${{ matrix.platform == 'windows' }} # change to egor-tensin/setup-mingw@v2 once pr #16 is merged uses: 3ddelano/setup-mingw@patch-1 - name: (Linux) Install dependencies if: ${{ matrix.platform == 'linux' }} run: | sudo apt-get update -qq sudo apt-get install -qqq build-essential pkg-config - name: (Android) Install dependencies if: ${{ matrix.platform == 'android' }} uses: nttld/setup-ndk@v1 with: ndk-version: r23c link-to-sdk: true - name: Setup build cache uses: actions/cache@v4 with: path: ${{ github.workspace }}/.scons-cache/ key: ${{ matrix.identifier }} restore-keys: | ${{ matrix.identifier }} continue-on-error: true - name: Compile extension shell: sh if: ${{ matrix.platform != 'ios' }} env: SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ SCONS_CACHE_LIMIT: 7168 run: | scons target='${{ matrix.target }}' platform='${{ matrix.platform }}' arch='${{ matrix.arch }}' dev_build='${{ matrix.dev_build }}' - name: (iOS) Compile extension shell: sh if: ${{ matrix.platform == 'ios' }} env: SCONS_CACHE: ${{ github.workspace }}/.scons-cache/ SCONS_CACHE_LIMIT: 7168 run: | chmod +x ./build-ios.sh && ./build-ios.sh - name: Copy extra files to addon folder shell: sh run: | cp -n '${{ github.workspace }}/README.md' '${{ github.workspace }}/LICENSE.md' '${{ github.workspace }}/sample/addons/epic-online-services-godot/' - name: Create artifact folder shell: sh run: | mkdir -p '${{ github.workspace }}/artifact-${{ matrix.identifier }}/${{ github.event.repository.name }}/' - name: Copy addons folder to artifact folder shell: sh run: | cp -n -r '${{ github.workspace }}/sample/addons' '${{ github.workspace }}/artifact-${{ matrix.identifier }}/${{ github.event.repository.name }}/' - name: (Windows) Delete compilation files if: ${{ matrix.platform == 'windows' }} run: | Remove-Item ${{ github.workspace }}/artifact-${{ matrix.identifier }}/${{ github.event.repository.name }}/addons/epic-online-services-godot/bin/windows/* -Include *.exp,*.lib,*.ilk -Force - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ github.event.repository.name }}-${{ matrix.identifier }}-${{ github.sha }} path: | ${{ github.workspace }}/artifact-${{ matrix.identifier }}/ compression-level: 9 retention-days: 14 merge-platforms: runs-on: ubuntu-latest needs: build strategy: fail-fast: false matrix: include: - identifier: linux-x86_64 name: 🐧 Linux matchpattern: linux - identifier: macos-universal name: 🍎 macOS (universal) matchpattern: macos - identifier: windows name: 🏁 Windows matchpattern: windows - identifier: android name: 🤖 Android matchpattern: android - identifier: ios name: 🍏 iOS matchpattern: ios steps: - name: Merge artifacts for platform uses: actions/upload-artifact/merge@v4 with: name: ${{ github.event.repository.name }}-${{ matrix.matchpattern }}-${{ github.sha }} pattern: ${{ github.event.repository.name }}-${{ matrix.matchpattern }}* compression-level: 9 retention-days: 14 merge-all: runs-on: ubuntu-latest needs: merge-platforms steps: - name: Merge artifacts into single uses: actions/upload-artifact/merge@v4 with: name: ${{ github.event.repository.name }}-all-${{ github.sha }} pattern: ${{ github.event.repository.name }}-* compression-level: 9 retention-days: 14 upload-to-release: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest needs: merge-all permissions: contents: write steps: - name: Download 'all' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-all-${{ github.sha }} path: artifact-all - name: Download 'android' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-android-${{ github.sha }} path: artifact-android - name: Download 'macos' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-macos-${{ github.sha }} path: artifact-macos - name: Download 'linux' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-linux-${{ github.sha }} path: artifact-linux - name: Download 'windows' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-windows-${{ github.sha }} path: artifact-windows - name: Download 'ios' artifact uses: actions/download-artifact@v4 with: name: ${{ github.event.repository.name }}-ios-${{ github.sha }} path: artifact-ios - name: Zip 'all' artifact run: | cd artifact-all zip -9 -r ../${{ github.event.repository.name }}-all-${{ github.sha }}.zip . cd .. - name: Zip 'android' artifact run: | cd artifact-android zip -9 -r ../${{ github.event.repository.name }}-android-${{ github.sha }}.zip . cd .. - name: Zip 'macos' artifact run: | cd artifact-macos zip -9 -r ../${{ github.event.repository.name }}-macos-${{ github.sha }}.zip . cd .. - name: Zip 'linux' artifact run: | cd artifact-linux zip -9 -r ../${{ github.event.repository.name }}-linux-${{ github.sha }}.zip . cd .. - name: Zip 'windows' artifact run: | cd artifact-windows zip -9 -r ../${{ github.event.repository.name }}-windows-${{ github.sha }}.zip . cd .. - name: Zip 'ios' artifact run: | cd artifact-ios zip -9 -r ../${{ github.event.repository.name }}-ios-${{ github.sha }}.zip . cd .. - name: Create draft release and upload assets uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: tag_name: ${{ github.ref_name }} draft: true files: | ${{github.event.repository.name}}-all-${{ github.sha }}.zip ${{github.event.repository.name}}-android-${{ github.sha }}.zip ${{github.event.repository.name}}-macos-${{ github.sha }}.zip ${{github.event.repository.name}}-linux-${{ github.sha }}.zip ${{github.event.repository.name}}-windows-${{ github.sha }}.zip ${{github.event.repository.name}}-ios-${{ github.sha }}.zip ================================================ FILE: .github/workflows/deploy-docs.yml ================================================ name: Deploy Docs to GitHub Pages on: push: branches: - "*" # Review gh actions docs if you want to further define triggers, paths, etc # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on jobs: build: name: Build Docusaurus runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - name: Install dependencies run: cd docs && bun install - name: Build website run: cd docs && bun run build - name: Upload Build Artifact uses: actions/upload-pages-artifact@v3 with: path: docs/build deploy: name: Deploy to GitHub Pages needs: build if: github.ref == 'refs/heads/main' # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source # Deploy to the github-pages environment environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .gitignore ================================================ # Godot logs logs/ cache/ .cache/ sample/android sample/android_debug.keystore .DS_Store .env vc140.pdb .sconsign.dblite *.o *.obj *.os **/*.tmp temp/ .idea/ compile_commands.json epic-online-services-godot-addon.zip ================================================ FILE: .gitmodules ================================================ [submodule "godot-cpp"] path = godot-cpp url = https://github.com/godotengine/godot-cpp branch = 4.2 [submodule "thirdparty/eos-sdk"] path = thirdparty/eos-sdk url = https://github.com/3ddelano/eos-sdk-mirror.git ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/ssciwr/clang-format-hook rev: v16.0.2 hooks: - id: clang-format ================================================ FILE: .vscode/c_cpp_properties.json ================================================ { "configurations": [ { "name": "Win32", "includePath": [ "${workspaceFolder}/godot-cpp/**", "${workspaceFolder}/godot-cpp/gen/include/**", "${workspaceFolder}/godot-cpp/include/**", "${workspaceFolder}/godot-cpp/gdextension/**", "${workspaceFolder}/thirdparty/eos-sdk/SDK/Include/", "${workspaceFolder}/thirdparty/eos-sdk/SDK/Include/**", "${workspaceFolder}/src/**", "${workspaceFolder}/src" ], "defines": ["_DEBUG", "UNICODE", "_UNICODE"], "windowsSdkVersion": "10.0.19041.0", "compilerPath": "cl.exe", "cStandard": "c17", "cppStandard": "c++17", "intelliSenseMode": "windows-msvc-x64" } ], "version": 4 } ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "Debug Project (Windows)", "type": "cppvsdbg", "request": "launch", "program": "${config:godotTools.editorPath.godot4}", "args": ["--debug"], "stopAtEntry": false, "cwd": "${workspaceFolder}/sample", "environment": [], "console": "integratedTerminal", "preLaunchTask": "build" }, { "name": "Debug Project (Macos)", "type": "cppdbg", "request": "launch", "program": "${config:godotTools.editorPath.godot4}", "args": ["--debug"], "stopAtEntry": false, "cwd": "${workspaceFolder}/sample", "environment": [], "console": "integratedTerminal", "MIMode": "lldb", "preLaunchTask": "build" } ] } ================================================ FILE: .vscode/settings.json ================================================ { "files.exclude": { "**/*.obj": true, "**/*.ilk": true, "**/*.exp": true, "**/*.pdb": true, "**/__pycache__": true, "**/*.os": true, "**/*.o": true }, "godot_tools.editor_path": "d:\\Projects\\Godot\\Godot_v4.exe", "files.associations": { "*.rmd": "markdown", "utility": "cpp", "sstream": "cpp", "unordered_map": "cpp", "algorithm": "cpp", "chrono": "cpp", "iterator": "cpp", "xhash": "cpp", "xtree": "cpp", "xutility": "cpp", "string": "cpp", "type_traits": "cpp", "deque": "cpp", "forward_list": "cpp", "list": "cpp", "vector": "cpp", "initializer_list": "cpp" }, "grammarly.selectors": [ { "language": "cpp", "scheme": "file" } ], "cmake.ignoreCMakeListsMissing": true } ================================================ FILE: .vscode/tasks.json ================================================ { "version": "2.0.0", "tasks": [ { "label": "build", "group": "build", "type": "shell", "command": "scons", "args": ["dev_build=yes"], "problemMatcher": "$msCompile" }, { "label": "build-release", "group": "build", "type": "shell", "command": "scons", "args": ["target=template_release"], "problemMatcher": "$msCompile" } ] } ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2022 Delano Lourenco Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Epic Online Services Godot (EOSG) Project Logo ### Easiest way to use Epic Online Services in Godot 4.2+ (includes demo project) Godot3    Epic Online Services 1.18.1.2 > Supports Windows x64, Linux x64/arm64, Android x64/arm64, MacOS, iOS arm64 (iphone/simulator) > Disclaimer: This project is NOT affiliated with Epic Games Inc or Godot Engine. It doesn't endorse Epic Online > Services. This project and sample Godot scenes are provided solely for educational purposes and may or may not comply > with Epic Games' Design Guidelines, if you plan to release a game make sure you read > the [Guidelines](https://dev.epicgames.com/docs/services/en-US/EpicAccountServices/DesignGuidelines/index.html) and > any > other steps needed to release a public game like asking for user consent, option to delete user data, website with > privacy policy and license, etc. ## This plugin has two main systems: - ### High Level EOS (Recommended) The High Level Epic Online Services provides easy to use methods and signals to interact with EOS recommended for beginners. - ### GDExtension EOS The GDExtension EOS provides advanced EOS usage not recommended for beginners. See `EOS` and `IEOS` classes. ## [🚀 Check out out GDAI MCP from the creator of EOSG](https://gdaimcp.com?ref=eosg-readme) Supercharge your Godot 4.2+ workflow with GDAI MCP – the ultimate Godot MCP server that lets AI tools like Claude, Cursor, Windsurf, VSCode and more automate scene creation, node editing, reading godot errors, creating scripts, debugging, and more. Vibe code like never before! ### 🔗 **[https://gdaimcp.com](https://gdaimcp.com?ref=eosg-readme)** # High Level Epic Online Services Following are the main classes in High Level Epic Online Services. They also have documentation in the Godot Editor: - ### HPlatform - ### HAuth - ### HAchievements - ### HFriends - ### HStats - ### HLeaderboards - ### HLobbies - ### HP2P - ### HLog A basic example of using High Level EOS: ```GDScript # In main script extends Node func _ready() -> void: # Setup HEOS Logs HLog.log_level = HLog.LogLevel.INFO var credentials = HCredentials.new() credentials.product_name = "PRODUCT_NAME_NAME" credentials.product_version = "PRODUCT_VERSION_HERE" credentials.product_id = "PRODUCT_ID_HERE" credentials.sandbox_id = "SANDBOX_ID_HERE" credentials.deployment_id = "DEPLOYMENT_ID_HERE" credentials.client_id = "CLIENT_ID_HERE" credentials.client_secret = "CLIENT_SECRET_HERE" # optional #credentials.encryption_key = "ENCRYPTION_KEY_HERE" var setup_success := await HPlatform.setup_eos_async(credentials) if not setup_success: printerr("Failed to setup EOS. See logs for more details") return # Setup Logs from EOS HPlatform.log_msg.connect(_on_eos_log_msg) var log_res := HPlatform.set_eos_log_level(EOS.Logging.LogCategory.AllCategories, EOS.Logging.LogLevel.Info) if not EOS.is_success(log_res): printerr("Failed to set logging level") return HAuth.logged_in.connect(_on_logged_in) # During development use the devauth tool to login HAuth.login_devtool_async("localhost:4545", "CREDENTIAL_NAME_HERE") # Or login without any credentials # await HAuth.login_anonymous_async() func _on_logged_in(): print("Logged in successfully: product_user_id=%s" % HAuth.product_user_id) # Example: Get top records for a leaderboard var records := await HLeaderboards.get_leaderboard_records_async("LEADERBOARD_ID_HERE") print(records) func _on_eos_log_msg(msg: EOS.Logging.LogMessage) -> void: print("SDK %s | %s" % [msg.category, msg.message]) ``` # GDextension Epic Online Services ## Features - Authentication (Epic Games, Steam, Discord, Anonymous etc) - Social Overlay on Windows - Achievements - Stats & Leaderboards - Lobby, Sessions and Multiplayer - Voice - Metrics - Mods - Player/Title data storage - Progression Snapshot - Reports and Sanctions - Ecommerce (Ecom Epic Games Store) - AntiCheat #### [View Current Project Status](#current-project-status) ## Simple P2P Example A simple demo showcasing P2P multiplayer using Epic Online Services: [Click Here](https://github.com/3ddelano/EOSGP2PInterfaceTestGame) ## Support Development #### Making this project took a lot of time and effort, reading the Epic Online Services documentation countless times and testing each method in Godot. I would really appreciate if you could support the project in any way. Buy Me A Coffee
Github Sponsor #### Want to support in other ways? Contact me on Discord: `@3ddelano` Join the Discord server for discussing suggestions or bugs: [3ddelano Cafe](https://discord.gg/FZY9TqW) ## [Demo Video (Youtube)](https://www.youtube.com/watch?v=ENyvF4yVjKg) [Watch the playlist](https://www.youtube.com/playlist?list=PL5t0hR7ADzun5JYF4e2a2FtZEWYHxK83_) [![Epic Online Services Tutorial series](https://img.youtube.com/vi/ENyvF4yVjKg/0.jpg)](https://www.youtube.com/playlist?list=PL5t0hR7ADzun5JYF4e2a2FtZEWYHxK83_) ## [Documentation](http://localhost:3000/epic-online-services-godot/docs/introduction) ## Screenshots - Windows - Android - iOS - Cross platform lobbies - iOS - macOS ## How does it work This project uses GDExtension to wrap the `Epic Online Services C SDK` so that it can be easily used in Godot using GDScript, C#, etc with similar class hierarchy and static type support. It makes use of signals for sending events like user login, logout, achievement unlock, etc. ## Installation This is a regular plugin for `Godot 4.2+`. To install the plugin follow the steps below: #### **Method 1: Install from Asset Library** 1. Asset Library link: [View the plugin on Godot Asset Library](https://godotengine.org/asset-library/asset/2453) 2. In the Godot editor, navigate to the `AssetLib` tab, and in the search bar type `EOSG`. 3. Click on the plugin with name `Epic Online Services Godot (EOSG)` by `3ddelano` 4. In the popup that opens, click the `Download` button. 5. Once the download is done, click the `Install` button. 6. Goto `Project->Project Settings->Plugins` and enable the `Epic Online Services Godot 4.2+ (EOSG)` plugin. 7. Restart the godot editor. 8. You can now use the plugin. Head to the [Documentation](#) for more information on how to use the plugin. #### **Method 2: Install from GitHub** 1. Goto the Releases section and download the [latest release](https://github.com/3ddelano/epic-online-services-godot/releases/latest) 2. Extract the zip file and copy the `addons/epic-online-services-godot` folder into the `res://addons/` folder of your project. If the `res://addons` does not exist, create it. 3. In the Godot editor, goto `Project->Project Settings->Plugins` and enable the `Epic Online Services Godot 4.2+ (EOSG)` plugin. 4. Restart the godot editor. 5. You can now use the plugin. Head to the [Documentation](#) for more information on how to use the plugin. ## Development Setup #### Pre-requisites - Godot Engine 4.2+ (Get it here [Godot Engine Download](https://godotengine.org/download/)) - Epic Online Services C SDK (Download from [Epic Developer Portal](https://dev.epicgames.com/portal/sdk)) - Make sure you have accepted the Terms and Conditions for [Epic Online Services](https://www.epicgames.com/site/en-US/tos?lang=en-US) - A product registered with Epic Games Services (Make one for free [Epic Developer Portal](https://dev.epicgames.com/portal)) To develop this plugin, follow the below steps: 1. Download/clone the repository. 1. Run the command: ```bash git submodule update --init --recursive ``` 1. Extract the `EOS C SDK` zip downloaded from Epic Games, rename it to `eos-sdk` and paste it in the `thirdparty/` folder. Refer to the below folder structure. 1. Build the GDExtension plugin in debug mode (With debug symbols) ```shell # In root folder scons platform= target=template_debug dev_build=yes ``` Eg. `scons platform=windows target=template_debug dev_build=yes` 1. Build the GDExtension plugin for release (Optimized) ```shell # In root folder scons platform=windows target=template_release ``` 1. The built GDExtension library will be in the `res://addons/epic-online-services-godot/bin/` folder of the sample project. ## Debugging GDExtension on MacOS If you get an error `Not allowed to attach to process` trying to debug GDExtension on MacOS using LLDB. Run the below command: ```bash codesign --entitlements debug-entitlements.plist -f -s - /Applications/Godot.app/Contents/MacOS/Godot ``` ### How to run the sample project? > The sample Godot project is located in the **sample** folder 1. Clone/Download the repo. 2. Download the [latest release](https://github.com/3ddelano/epic-online-services-godot/releases/latest) from the Releases section and replace the existing `/addons/epic-online-services-godot` with the one from the Release (this includes the built shared libraries). 3. Copy your credentials (`Product Id`, `Sandbox Id`, `Deployment Id`, `Client Id`, `Client Secret`) of your Epic Games "Product" from the Epic Games Dev Portal and paste them in `Main.gd` script in the relevant sections. The encryption key is a random 64 character long string. These credentials need to be kept as private as possible. One way is to make sure to encrypt all scripts when exporting the final game. ( See [Compiling with script key encryption](https://docs.godotengine.org/en/stable/development/compiling/compiling_with_script_encryption_key.html)) 4. Configure your Product on the EOS Dev Portal with the following configuration: - In the `Client Policies` section in `Product Settings`, for the Client policy type choose `Custom policy`, enable the `User is required` and enable every features and action except `Connect` (Trusted Server Required). This will allow the sample to access the different services provided by Epic Online Services. In your actual game, the client policy is important and you should give minimal permissions to features. - In the `Permissions` section of `Epic Account Services`, enable all three: `Basic Profile`, `Online Presence` and `Friends`. - (Optional if you want some pre-made achievements) In the `Achievements` section in `Game Services`, use the `Bulk Import` option and import the `HelloProduct.zip` file located at `res://HelloProduct.zip` ### Bootstrapping Godot executable with Epic Online Services If you want to use the `Account Portal` login option in Epic Online Services, you need to bootstrap the Godot/Game executable as needed by `EOS-SDK 1.15` and greater. See [Redistributable Installer](https://dev.epicgames.com/docs/services/en-US/EpicAccountServices/Crossplayacrossplatforms/RedistributableInstaller/index.html) A sample of the generated `.ini` file for the Godot Editor is shown below (during game development): ``` ApplicationPath=Godot_v4.2.0-stable_win64.exe WorkingDirectory= WaitForExit=0 NoOperation=0 ``` Follow the instructions in [Running the service for local development](https://dev.epicgames.com/docs/services/en-US/EpicAccountServices/Crossplayacrossplatforms/RedistributableInstaller/index.html#runningtheserviceforlocaldevelopment) and: - During game development Bootstrap the Godot Editor executable (eg. `Godot_v4.2.0-stable_win64.exe`) to test the `Account Portal` login - After exporting the game Bootstrap the exported game executable (eg. `My Amazing Game.exe`) ## Exporting for Android Use a community created [Android Export Plugin](https://github.com/uno1982/EOSG-android-exporter) Or follow the steps below to manually export for Android. ### Pre-requisites 1. Setup the `Android Build Template` in your Godot project by following the tutorial [Gradle builds for Andriod](https://docs.godotengine.org/en/stable/tutorials/export/android_gradle_build.html). This will create an android project in `res://android/build`. 2. Now with reference to the tutorial [Add the EOS SDK to Your Android Studio Project](https://dev.epicgames.com/docs/epic-online-services/platforms/android#4-add-the-eos-sdk-to-your-android-studio-project), perform the following steps. 3. In the `res://android/build/build.gradle` file, add the following lines after the implementations in the `dependencies` section. Before ```gradle dependencies { implementation libraries.kotlinStdLib implementation libraries.androidxFragment ... other code ``` After ```gradle dependencies { implementation libraries.kotlinStdLib implementation libraries.androidxFragment // EOS SDK dependencies implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.security:security-crypto:1.0.0' implementation 'androidx.browser:browser:1.4.0' implementation 'androidx.webkit:webkit:1.7.0' // Update the path so that it points to eossdk-StaticSTDC-release.aar provided in addons/epic-online-services-godot/bin/android/ implementation files('../../addons/epic-online-services-godot/bin/android/eossdk-StaticSTDC-release.aar') ...other code ``` 4. In the `res://android/build/build.gradle` file, add the following lines after the `defaultConfig` in the `android` section. Before ```gradle android { ... other code defaultConfig { ... other code // Feel free to modify the application id to your own. applicationId getExportPackageName() versionCode getExportVersionCode() versionName getExportVersionName() minSdkVersion getExportMinSdkVersion() targetSdkVersion getExportTargetSdkVersion() missingDimensionStrategy 'products', 'template' } ... other code ``` After ```gradle android { ... other code defaultConfig { ... other code // Feel free to modify the application id to your own. applicationId getExportPackageName() versionCode getExportVersionCode() versionName getExportVersionName() minSdkVersion getExportMinSdkVersion() targetSdkVersion getExportTargetSdkVersion() missingDimensionStrategy 'products', 'template' // This is needed by EOS Android SDK String ClientId = "PUT YOUR EOS CLIENT ID HERE" resValue("string", "eos_login_protocol_scheme", "eos." + ClientId.toLowerCase()) } ... other code ``` 5. In the `res://android/build/config.gradle` file, update the `minSdk` to `23` to match with the requirements of the `EOS Android SDK`. Before ```gradle minSdk : 21, ``` After ```gradle minSdk : 23, ``` 6. In the `res://android/build/src/com/godot/game/GodotGame.java` file, update it as follows. ```java package com.godot.game; import com.epicgames.mobile.eossdk.EOSSDK; // added import org.godotengine.godot.GodotActivity; import android.os.Bundle; public class GodotApp extends GodotActivity { static { // added System.loadLibrary("EOSSDK"); // added } // added @Override public void onCreate(Bundle savedInstanceState) { EOSSDK.init(getActivity()); // added setTheme(R.style.GodotAppMainTheme); super.onCreate(savedInstanceState); } } ``` 7. Now open your project in the Godot Editor, and goto `Project -> Export` and create a new Android export profile. 8. In the `Gradle Build` section, enable `Use Gradle Build`. In the `Architectures` section enable `arm64-v8a`. In the `Permissions` section ensure that `ACESSS_NETWORK_STATE`, `ACCESS_WIFI_STATE` and `INTERNET` are enabled. These permissions are needed for the EOS SDK to work. Fill in the other details such as package name, etc as needed. 9. You can now export the Android APK by clicking the `Export Project` button. ## Exporting for iOS Export the project from Godot editor for iOS target. You might get a build error during this process, ignore it. Open the generated iOS project in XCode and build the project. The build should be successful. EOSG has support for iOS arm64 device and iOS arm64 simulator. ## Current Project Status - Completed with sample - Auth Interface - Achievements Interface - Connect Interface - CustomInvites Interface - Friends Interface - Stats Interface - UserInfo Interface - Leaderboards Interface - Metrics Interface - Mods Interface - Presence Interface - ProgressionSnapshot Interface - Reports Interface - UI Interface - Lobby Interface (Also sample for manual audio input/output) - RTC Interface - RTCAudio Interface - P2P Interface - Version Interface - Completed without sample - KWS Interface - PlayerDataStorage Interface - Sanctions Interface - Sessions Interface - TitleStorage Interface - Ecom Interface - AntiCheatServer Interface - AntiCheatClient Interface - Not completed - Integrated Platform Interface ================================================ FILE: SConstruct ================================================ #!/usr/bin/env python import os import shutil env = SConscript("godot-cpp/SConstruct") lib_name = "libeosg" plugin_bin_folder = "sample/addons/epic-online-services-godot/bin" eos_sdk_folder = "thirdparty/eos-sdk/SDK/" # Add source files env.Append(CPPPATH=["src/", eos_sdk_folder + "Include/"]) sources = Glob("src/*.cpp") platform = env["platform"] build_target = env["target"] def copy_file(from_path, to_path): if not os.path.exists(os.path.dirname(to_path)): os.makedirs(os.path.dirname(to_path)) shutil.copyfile(from_path, to_path) def copy_folder(from_path, to_path): if not os.path.exists(to_path): os.makedirs(to_path) shutil.rmtree(to_path, ignore_errors=True) shutil.copytree(from_path, to_path) def extract_eos_android_libraries(): libs_path = eos_sdk_folder + "Bin/Android/static-stdc++/libs/" aar_file = eos_sdk_folder + "Bin/Android/static-stdc++/aar/eossdk-StaticSTDC-release.aar" zip_file = libs_path + "eos-sdk.zip" # Delete libs folder if exists shutil.rmtree(libs_path, ignore_errors=True) # Copy aar to zip file copy_file(aar_file, zip_file) # Copy aar to plugin bin/android copy_file(aar_file, plugin_bin_folder + "/android/eossdk-StaticSTDC-release.aar") # Create folder if doesnt exist if not os.path.exists(libs_path + "extracted"): os.makedirs(libs_path + "extracted") # Unzip the file shutil.unpack_archive(zip_file, libs_path + "extracted") # Copy all folders from libs_path+"extracted/jni" to libs_path for folder in os.listdir(libs_path + "extracted/jni"): shutil.copytree(libs_path + "extracted/jni/" + folder, libs_path + folder) # Delete extracted folder shutil.rmtree(libs_path + "extracted", ignore_errors=True) # Delete zip file os.remove(zip_file) genv = env def on_complete(target, source, env): if platform == "windows": shutil.rmtree(plugin_bin_folder + "/windows/x64", ignore_errors=True) shutil.copytree(eos_sdk_folder + "Bin/x64", plugin_bin_folder + "/windows/x64") copy_file(eos_sdk_folder + "Bin/EOSSDK-Win64-Shipping.dll", plugin_bin_folder + "/windows/EOSSDK-Win64-Shipping.dll") elif platform == "linux": so_variant = "LinuxArm64" if genv["arch"] == "arm64" else "Linux" copy_file(eos_sdk_folder + f"Bin/libEOSSDK-{so_variant}-Shipping.so", plugin_bin_folder + f"/linux/libEOSSDK-{so_variant}-Shipping.so") elif platform == "macos": framework_folder = plugin_bin_folder + f"/macos/{lib_name}.{platform}.{build_target}.framework" # Copies EOS dylib inside framework folder copy_file(eos_sdk_folder + "Bin/libEOSSDK-Mac-Shipping.dylib", framework_folder + f"/libEOSSDK-Mac-Shipping.dylib") lib_path = f"{framework_folder}/{lib_name}.{platform}.{build_target}" print(f"Updating libEOSSDK-Mac-Shipping.dylib path in {lib_path}") os.system(f"install_name_tool -change @rpath/libEOSSDK-Mac-Shipping.dylib @loader_path/libEOSSDK-Mac-Shipping.dylib {lib_path}") # For reference: # - CCFLAGS are compilation flags shared between C and C++ # - CFLAGS are for C-specific compilation flags # - CXXFLAGS are for C++-specific compilation flags # - CPPFLAGS are for pre-processor flags # - CPPDEFINES are for pre-processor defines # - LINKFLAGS are for linking flags env.Append(LIBPATH=[eos_sdk_folder + "Lib/"]) env.Append(LIBPATH=[eos_sdk_folder + "Bin/"]) if env["platform"] == "windows": # TODO: dont ignore this warning # this disables LINK : error LNK1218: warning treated as error; # so that it can build in github action with scons cache env.Append(LINKFLAGS=["/ignore:4099"]) env.Append(LIBS=["EOSSDK-Win64-Shipping"]) elif env["platform"] == "linux": env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) if env["arch"] == "arm64": env.Append(LIBS=["EOSSDK-LinuxArm64-Shipping"]) else: env.Append(LIBS=["EOSSDK-Linux-Shipping"]) elif env["platform"] == "macos": env.Append(LIBS=["EOSSDK-Mac-Shipping"]) elif env["platform"] == "ios": if env["arch"] != "arm64": raise Exception("Only arm64 is supported on iOS.") copy_folder(eos_sdk_folder + "Bin/IOS/EOSSDK.xcframework", plugin_bin_folder + "/ios/EOSSDK.xcframework") env.Append(ENV={'IPHONEOS_DEPLOYMENT_TARGET': '15.0'}) env.Append(LINKFLAGS=[ "-F", plugin_bin_folder + f"/ios/EOSSDK.xcframework/ios-arm64{"-simulator" if env["ios_simulator"] else ""}", '-framework', 'AuthenticationServices', '-framework', 'EOSSDK', ]) elif env["platform"] == "android": eos_android_arch = "arm64-v8a" if env["arch"] == "x86_64": eos_android_arch = "x86_64" extract_eos_android_libraries() env.Append(LIBPATH=[eos_sdk_folder + "Bin/Android/static-stdc++/libs/" + eos_android_arch + "/"]) env.Append(LIBS=["EOSSDK"]) if env["platform"] == "macos": library = env.SharedLibrary( f"{plugin_bin_folder}/macos/{lib_name}.{platform}.{build_target}.framework/{lib_name}.{platform}.{build_target}", source=sources,) else: library = env.SharedLibrary( f"{plugin_bin_folder}/{platform}/{lib_name}{env['suffix']}{env['SHLIBSUFFIX']}", source=sources, ) # Disable scons cache for source files # NoCache(sources) complete_command = Command('complete', library, on_complete) Depends(complete_command, library) Default(complete_command) ================================================ FILE: build-ios.sh ================================================ #!/bin/sh build=yes if [ "$1" = "build=n" ] || [ "$1" = "build=N" ] || [ "$1" = "build=no" ] || [ "$1" = "build=0" ]; then build=no fi dev_build=no dev_build_ext="" if [ "$2" = "dev_build=y" ] || [ "$2" = "dev_build=Y" ] || [ "$2" = "dev_build=yes" ] || [ "$2" = "dev_build=1" ]; then dev_build=yes dev_build_ext=".dev" fi echo "Config: dev_build=${dev_build}, build=${build}" if [ "$build" = "yes" ]; then echo "\nBuilding for iOS simulator template_debug target" scons arch=arm64 ios_simulator=yes platform=ios target=template_debug dev_build=${dev_build} echo "\nBuilding for iOS device template_debug target" scons arch=arm64 ios_simulator=no platform=ios target=template_debug dev_build=${dev_build} echo "\nBuilding for iOS simulator template_release target" scons arch=arm64 ios_simulator=yes platform=ios target=template_release dev_build=no echo "\nBuilding for iOS device template_release target" scons arch=arm64 ios_simulator=no platform=ios target=template_release dev_build=no fi eosg_ios_bin_dir=./sample/addons/epic-online-services-godot/bin/ios godotcpp_bin_dir=./godot-cpp/bin echo "\nDeleting existing libgodot-cpp xcframework(s) if any" rm -rf ${eosg_ios_bin_dir}/libgodot-cpp.ios.template* echo "\nCreating libgodot-cpp xcframework for template_debug" xcodebuild -create-xcframework \ -library ${godotcpp_bin_dir}/libgodot-cpp.ios.template_debug${dev_build_ext}.arm64.a \ -library ${godotcpp_bin_dir}/libgodot-cpp.ios.template_debug${dev_build_ext}.arm64.simulator.a \ -output ${eosg_ios_bin_dir}/libgodot-cpp.ios.template_debug.xcframework echo "\nCreating libgodot-cpp xcframework for template_release" xcodebuild -create-xcframework \ -library ${godotcpp_bin_dir}/libgodot-cpp.ios.template_release.arm64.a \ -library ${godotcpp_bin_dir}/libgodot-cpp.ios.template_release.arm64.simulator.a \ -output ${eosg_ios_bin_dir}/libgodot-cpp.ios.template_release.xcframework echo "\nCreating libeosg xcframework for template_debug" xcodebuild -create-xcframework \ -library ${eosg_ios_bin_dir}/libeosg.ios.template_debug${dev_build_ext}.arm64.dylib \ -library ${eosg_ios_bin_dir}/libeosg.ios.template_debug${dev_build_ext}.arm64.simulator.dylib \ -output ${eosg_ios_bin_dir}/libeosg.ios.template_debug.xcframework echo "\nCreating libeosg xcframework for template_release" xcodebuild -create-xcframework \ -library ${eosg_ios_bin_dir}/libeosg.ios.template_release.arm64.dylib \ -library ${eosg_ios_bin_dir}/libeosg.ios.template_release.arm64.simulator.dylib \ -output ${eosg_ios_bin_dir}/libeosg.ios.template_release.xcframework echo "\nDeleting all .dylib files from eosg ios bin dir" rm -rf ${eosg_ios_bin_dir}/*.dylib ================================================ FILE: debug-entitlements.plist ================================================ com.apple.security.get-task-allow ================================================ FILE: debug.cmd ================================================ @echo off scons dev_build=yes && godot4 --path sample ================================================ FILE: debug.sh ================================================ scons dev_build=yes && godot45 --path sample ================================================ FILE: docs/.gitignore ================================================ # Dependencies /node_modules # Production /build # Generated files .docusaurus .cache-loader # Misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: docs/README.md ================================================ # Website This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. ### Installation ``` $ yarn ``` ### Local Development ``` $ yarn start ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. ### Build ``` $ yarn build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. ### Deployment Using SSH: ``` $ USE_SSH=true yarn deploy ``` Not using SSH: ``` $ GIT_USER= yarn deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. ================================================ FILE: docs/docs/installation.md ================================================ # Installation ## Method 1: Install from Asset Library (Recommended) [View the plugin on Godot Asset Library](https://godotengine.org/asset-library/asset/2453) 1. In the Godot editor, navigate to the `AssetLib` tab at the top, and in the search bar type `EOSG`. 2. Click on the plugin with name `Epic Online Services Godot (EOSG)` by `3ddelano` 3. In the popup that opens, click the `Download` button. 4. Once the download is done, click the `Install` button. 5. Goto `Project -> Project Settings -> Plugins` and enable the `Epic Online Services Godot 4.2+ (EOSG)` plugin. 6. **Restart the godot editor.** 7. The plugin is now installeed. Check out the [Initialization](/docs/topics/initialization) guide next. ## Method 2: Install from GitHub 1. Goto the [Releases](https://github.com/3ddelano/epic-online-services-godot/releases) section and download the latest release. Here there are separate zip files for each platform which you can download if you are only looking to release on one platform or download the `epic-online-services-godot-all` that supports all platforms. 2. Extract the zip file and copy the `addons/epic-online-services-godot` folder into the `res://addons/` folder of your project. If the `res://addons` does not exist, create it. 3. In the Godot editor, goto `Project -> Project Settings -> Plugins` and enable the `Epic Online Services Godot 4.2+ (EOSG)` plugin. 4. **Restart the godot editor.** 5. The plugin is now installeed. Check out the [Initialization](/docs/topics/initialization) guide next. ================================================ FILE: docs/docs/introduction.md ================================================ # Introduction Epic Online Services Godot (EOSG) is the easiest way to integrate Epic Online Services into your Godot project. The plugin supports the following platforms: - Windows x64 - Linux x64/arm64 - Android x64/arm64 - MacOS universal - iOS arm64 (iPhone/simulator) This plugin has two main ways in which you can interact with Epic Online Services: 1. High Level EOS (Recommended) 2. GDExtension EOS ### High Level EOS (Recommended) The High Level Epic Online Services provides easy-to-use methods and signals to interact with EOS, making it ideal for beginners. This simplified API abstracts away many of the complexities of the EOS SDK while providing access to its core functionality. ### GDExtension EOS The GDExtension EOS provides low-level access to the Epic Online Services SDK. This interface is not recommended for beginners but gives you full access to the underlying EOS functionality through the `EOS` and `IEOS` classes in Godot.

Here's the next steps; - To get started with using the plugin, check out the [Installation](/docs/installation) guide. - Checkout the [Games Showcase](/showcase) ================================================ FILE: docs/docs/sample-project.md ================================================ # Sample Project To run the sample project which showcases most of the EOS features, follow these steps: 1. Clone/Download the [plugin repo](https://github.com/3ddelano/epic-online-services-godot) 2. You cannot directly open the ./sample folder and run the project, because the plugin binaries are not included. To include the plugin binaries. Download the [latest release](https://github.com/3ddelano/epic-online-services-godot/releases/latest) from the Releases section and replace the existing `sample/addons/epic-online-services-godot` folder with the one from the Release (this includes the built shared libraries). See the [Installation](/docs/installation) section for more information on how to install the plugin. 3. Copy your credentials (`Product Id`, `Sandbox Id`, `Deployment Id`, `Client Id`, `Client Secret`) of your Epic Games "Product" from the Epic Games Dev Portal and paste them in `./sample/Main.gd` script in the relevant sections. The encryption key is a random 64 character long string. These credentials need to be kept as private as possible. One way is to make sure to encrypt all scripts when exporting the final game. (See [Compiling with script key encryption](https://docs.godotengine.org/en/stable/development/compiling/compiling_with_script_encryption_key.html)) 4. Configure your Product on the [EOS Dev Portal](https://dev.epicgames.com/portal) with the following configuration: - In the `Client Policies` section in `Product Settings`, for the Client policy type choose `Custom policy`, enable the `User is required` and enable every features and action except `Connect` (Trusted Server Required). This will allow the sample to access the different services provided by Epic Online Services. In your actual game, the client policy is important and you should give minimal permissions to features. - In the `Permissions` section of `Epic Account Services`, enable all three: `Basic Profile`, `Online Presence` and `Friends`. - (Optional if you want some pre-made achievements) In the `Achievements` section in `Game Services`, use the `Bulk Import` option and import the `HelloProduct.zip` file located at `res://HelloProduct.zip` 5. Finally open the `./sample` folder in Godot. ================================================ FILE: docs/docs/topics/authentication.md ================================================ # Authentication WIP - Work In Progress ================================================ FILE: docs/docs/topics/initialization.md ================================================ import ReactPlayer from 'react-player' # Initialization Follow the steps below to initialize and use the plugin. Check out the below video for a quick walkthrough.

1. You will need the following from the EOS Developer Portal: - `PRODUCT_ID` - `SANDBOX_ID` - `DEPLOYMENT_ID` - `CLIENT_ID` - `CLIENT_SECRET` 2. You also need to provide these values yourself: - `PRODUCT_NAME` - A name chosen by you for the project - `PRODUCT_VERSION` - A version chosen by you for the project - `ENCRYPTION_KEY` - A random 64 character long string Now at the start of your game, or when you want to initialize EOS. Use the following script: ```gdscript extends Node func _ready() -> void: # This will control which logs you get from EOSG HLog.log_level = HLog.LogLevel.INFO var init_opts = EOS.Platform.InitializeOptions.new() init_opts.product_name = "PRODUCT_NAME_HERE" # Change this init_opts.product_version = "PRODUCT_VERSION_HERE" # Change this var create_opts = EOS.Platform.CreateOptions.new() create_opts.product_id = "PRODUCT_ID_HERE" # Change this create_opts.sandbox_id = "SANDBOX_ID_HERE" # Change this create_opts.deployment_id = "DEPLOYMENT_ID_HERE" # Change this create_opts.client_id = "CLIENT_ID_HERE" # Change this create_opts.client_secret = "CLIENT_SECRET_HERE" # Change this create_opts.encryption_key = "ENCRYPTION_KEY_HERE" # Change this # Enable Social Overlay on Windows if OS.get_name() == "Windows": HAuth.auth_login_flags = EOS.Auth.LoginFlags.None create_opts.flags = EOS.Platform.PlatformFlags.WindowsEnableOverlayOpengl # Initialize the SDK var init_res := await HPlatform.initialize_async(init_opts) if not EOS.is_success(init_res): printerr("Failed to initialize EOS SDK: ", EOS.result_str(init_res)) return # Create platform var create_success := await HPlatform.create_platform_async(create_opts) if not create_success: printerr("Failed to create EOS Platform") return # Setup Logs from EOS HPlatform.log_msg.connect(_on_eos_log_msg) # This will control which logs you get from EOS SDK var log_res := HPlatform.set_eos_log_level(EOS.Logging.LogCategory.AllCategories, EOS.Logging.LogLevel.Info) if not EOS.is_success(log_res): printerr("Failed to set logging level") return HAuth.logged_in.connect(_on_logged_in) # During development use the devauth tool to login HAuth.login_devtool_async("localhost:4545", "CREDENTIAL_NAME_HERE") # Only on mobile device (Login without any credentials) # await HAuth.login_anonymous_async() func _on_logged_in(): print("Logged in successfully: product_user_id=%s" % HAuth.product_user_id) # This method is called when we get a log message from EOS SDK func _on_eos_log_msg(msg: EOS.Logging.LogMessage) -> void: print("SDK %s | %s" % [msg.category, msg.message]) ``` ## Initialization on Windows To use the `Account Portal` login option on Windows, you need to bootstrap Godot with EOS. See the steps below. ### Bootstrapping Godot with EOS for Windows Follow the tutorial at [Redistributable Installer](https://dev.epicgames.com/docs/services/en-US/EpicAccountServices/Crossplayacrossplatforms/RedistributableInstaller/index.html) and also see the below steps. A sample of the generated `.ini` file for the Godot Editor is shown below (during game development): ``` ApplicationPath=APPLICATION_PATH_HERE WorkingDirectory= WaitForExit=0 NoOperation=0 ``` In the above `.ini` file, the `APPLICATION_PATH_HERE` can either be the path to your Godot Editor executable (Eg. `Godot_v4.2.0-stable_win64.exe`) when you are in development or the path to the exported game (Eg. `MyAmazingGame.exe`) ## Initialization on Android The plugin supports the Android platform, but needs a few one-time steps to initialize and configure android exports. ### Pre-requisites 1. Setup the `Android Build Template` in your Godot project by following the tutorial [Gradle builds for Andriod](https://docs.godotengine.org/en/stable/tutorials/export/android_gradle_build.html). This will create an android project in `res://android/build`. 2. Now with reference to the tutorial [Add the EOS SDK to Your Android Studio Project](https://dev.epicgames.com/docs/epic-online-services/platforms/android#4-add-the-eos-sdk-to-your-android-studio-project), perform the following steps. 3. In the `res://android/build/build.gradle` file, add the following lines after the implementations in the `dependencies` section. ```gradle dependencies { implementation libraries.kotlinStdLib implementation libraries.androidxFragment // highlight-start // Add these lines // EOS SDK dependencies implementation 'androidx.appcompat:appcompat:1.5.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.security:security-crypto:1.0.0' implementation 'androidx.browser:browser:1.4.0' // Update the path so that it points to eossdk-StaticSTDC-release.aar provided in addons/epic-online-services-godot/bin/android/ implementation files('../../addons/epic-online-services-godot/bin/android/eossdk-StaticSTDC-release.aar') // highlight-end ...other code ``` 4. In the `res://android/build/build.gradle` file, add the following lines after the `defaultConfig` in the `android` section. ```gradle android { ... other code defaultConfig { ... other code // Feel free to modify the application id to your own. applicationId getExportPackageName() versionCode getExportVersionCode() versionName getExportVersionName() minSdkVersion getExportMinSdkVersion() targetSdkVersion getExportTargetSdkVersion() missingDimensionStrategy 'products', 'template' // highlight-start // Add these lines // This is needed by EOS Android SDK String ClientId = "PUT YOUR EOS CLIENT_ID HERE" resValue("string", "eos_login_protocol_scheme", "eos." + ClientId.toLowerCase()) // highlight-end } ... other code ``` 5. In the `res://android/build/config.gradle` file, update the `minSdk` to `23` to match with the requirements of the `EOS Android SDK`. Before ```gradle minSdk : 21, ``` After ```gradle minSdk : 23, ``` 6. In the `res://android/build/src/com/godot/game/GodotGame.java` file, update it as follows. ```java package com.godot.game; // highlight-next-line import com.epicgames.mobile.eossdk.EOSSDK; // added import org.godotengine.godot.GodotActivity; import android.os.Bundle; public class GodotApp extends GodotActivity { // highlight-next-line static { // added // highlight-next-line System.loadLibrary("EOSSDK"); // added // highlight-next-line } // added @Override public void onCreate(Bundle savedInstanceState) { EOSSDK.init(getActivity()); // added setTheme(R.style.GodotAppMainTheme); super.onCreate(savedInstanceState); } } ``` 7. Now open your project in the Godot Editor, and goto `Project -> Export` and create a new Android export profile. 8. In the `Gradle Build` section, enable `Use Gradle Build`. In the `Architectures` section enable `arm64-v8a`. In the `Permissions` section ensure that `ACESSS_NETWORK_STATE`, `ACCESS_WIFI_STATE` and `INTERNET` are enabled. These permissions are needed for the EOS SDK to work. Fill in the other details such as package name, etc as needed. 9. You can now export the Android APK by clicking the `Export Project` button. ## Initialization on iOS Export the project from Godot editor for iOS target. **You might get a build error during this process, ignore it.** Open the generated iOS project in XCode and build the project. The build should be successful. EOSG has support for `iOS arm64 device` and `iOS arm64 simulator`. ================================================ FILE: docs/docs/update-guide.md ================================================ # Update Guide Follow these steps to update to a newer version of the plugin: 1. Disable the plugin in the Editor in `Project -> Project Setings -> Plugins`. 2. Delete the `res://addons/epic-online-services-godot` folder. 3. Now you can install the newer version using any of the methods described in [Installation](/docs/installation) section 4. After installing, enable the plugin and restart the Editor. ================================================ FILE: docs/docusaurus.config.ts ================================================ import { themes as prismThemes } from "prism-react-renderer"; import type { Config } from "@docusaurus/types"; import type * as Preset from "@docusaurus/preset-classic"; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) const config: Config = { title: "Epic Online Services Godot", tagline: "Easiest way to use Epic Online Services in Godot 4.2+", favicon: "img/favicon.ico", url: "https://3ddelano.github.io", baseUrl: "/epic-online-services-godot/", // GitHub pages deployment config organizationName: "3ddelano", projectName: "epic-online-services-godot", deploymentBranch: "gh-pages", trailingSlash: false, onBrokenLinks: "throw", onBrokenMarkdownLinks: "warn", i18n: { defaultLocale: "en", locales: ["en"], }, presets: [ [ "classic", { docs: { sidebarPath: "./sidebars.ts", editUrl: "https://github.com/3ddelano/epic-online-services-godot/tree/main/docs", }, theme: { customCss: "./src/css/custom.css", }, } satisfies Preset.Options, ], ], themeConfig: { colorMode: { defaultMode: "dark", disableSwitch: true, respectPrefersColorScheme: false, }, image: "img/og-image.jpg", navbar: { title: "Epic Online Services Godot", logo: { alt: "EOSG Logo", src: "img/eosg-logo.svg", }, items: [ { type: "docSidebar", sidebarId: "docsSidebar", position: "left", label: "Docs", }, { to: "showcase", position: "left", label: "Showcase", }, { href: "https://github.com/3ddelano/epic-online-services-godot", label: "GitHub", position: "right", }, ], }, footer: { style: "dark", links: [ { title: "Docs", items: [ { label: "Docs", to: "/docs/introduction", }, ], }, { title: "Community", items: [ { label: "Discord", href: "https://discord.gg/FZY9TqW", }, ], }, { title: "More", items: [ { label: "GitHub", href: "https://github.com/facebook/docusaurus", }, { label: "Sponsor", href: "https://github.com/sponsors/3ddelano", }, ], }, ], copyright: `Copyright © ${new Date().getFullYear()} Delano Lourenco`, }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, additionalLanguages: ["gdscript", "gradle", "java"], }, } satisfies Preset.ThemeConfig, plugins: ["./src/plugins/tailwind-config.ts"], }; export default config; ================================================ FILE: docs/package.json ================================================ { "name": "docs", "version": "0.0.0", "private": true, "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start", "dev": "docusaurus start", "build": "docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "typecheck": "tsc" }, "dependencies": { "@docusaurus/core": "^3.9.2", "@docusaurus/preset-classic": "^3.9.2", "@mdx-js/react": "^3.0.0", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001769", "clsx": "^2.0.0", "prism-react-renderer": "^2.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-player": "^2.16.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.9.2", "@docusaurus/tsconfig": "^3.9.2", "@docusaurus/types": "^3.9.2", "@tailwindcss/postcss": "^4.1.3", "postcss": "^8.5.3", "tailwindcss": "^4.1.3", "typescript": "~5.6.2" }, "browserslist": { "production": [ ">0.5%", "not dead", "not op_mini all" ], "development": [ "last 3 chrome version", "last 3 firefox version", "last 5 safari version" ] }, "engines": { "node": ">=18.0" } } ================================================ FILE: docs/postcss.config.js ================================================ module.exports = { plugins: { '@tailwindcss/postcss': {}, } } ================================================ FILE: docs/sidebars.ts ================================================ import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) /** * Creating a sidebar enables you to: - create an ordered group of docs - render a sidebar for each doc of that group - provide next/previous navigation The sidebars can be generated from the filesystem, or explicitly defined here. Create as many sidebars as you want. */ const sidebars: SidebarsConfig = { // By default, Docusaurus generates a sidebar from the docs folder structure // docsSidebar: [{type: 'autogenerated', dirName: '.'}], // But you can create a sidebar manually docsSidebar: [ 'introduction', 'installation', 'update-guide', 'sample-project', { type: 'category', label: 'Topics', items: ['topics/initialization', 'topics/authentication'], collapsed: false }, ], }; export default sidebars; ================================================ FILE: docs/src/components/ExampleSection.tsx ================================================ import CodeBlock from "@theme/CodeBlock"; const codeExample = ` # In main script extends Node func _ready() -> void: # Setup HEOS Logs HLog.log_level = HLog.LogLevel.INFO var init_opts = EOS.Platform.InitializeOptions.new() init_opts.product_name = "PRODUCT_NAME_HERE" init_opts.product_version = "PRODUCT_VERSION_HERE" var create_opts = EOS.Platform.CreateOptions.new() create_opts.product_id = "PRODUCT_ID_HERE" create_opts.sandbox_id = "SANDBOX_ID_HERE" create_opts.deployment_id = "DEPLOYMENT_ID_HERE" create_opts.client_id = "CLIENT_ID_HERE" create_opts.client_secret = "CLIENT_SECRET_HERE" create_opts.encryption_key = "ENCRYPTION_KEY_HERE" # Enable Social Overlay on Windows if OS.get_name() == "Windows": HAuth.auth_login_flags = EOS.Auth.LoginFlags.None create_opts.flags = EOS.Platform.PlatformFlags.WindowsEnableOverlayOpengl # Initialize the SDK var init_res := await HPlatform.initialize_async(init_opts) if not EOS.is_success(init_res): printerr("Failed to initialize EOS SDK: ", EOS.result_str(init_res)) return # Create platform var create_success := await HPlatform.create_platform_async(create_opts) if not create_success: printerr("Failed to create EOS Platform") return # Setup Logs from EOS HPlatform.log_msg.connect(_on_eos_log_msg) var log_res := HPlatform.set_eos_log_level(EOS.Logging.LogCategory.AllCategories, EOS.Logging.LogLevel.Info) if not EOS.is_success(log_res): printerr("Failed to set logging level") return HAuth.logged_in.connect(_on_logged_in) # During development use the devauth tool to login HAuth.login_devtool_async("localhost:4545", "CREDENTIAL_NAME_HERE") # Only on mobile device (Login without any credentials) # await HAuth.login_anonymous_async() func _on_logged_in(): print("Logged in successfully: product_user_id=%s" % HAuth.product_user_id) # Example: Get top records for a leaderboard var records := await HLeaderboards.get_leaderboard_records_async("LEADERBOARD_ID_HERE") print(records) func _on_eos_log_msg(msg: EOS.Logging.LogMessage) -> void: print("SDK %s | %s" % [msg.category, msg.message]) `; export function ExampleSection() { return (

Basic Usage Example (GDScript)

{codeExample}
); } ================================================ FILE: docs/src/components/FeaturesSection.tsx ================================================ const FEATURES = [ "Authentication", "Social Overlay (Win)", "Achievements", "Stats & Leaderboards", "Lobby & Sessions", "Multiplayer (P2P)", "Voice", "Metrics", "Mods", "Player/Title Storage", "Progression Snapshot", "Reports & Sanctions", "Ecommerce (EGS)", "AntiCheat", ]; const HIGH_LEVEL_CLASSES = [ "HPlatform", "HAuth", "HAchievements", "HFriends", "HStats", "HLeaderboards", "HLobbies", "HP2P", "HLog", ]; export function FeaturesSection() { return (

Core Features

All the EOS features you need, wrapped in a clean GDScript API.

{FEATURES.map((feature, index) => (
{feature}
))}
); } export function HighLevelSection() { return (

High Level EOS Classes

These classes simplify interaction with EOS services. They include built-in documentation accessible directly within the Godot Editor.

{HIGH_LEVEL_CLASSES.map((cls, index) => (
{cls}
))}
); } ================================================ FILE: docs/src/components/HeroSection.tsx ================================================ import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import { GodotIcon, EpicIcon } from "../components/Icons"; const VERSIONS = [ { label: "Godot", value: "4.2+", icon: GodotIcon }, { label: "EOS SDK", value: "1.18.1.2", icon: EpicIcon }, ]; const PLATFORMS = [ { label: "Windows x64/arm64", icon: "🖥️" }, { label: "Linux x64/arm64", icon: "🐧" }, { label: "Android x64/arm64", icon: "🤖" }, { label: "MacOS x64/arm64", icon: "🍎" }, { label: "iOS arm64", icon: "🍏" }, ]; const HERO_ACTIONS = [ { label: "Download Latest", href: "https://github.com/3ddelano/epic-online-services-godot/releases/latest", primary: true, target: "_blank", }, { label: "Documentation", href: "docs/introduction", primary: false, }, ]; export function HeroSection() { const { siteConfig } = useDocusaurusContext(); return (
{/* Glow orbs */}
{/* Left col */}

{siteConfig.title}

{siteConfig.tagline}

{VERSIONS.map((v, i) => ( {v.label} {v.value} ))}
{HERO_ACTIONS.map((action, i) => ( {action.label} ))}

Supported Platforms

{PLATFORMS.map((platform, i) => ( {platform.icon} {platform.label} ))}
{/* Logo Column */}
Logo
); } ================================================ FILE: docs/src/components/Icons.tsx ================================================ export function GodotIcon({ className = "w-4 h-4" }) { return ( ); } export function EpicIcon({ className = "w-4 h-4" }) { return ( ); } ================================================ FILE: docs/src/components/InfoSections.tsx ================================================ export function HowItWorksSection() { return (

How It Works

This project utilizes Godot's{" "} GDExtension system to wrap the official Epic Online Services C SDK. This allows seamless integration and usage within Godot projects using GDScript, C#, or other supported languages. It provides a familiar class hierarchy and leverages Godot's signal system for handling asynchronous events like user login, achievement unlocks, lobby updates, and more.

); } export function InstallationSection() { return (

Installation

Method 1: Asset Library (Recommended)

  1. Open the AssetLib tab in the Godot editor.
  2. Search for{" "} EOSG {" "} or{" "} Epic Online Services Godot .
  3. Find the plugin by{" "} 3ddelano and click on it.
  4. Click Download, then{" "} Install.
  5. Go to{" "} Project {"->"} Project Settings {"->"} Plugins .
  6. Enable the{" "} Epic Online Services Godot (EOSG) {" "} plugin.
  7. Restart the Godot editor.
View on Godot Asset Library

Method 2: GitHub Release

  1. Download the latest release{" "} .zip {" "} file from GitHub.
  2. Extract the zip archive.
  3. Copy the{" "} addons/epic-online-services-godot {" "} folder into your project's{" "} res://addons/ {" "} folder (create it if needed).
  4. Go to{" "} Project {"->"} Project Settings {"->"} Plugins .
  5. Enable the{" "} Epic Online Services Godot (EOSG) {" "} plugin.
  6. Restart the Godot editor.
Download from GitHub Releases

After installation, you can start using the{" "} EOS {" "} and{" "} H {" "} classes in your scripts.

); } ================================================ FILE: docs/src/components/Layout.tsx ================================================ import { useState } from "react"; // Navigation links data const NAV_LINKS = [ { label: "Docs", href: "docs/introduction", external: false, }, { label: "Showcase", href: "showcase", external: false, }, { label: "GitHub", href: "https://github.com/3ddelano/epic-online-services-godot", external: true, icon: ( ), }, ]; export function NavBar() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); return ( ); } export function Footer() { return (

Disclaimer: This project Epic Online Services Godot (EOSG) is NOT{" "} affiliated with Epic Games Inc or Godot Engine. It does not endorse Epic Online Services. This project and the sample Godot scenes are provided solely for educational purposes and may or may not comply with Epic Games' Design Guidelines. If you plan to release a game using EOS, ensure you read the official{" "} Guidelines {" "} and fulfill all requirements (user consent, data deletion, privacy policy, etc.).

Copyright © {new Date().getFullYear()}{" "} Delano Lourenco . All rights reserved.

Epic Games, Epic Online Services, Godot Engine, and their respective logos are trademarks or registered trademarks of their respective owners.

); } ================================================ FILE: docs/src/components/MainSystemsSection.tsx ================================================ const MAIN_SYSTEMS = [ { title: "High Level EOS", tag: "Recommended", description: "Provides easy-to-use methods and signals to interact with EOS. Ideal for most cases and rapid development.", link: "docs/introduction", icon: ( ), color: "blue", }, { title: "GDExtension EOS", tag: "Advanced", description: "Offers advanced EOS usage with direct access to the C SDK via GDExtension. Suitable for complex needs.", extra: ( <> Provides{" "} EOS {" "} and{" "} IEOS {" "} classes. ), icon: ( ), color: "purple", }, ]; export function MainSystemsSection() { return (

Two Main Systems

Choose the approach that fits your project's complexity.

{MAIN_SYSTEMS.map((system, i) => (
{system.icon}

{system.title}

{system.tag}

{system.description}

{system.link && ( Learn more )} {system.extra && (

{system.extra}

)}
))}
); } ================================================ FILE: docs/src/components/MediaSections.tsx ================================================ const SCREENSHOTS_DATA = [ { category: "Windows", images: [ { src: "img/screenshots/windows_auth_success.png", alt: "Windows Auth Success Screenshot", }, ], }, { category: "Android", images: [ { src: "img/screenshots/android_auth_success.jpg", alt: "Android Auth Success Screenshot", }, { src: "img/screenshots/android_p2p_game.jpg", alt: "Android P2P Game Screenshot", }, ], }, { category: "iOS / macOS", images: [ { src: "img/screenshots/ios_simulator_auth_success.png", alt: "iOS Simulator Auth Success Screenshot", }, { src: "img/screenshots/ios_simulator_in_lobby.png", alt: "iOS Simulator In Lobby Screenshot", subtitle: "Cross-Platform Lobbies", }, { src: "img/screenshots/mac_in_lobby.png", alt: "macOS In Lobby Screenshot", }, ], }, ]; const TUTORIALS_DATA = { playlist_url: "https://www.youtube.com/playlist?list=PL5t0hR7ADzun5JYF4e2a2FtZEWYHxK83_", thumbnail_url: "https://img.youtube.com/vi/ENyvF4yVjKg/0.jpg", thumbnail_alt: "Epic Online Services Tutorial Series Thumbnail", }; export function ScreenshotsSection() { return (

Screenshots & Demos

See the plugin running on multiple platforms.

{SCREENSHOTS_DATA.map((platform, i) => (

{platform.category}

{platform.images.map((image, j) => (
{image.subtitle && (

{image.subtitle}

)} {image.alt}
))}
))}
); } export function DemoSection() { return (

Video Tutorials

Watch the tutorial playlist on YouTube for a visual guide and examples.

Watch the playlist on YouTube →
); } ================================================ FILE: docs/src/components/SupportSection.tsx ================================================ const SUPPORT_DATA = { bmc_url: "https://www.buymeacoffee.com/3ddelano", github_sponsor_url: "https://github.com/sponsors/3ddelano", discord_url: "https://discord.gg/FZY9TqW", username: "@3ddelano", }; export function SupportSection() { return (
{/* Background glow */}

Support Development

Creating and maintaining this plugin requires significant time and effort. If you find this project helpful, please consider supporting its development.

Need help, have suggestions, or found a bug?

Join Discord or DM{" "} {SUPPORT_DATA.username}
); } ================================================ FILE: docs/src/css/custom.css ================================================ @import "tailwindcss"; @import url("https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"); :root { --ifm-font-family-base: "Outfit", sans-serif; --ifm-font-family-monospace: "JetBrains Mono", monospace; --ifm-color-primary: #3b82f6; --ifm-color-primary-dark: #2563eb; --ifm-color-primary-darker: #1d4ed8; --ifm-color-primary-darkest: #1e40af; --ifm-color-primary-light: #60a5fa; --ifm-color-primary-lighter: #93c5fd; --ifm-color-primary-lightest: #bfdbfe; --ifm-code-font-size: 90%; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); --brand-gradient: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); --glass-bg: rgba(255, 255, 255, 0.03); --glass-border: rgba(255, 255, 255, 0.08); } [data-theme='dark'] { --ifm-background-color: #030712; --ifm-navbar-background-color: rgba(3, 7, 18, 0.8); --ifm-footer-background-color: #030712; --ifm-color-primary: #60a5fa; --ifm-color-primary-dark: #3b82f6; --ifm-color-primary-darker: #2563eb; --ifm-color-primary-darkest: #1d4ed8; --ifm-color-primary-light: #93c5fd; --ifm-color-primary-lighter: #bfdbfe; --ifm-color-primary-lightest: #dbeafe; --docusaurus-highlighted-code-line-bg: rgba(255, 255, 255, 0.1); } /* Glassmorphism utility */ .glass { background: var(--glass-bg); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid var(--glass-border); } .hero-image { background-color: #030712; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='192' height='192' viewBox='0 0 192 192'%3E%3Cpath fill='%233b82f6' fill-opacity='0.04' d='M192 15v2a11 11 0 0 0-11 11c0 1.94 1.16 4.75 2.53 6.11l2.36 2.36a6.93 6.93 0 0 1 1.22 7.56l-.43.84a8.08 8.08 0 0 1-6.66 4.13H145v35.02a6.1 6.1 0 0 0 3.03 4.87l.84.43c1.58.79 4 .4 5.24-.85l2.36-2.36a12.04 12.04 0 0 1 7.51-3.11 13 13 0 1 1 .02 26 12 12 0 0 1-7.53-3.11l-2.36-2.36a4.93 4.93 0 0 0-5.24-.85l-.84.43a6.1 6.1 0 0 0-3.03 4.87V143h35.02a8.08 8.08 0 0 1 6.66 4.13l.43.84a6.91 6.91 0 0 1-1.22 7.56l-2.36 2.36A10.06 10.06 0 0 0 181 164a11 11 0 0 0 11 11v2a13 13 0 0 1-13-13 12 12 0 0 1 3.11-7.53l2.36-2.36a4.93 4.93 0 0 0 .85-5.24l-.43-.84a6.1 6.1 0 0 0-4.87-3.03H145v35.02a8.08 8.08 0 0 1-4.13 6.66l-.84.43a6.91 6.91 0 0 1-7.56-1.22l-2.36-2.36A10.06 10.06 0 0 0 124 181a11 11 0 0 0-11 11h-2a13 13 0 0 1 13-13c2.47 0 5.79 1.37 7.53 3.11l2.36 2.36a4.94 4.94 0 0 0 5.24.85l.84-.43a6.1 6.1 0 0 0 3.03-4.87V145h-35.02a8.08 8.08 0 0 1-6.66-4.13l-.43-.84a6.91 6.91 0 0 1 1.22-7.56l2.36-2.36A10.06 10.06 0 0 0 107 124a11 11 0 0 0-22 0c0 1.94 1.16 4.75 2.53 6.11l2.36 2.36a6.93 6.93 0 0 1 1.22 7.56l-.43.84a8.08 8.08 0 0 1-6.66 4.13H49v35.02a6.1 6.1 0 0 0 3.03 4.87l.84.43c1.58.79 4 .4 5.24-.85l2.36-2.36a12.04 12.04 0 0 1 7.51-3.11A13 13 0 0 1 81 192h-2a11 11 0 0 0-11-11c-1.94 0-4.75 1.16-6.11 2.53l-2.36 2.36a6.93 6.93 0 0 1-7.56 1.22l-.84-.43a8.08 8.08 0 0 1-4.13-6.66V145H11.98a6.1 6.1 0 0 0-4.87 3.03l-.43.84c-.79 1.58-.4 4 .85 5.24l2.36 2.36a12.04 12.04 0 0 1 3.11 7.51A13 13 0 0 1 0 177v-2a11 11 0 0 0 11-11c0-1.94-1.16-4.75-2.53-6.11l-2.36-2.36a6.93 6.93 0 0 1-1.22-7.56l.43-.84a8.08 8.08 0 0 1 6.66-4.13H47v-35.02a6.1 6.1 0 0 0-3.03-4.87l-.84-.43c-1.59-.8-4-.4-5.24.85l-2.36 2.36A12 12 0 0 1 28 109a13 13 0 1 1 0-26c2.47 0 5.79 1.37 7.53 3.11l2.36 2.36a4.94 4.94 0 0 0 5.24.85l.84-.43A6.1 6.1 0 0 0 47 84.02V49H11.98a8.08 8.08 0 0 1-6.66-4.13l-.43-.84a6.91 6.91 0 0 1 1.22-7.56l2.36-2.36A10.06 10.06 0 0 0 11 28 11 11 0 0 0 0 17v-2a13 13 0 0 1 13 13c0 2.47-1.37 5.79-3.11 7.53l-2.36 2.36a4.94 4.94 0 0 0-.85 5.24l.43.84A6.1 6.1 0 0 0 11.98 47H47V11.98a8.08 8.08 0 0 1 4.13-6.66l.84-.43a6.91 6.91 0 0 1 7.56 1.22l2.36 2.36A10.06 10.06 0 0 0 68 11 11 11 0 0 0 79 0h2a13 13 0 0 1-13 13 12 12 0 0 1-7.53-3.11l-2.36-2.36a4.93 4.93 0 0 0-5.24-.85l-.84.43A6.1 6.1 0 0 0 49 11.98V47h35.02a8.08 8.08 0 0 1 6.66 4.13l.43.84a6.91 6.91 0 0 1-1.22 7.56l-2.36 2.36A10.06 10.06 0 0 0 85 68a11 11 0 0 0 22 0c0-1.94-1.16-4.75-2.53-6.11l-2.36-2.36a6.93 6.93 0 0 1-1.22-7.56l.43-.84a8.08 8.08 0 0 1 6.66-4.13H143V11.98a6.1 6.1 0 0 0-3.03-4.87l-.84-.43c-1.59-.8-4-.4-5.24.85l-2.36 2.36A12 12 0 0 1 124 13a13 13 0 0 1-13-13h2a11 11 0 0 0 11 11c1.94 0 4.75-1.16 6.11-2.53l2.36-2.36a6.93 6.93 0 0 1 7.56-1.22l.84.43a8.08 8.08 0 0 1 4.13 6.66V47h35.02a6.1 6.1 0 0 0 4.87-3.03l.43-.84c.8-1.59.4-4-.85-5.24l-2.36-2.36A12 12 0 0 1 179 28a13 13 0 0 1 13-13zM84.02 143a6.1 6.1 0 0 0 4.87-3.03l.43-.84c.8-1.59.4-4-.85-5.24l-2.36-2.36A12 12 0 0 1 83 124a13 13 0 1 1 26 0c0 2.47-1.37 5.79-3.11 7.53l-2.36 2.36a4.94 4.94 0 0 0-.85 5.24l.43.84a6.1 6.1 0 0 0 4.87 3.03H143v-35.02a8.08 8.08 0 0 1 4.13-6.66l.84-.43a6.91 6.91 0 0 1 7.56 1.22l2.36 2.36A10.06 10.06 0 0 0 164 107a11 11 0 0 0 0-22c-1.94 0-4.75 1.16-6.11 2.53l-2.36 2.36a6.93 6.93 0 0 1-7.56 1.22l-.84-.43a8.08 8.08 0 0 1-4.13-6.66V49h-35.02a6.1 6.1 0 0 0-4.87 3.03l-.43.84c-.79 1.58-.4 4 .85 5.24l2.36 2.36a12.04 12.04 0 0 1 3.11 7.51A13 13 0 1 1 83 68a12 12 0 0 1 3.11-7.53l2.36-2.36a4.93 4.93 0 0 0 .85-5.24l-.43-.84A6.1 6.1 0 0 0 84.02 49H49v35.02a8.08 8.08 0 0 1-4.13 6.66l-.84.43a6.91 6.91 0 0 1-7.56-1.22l-2.36-2.36A10.06 10.06 0 0 0 28 85a11 11 0 0 0 0 22c1.94 0 4.75-1.16 6.11-2.53l2.36-2.36a6.93 6.93 0 0 1 7.56-1.22l.84.43a8.08 8.08 0 0 1 4.13 6.66V143h35.02z'%3E%3C/path%3E%3C/svg%3E"); } /* Navbar glassmorphism */ .navbar { backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-bottom: 1px solid var(--glass-border); /* Override Docusaurus link color variables */ --ifm-link-color: white; --ifm-link-hover-color: #60a5fa; --ifm-link-decoration: none; --ifm-link-hover-decoration: none; } /* Override Docusaurus link colors in navbar */ .navbar a:not(.button-secondary) { color: white !important; } .navbar a:not(.button-secondary):hover, .navbar a:not(.button-secondary):active { color: #60a5fa !important; } /* Button system */ .button-primary { background: var(--brand-gradient); border: none; color: white; font-weight: 600; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3); } .button-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(59, 130, 246, 0.5); color: white !important; text-decoration: none; } .button-secondary { background: var(--glass-bg); border: 1px solid var(--glass-border); color: white; backdrop-filter: blur(8px); transition: all 0.3s ease; } .button-secondary:hover { background: rgba(255, 255, 255, 0.08); border-color: rgba(255, 255, 255, 0.2); color: white !important; text-decoration: none; } /* Feature cards */ .feature-card { background: var(--glass-bg); border: 1px solid var(--glass-border); backdrop-filter: blur(8px); transition: all 0.3s ease; } .feature-card:hover { border-color: rgba(255, 255, 255, 0.15); transform: translateY(-2px); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); } ================================================ FILE: docs/src/pages/index.tsx ================================================ import { HeroSection } from "../components/HeroSection"; import { MainSystemsSection } from "../components/MainSystemsSection"; import { FeaturesSection } from "../components/FeaturesSection"; import { ScreenshotsSection, DemoSection } from "../components/MediaSections"; import { SupportSection } from "../components/SupportSection"; import { Footer, NavBar } from "../components/Layout"; export default function App() { return (
); } ================================================ FILE: docs/src/pages/showcase.md ================================================ --- --- # Showcase Here is a list of games using the Epic Online Services Godot plugin. If you are using this plugin and want to list your game in this list. Create an Issue on the [Github Repo](https://github.com/3ddelano/epic-online-services-godot/issues) with your game URL and which features of the plugin you used, or leave a message in the [Discord server](https://discord.gg/FZY9TqW). - ### Kamaeru: A Frog Refuge Foster a sanctuary for frogs and restore the biodiversity of the wetlands in Kamaeru, a cozy frog collecting game, where you take pictures of frogs, play mini-games and decorate your habitat. Hop right to it! Links: [Steam](https://store.epicgames.com/en-US/p/kamaeru-0c301e) ![Kamaeru Logo](/img/showcase/kamaeru-logo.png) > Features Used: Achievements, Cloud Saves / PlayerDataStorage

- ### Kitchen Together Get ready to stir up some fun in the ultimate cooking challenge! Join your friends in this engaging multiplayer game where teamwork meets culinary creativity. Choose your kitchen role, from chopping and frying to decorating and serving, as you race against the clock to prepare delicious dishes. Links: [Android Play Store](https://play.google.com/store/apps/details?id=com.kitchentogether) | [iOS App Store](https://apps.apple.com/us/app/kitchen-together-2/id6748915442) ![Kitchen Together Logo](/img/showcase/kitchen-together-logo.webp) > Features Used: Voice Chat, P2P, Lobby

- ### Hamsteria Hamsteria is a short (~1-2 hours) difficult two player co-op game where you're each a hamster in attached wheels. You can only spin your individual wheel forwards or backwards. There is no jump button, and there is no turn action. To escape, you must work together. Links: [Itch.io](https://2wheelerdev.itch.io/hamsteria) ![Hamsteria Logo](/img/showcase/hamsteria-logo.webp) > Features Used: P2P, Lobby ================================================ FILE: docs/src/plugins/tailwind-config.ts ================================================ module.exports = function tailwindPlugin(context, options) { return { name: 'tailwind-plugin', configurePostCss(postcssOptions) { postcssOptions.plugins = [require('@tailwindcss/postcss')] return postcssOptions } } } ================================================ FILE: docs/static/.nojekyll ================================================ ================================================ FILE: docs/tsconfig.json ================================================ { // This file is not used in compilation. It is here just for a nice editor experience. "extends": "@docusaurus/tsconfig", "compilerOptions": { "baseUrl": "." }, "exclude": [".docusaurus", "build"] } ================================================ FILE: function_analyzer.py ================================================ # eos_function_usage_checker.py import os import re # Function to extract function names from the EOS C SDK header files def extract_function_names(sdk_path): function_declarations = {} pattern = re.compile(r"EOS_DECLARE_FUNC\(\w+\)\s+(EOS_\w+)\(") for root, _, files in os.walk(sdk_path): for file in files: if file.endswith(".h"): with open(os.path.join(root, file), 'r', encoding='utf-8') as f: content = f.read() matches = pattern.findall(content) for match in matches: function_declarations[match] = os.path.join(root, file) return function_declarations # Function to search for function names in the src folder def find_function_calls(src_path, function_names): function_usage = {name: [] for name in function_names} pattern = re.compile(r"(\bEOS_\w+\b)") for root, _, files in os.walk(src_path): for file in files: if file.endswith(".h") or file.endswith(".cpp"): with open(os.path.join(root, file), 'r', encoding='utf-8') as f: content = f.read() matches = pattern.findall(content) for match in matches: if match in function_usage: function_usage[match].append(os.path.join(root, file)) return function_usage def main(): sdk_path = 'thirdparty/eos-sdk/SDK' # Replace with the actual path to the EOS C SDK folder src_path = 'src' # Replace with the actual path to the src folder whitelist = ['EOS_Achievements_CopyAchievementDefinitionByIndex', 'EOS_Achievements_CopyAchievementDefinitionByAchievementId', 'EOS_Achievements_GetUnlockedAchievementCount', 'EOS_Achievements_CopyUnlockedAchievementByIndex', 'EOS_Achievements_CopyUnlockedAchievementByAchievementId', 'EOS_Achievements_AddNotifyAchievementsUnlocked', 'EOS_AntiCheatClient_PollStatus', 'EOS_ByteArray_ToString', 'EOS_EpicAccountId_IsValid', 'EOS_ProductUserId_IsValid', ] # Step 1: Extract function names from SDK header files function_declarations = extract_function_names(sdk_path) function_names = list(function_declarations.keys()) print(f"Extracted {len(function_names)} function names from SDK headers.") # Step 2: Search for these function names in the src folder function_usage = find_function_calls(src_path, function_names) # Print the results print("SDK functions not called by the plugin:") uncalled_count = 0 for function, files in function_usage.items(): if files: continue if function in whitelist: continue if "RemoveNotify" in function: continue uncalled_count += 1 print(f"{function} : {function_declarations[function]}") print(f"\nTotal number of SDK functions not called by the plugin: {uncalled_count}") if __name__ == "__main__": main() ================================================ FILE: sample/.gitignore ================================================ .import/ export.cfg export_presets.cfg *.translation .godot/ *.env *.import ================================================ FILE: sample/AntiCheatServerMain.gd ================================================ class_name AntiCheatServerMain extends Node @onready var PRODUCT_NAME: String = Env.get_var("PRODUCT_NAME") @onready var PRODUCT_VERSION: String = Env.get_var("PRODUCT_VERSION") @onready var PRODUCT_ID: String = Env.get_var("PRODUCT_ID") @onready var SANDBOX_ID: String = Env.get_var("SANDBOX_ID") @onready var DEPLOYMENT_ID: String = Env.get_var("DEPLOYMENT_ID") @onready var CLIENT_ID: String = Env.get_var("CLIENT_ID") @onready var CLIENT_SECRET: String = Env.get_var("CLIENT_SECRET") @onready var ENCRYPTION_KEY: String = Env.get_var("ENCRYPTION_KEY") const PORT = 12345 func _ready() -> void: print("AntiCheatServer _ready") var init_success = await _load_and_init_sdk() if not init_success: return _begin_session() _start_server() func _load_and_init_sdk() -> bool: # ----- # EOS Setup # ----- # Initialize the SDK var init_opts = EOS.Platform.InitializeOptions.new() init_opts.product_name = PRODUCT_NAME init_opts.product_version = PRODUCT_VERSION var init_res := EOS.Platform.PlatformInterface.initialize(init_opts) var init_retry_count = 10 while not EOS.is_success(init_res) and init_retry_count > 0: init_res = EOS.Platform.PlatformInterface.initialize(init_opts) init_retry_count -= 1 await get_tree().create_timer(0.2).timeout if not EOS.is_success(init_res): print("Failed to initialize EOS SDK: ", EOS.result_str(init_res)) return false print("Initialized EOS platform") # Create platform var create_opts = EOS.Platform.CreateOptions.new() create_opts.product_id = PRODUCT_ID create_opts.sandbox_id = SANDBOX_ID create_opts.deployment_id = DEPLOYMENT_ID create_opts.client_id = CLIENT_ID create_opts.client_secret = CLIENT_SECRET create_opts.encryption_key = ENCRYPTION_KEY create_opts.is_server = true create_opts.flags = EOS.Platform.PlatformFlags.DisableOverlay var create_success: bool = EOS.Platform.PlatformInterface.create(create_opts) var create_retry_count = 10 while not create_success&&create_retry_count > 0: create_success = EOS.Platform.PlatformInterface.create(create_opts) create_retry_count -= 1 await get_tree().create_timer(0.2).timeout if not create_success: print("Failed to create EOS Platform") return false print("Created EOS platform") return true func _begin_session(): IEOS.anticheatserver_interface_message_to_client_callback.connect(func(data): print("--- AntiCheatServer: message_to_client_callback: ", data) ) IEOS.anticheatserver_interface_client_action_required_callback.connect(func(data): print("--- AntiCheatServer: client_action_required_callback: ", data) ) IEOS.anticheatserver_interface_client_auth_status_changed_callback.connect(func(data): print("--- AntiCheatServer: client_auth_status_changed_callback: ", data) ) var begin_sess_opts = EOS.AntiCheatServer.BeginSessionOptions.new() begin_sess_opts.register_timeout_seconds = 60 begin_sess_opts.server_name = "Godot 4.2 server" begin_sess_opts.enable_gameplay_data = false print("--- AntiCheatServer: begin_session: ", EOS.AntiCheatServer.AntiCheatServerInterface.begin_session(begin_sess_opts)) func _verify_id_token(_peer_id: int, product_user_id: String, connect_id_jwt: String): var id_token = EOS.Connect.IdToken.new() id_token.json_web_token = connect_id_jwt id_token.product_user_id = product_user_id var verify_token_opts = EOS.Connect.VerifyIdTokenOptions.new() verify_token_opts.id_token = id_token EOS.Connect.ConnectInterface.verify_id_token(verify_token_opts) print("--- AntiCheatServer: verify_id_token: ", await IEOS.connect_interface_verify_id_token_callback) func _start_server(): multiplayer.peer_connected.connect(func(id: int): print("--- AntiCheatServer:: peer_connected: ", id) ) multiplayer.peer_disconnected.connect(func(id: int): print("--- AntiCheatServer:: peer_disconnected: ", id) ) var peer = ENetMultiplayerPeer.new() peer.create_server(PORT) multiplayer.multiplayer_peer = peer print("Listening for clients on port: ", PORT) func on_client_message_receive(peer_id: int, type: String, data: Dictionary): if type == "register": _verify_id_token(peer_id, data.local_user_id, data.jwt) ================================================ FILE: sample/AntiCheatServerMain.gd.uid ================================================ uid://clbqhgyalkxuw ================================================ FILE: sample/Main.gd ================================================ extends Control @onready var PRODUCT_NAME: String = Env.get_var("PRODUCT_NAME") # Paste your own instead of Env.get_var @onready var PRODUCT_VERSION: String = Env.get_var("PRODUCT_VERSION") # Paste your own @onready var PRODUCT_ID: String = Env.get_var("PRODUCT_ID") # Paste your own @onready var SANDBOX_ID: String = Env.get_var("SANDBOX_ID") # Paste your own @onready var DEPLOYMENT_ID: String = Env.get_var("DEPLOYMENT_ID") # Paste your own @onready var CLIENT_ID: String = Env.get_var("CLIENT_ID") # Paste your own @onready var CLIENT_SECRET: String = Env.get_var("CLIENT_SECRET") # Paste your own @onready var ENCRYPTION_KEY: String = Env.get_var("ENCRYPTION_KEY") # Paste your own @export var _views_path: NodePath @onready var views = get_node(_views_path) as VBoxContainer func _ready() -> void: if _check_for_dedicated_server_sample(): return print("Ready!") Store._main_node = self HLog.log_level = HLog.LogLevel.INFO # ----- # EOS Setup # ----- var credentials = HCredentials.new() credentials.product_name = PRODUCT_NAME credentials.product_version = PRODUCT_VERSION credentials.product_id = PRODUCT_ID credentials.sandbox_id = SANDBOX_ID credentials.deployment_id = DEPLOYMENT_ID credentials.client_id = CLIENT_ID credentials.client_secret = CLIENT_SECRET credentials.encryption_key = ENCRYPTION_KEY print("Setting up EOS...") var setup_success := await HPlatform.setup_eos_async(credentials) if not setup_success: printerr("Failed to setup EOS. See logs for error details") return var sdk_constants := EOS.Version.VersionInterface.get_constants() print("EOS SDK Version: %s (%s)" % [EOS.Version.VersionInterface.get_version(), sdk_constants.copyright_string]) # See LoginView.gd for the user login flow _parse_cmdline_user_args() func get_view_manager(): return views func _notification(what: int) -> void: if what == NOTIFICATION_WM_CLOSE_REQUEST: print("Shutting down EOS...") EOS.Platform.PlatformInterface.release() var res := EOS.Platform.PlatformInterface.shutdown() if not EOS.is_success(res): printerr("Failed to shutdown EOS: ", EOS.result_str(res)) func _check_for_dedicated_server_sample() -> bool: # Ignore this method # This is for the sample project to dynamically load the # dedicated server example when certain cli flags are passed var args = OS.get_cmdline_user_args() for arg in args: if arg.begins_with("--screenpos="): var rows = 2 var cols = 2 var pos := int(arg.replace("--screenpos=", "")) var screen_size = DisplayServer.screen_get_size() var scale_x = screen_size.x / cols var scale_y = screen_size.y / rows var x = (pos - 1) % cols @warning_ignore("integer_division") var y = (pos - 1) / rows get_window().position = Vector2(x * scale_x, y * scale_y) get_window().size = Vector2(screen_size.x / cols, screen_size.y / rows) if "--eosg-dedicated-server-example" in args: if "--eosg-server" in args: _load_dedicated_server_script() return true if "--eosg-client" in args: _load_dedicated_client_script() return true return false func _load_dedicated_server_script(): var SCENE := load("res://dedicated_server_example/server_main.tscn") as PackedScene var server = SCENE.instantiate() add_child(server) func _load_dedicated_client_script(): var SCENE := load("res://dedicated_server_example/client_main.tscn") as PackedScene var client = SCENE.instantiate() add_child(client) func _parse_cmdline_user_args(): var args = OS.get_cmdline_user_args() for arg in args: if arg.begins_with("--devuser="): var login_view = Store.get_view("Login") var username = arg.replace("--devuser=", "") login_view._set_login_status("Logging in with devtool using cli arg...") login_view._set_login_state(LoginView.States.Pending) HAuth.login_devtool_async("localhost:4545", username) if arg.begins_with("--screenpos="): var rows = 2 var cols = 2 var pos := int(arg.replace("--screenpos=", "")) var screen_size = DisplayServer.screen_get_size() var scale_x = screen_size.x / cols var scale_y = screen_size.y / rows var x = (pos - 1) % cols @warning_ignore("integer_division") var y = (pos - 1) / rows get_window().position = Vector2(x * scale_x, y * scale_y) get_window().size = Vector2(screen_size.x / cols, screen_size.y / rows) ================================================ FILE: sample/Main.gd.uid ================================================ uid://cf8kvjx2nbysy ================================================ FILE: sample/Main.tscn ================================================ [gd_scene load_steps=25 format=3 uid="uid://gcoi8ld0uvkl"] [ext_resource type="Theme" uid="uid://dwns7wwoyi1hy" path="res://components/StyledPopupWindow/StyledPopupWindowTheme.tres" id="1_dhxmx"] [ext_resource type="PackedScene" uid="uid://d3hxdkoy73pf1" path="res://scenes/AchievementsView/AchievementsView.tscn" id="2"] [ext_resource type="Script" uid="uid://cf8kvjx2nbysy" path="res://Main.gd" id="3"] [ext_resource type="Script" uid="uid://b8dbhfvrewi8w" path="res://scripts/ViewManager.gd" id="3_j157n"] [ext_resource type="PackedScene" uid="uid://cl2qvlvdhn2iw" path="res://scenes/LogsView/LogsView.tscn" id="4"] [ext_resource type="PackedScene" uid="uid://bht0ln2ftshrw" path="res://scenes/LoginView/LoginView.tscn" id="5"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="6"] [ext_resource type="PackedScene" uid="uid://d2bkej1dakv6s" path="res://scenes/CustomInvitesView/CustomInvitesView.tscn" id="9"] [ext_resource type="PackedScene" uid="uid://dgi7j5qx3xt84" path="res://scenes/StatsView/StatsView.tscn" id="10"] [ext_resource type="PackedScene" uid="uid://5xrk7nvpwosj" path="res://scenes/LeaderboardsView/LeaderboardsView.tscn" id="11"] [ext_resource type="PackedScene" uid="uid://t016k2yh382d" path="res://scenes/FriendsView/FriendsView.tscn" id="12"] [ext_resource type="PackedScene" uid="uid://b51w7a6ofuubp" path="res://scenes/UIView/UIView.tscn" id="13"] [ext_resource type="PackedScene" uid="uid://dsiyt23hnmngd" path="res://scenes/LobbiesView/LobbiesView.tscn" id="13_eyvcp"] [ext_resource type="PackedScene" uid="uid://bd06h3ufkhfd7" path="res://scenes/MetricsView/MetricsView.tscn" id="14"] [ext_resource type="Script" uid="uid://6tlvlmuarqub" path="res://scenes/NotificationsView/NotificationsView.gd" id="14_v0jrb"] [ext_resource type="Script" uid="uid://bb2nerxdrhped" path="res://test.gd" id="16_4rltv"] [ext_resource type="Script" uid="uid://d0vgtesnrato5" path="res://scripts/network.gd" id="17_ailes"] [ext_resource type="Script" uid="uid://k3lb65sounwm" path="res://test_manual_audio_output.gd" id="17_bg4s2"] [ext_resource type="PackedScene" uid="uid://bdlcslag0jdj1" path="res://scenes/UI/nat_type.tscn" id="18_vylep"] [ext_resource type="PackedScene" uid="uid://c37wtoikc8pgp" path="res://scenes/UI/ping.tscn" id="19_enhih"] [ext_resource type="PackedScene" uid="uid://8kmyoi08gr22" path="res://scenes/UI/players_score.tscn" id="20_i3cca"] [ext_resource type="Script" uid="uid://cgmpdyovn5vfm" path="res://scenes/UI/touch_screen_joystick.gd" id="21_5h21x"] [ext_resource type="Script" uid="uid://dygslh2f7uw40" path="res://scenes/UI/joysticks.gd" id="21_einjf"] [sub_resource type="StyleBoxEmpty" id="5"] [node name="Main" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 theme = ExtResource("1_dhxmx") script = ExtResource("3") _views_path = NodePath("MC/ViewManager") [node name="ColorRect" type="ColorRect" parent="."] layout_mode = 0 anchor_right = 1.0 anchor_bottom = 1.0 color = Color(0.121569, 0.121569, 0.121569, 1) [node name="MC" type="MarginContainer" parent="."] layout_mode = 0 anchor_right = 1.0 anchor_bottom = 1.0 theme_override_constants/margin_left = 8 theme_override_constants/margin_top = 8 theme_override_constants/margin_right = 8 theme_override_constants/margin_bottom = 8 [node name="ViewManager" type="VBoxContainer" parent="MC"] layout_mode = 2 script = ExtResource("3_j157n") [node name="HB" type="HFlowContainer" parent="MC/ViewManager"] layout_mode = 2 size_flags_horizontal = 3 [node name="AuthBtn" parent="MC/ViewManager/HB" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Auth" [node name="NeedsLoginBtns" type="HFlowContainer" parent="MC/ViewManager/HB"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 [node name="AchievementsBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Achievements" [node name="CustomInvitesBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Custom Invites" [node name="StatsBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Stats" [node name="LeaderboardsBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Leaderboards" [node name="FriendsBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Friends" [node name="UIBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "UI" [node name="MetricsBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Metrics" [node name="LobbiesBtn" parent="MC/ViewManager/HB/NeedsLoginBtns" instance=ExtResource("6")] unique_name_in_owner = true layout_mode = 2 text = "Lobbies" [node name="VSC" type="VSplitContainer" parent="MC/ViewManager"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 theme_override_constants/autohide = 0 [node name="VB" type="VBoxContainer" parent="MC/ViewManager/VSC"] layout_mode = 2 theme_override_constants/separation = 52 [node name="LoginView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("5")] custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="AchievementsView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("2")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="CustomInvitesView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("9")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="StatsView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("10")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="LeaderboardsView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("11")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="FriendsView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("12")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="UIView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("13")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="MetricsView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("14")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="LobbiesView" parent="MC/ViewManager/VSC/VB" instance=ExtResource("13_eyvcp")] visible = false custom_minimum_size = Vector2(0, 200) layout_mode = 2 [node name="NotificationsLayer" type="CanvasLayer" parent="MC/ViewManager/VSC/VB"] [node name="NotificationsView" type="MarginContainer" parent="MC/ViewManager/VSC/VB/NotificationsLayer"] offset_top = 270.0 offset_right = 366.0 offset_bottom = 600.0 mouse_filter = 2 script = ExtResource("14_v0jrb") [node name="SC" type="ScrollContainer" parent="MC/ViewManager/VSC/VB/NotificationsLayer/NotificationsView"] custom_minimum_size = Vector2(350, 0) layout_mode = 2 mouse_filter = 2 [node name="PC" type="PanelContainer" parent="MC/ViewManager/VSC/VB/NotificationsLayer/NotificationsView/SC"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 10 mouse_filter = 2 theme_override_styles/panel = SubResource("5") [node name="MC" type="MarginContainer" parent="MC/ViewManager/VSC/VB/NotificationsLayer/NotificationsView/SC/PC"] layout_mode = 2 mouse_filter = 2 theme_override_constants/margin_left = 4 theme_override_constants/margin_top = 8 theme_override_constants/margin_right = 4 theme_override_constants/margin_bottom = 8 [node name="VB" type="VBoxContainer" parent="MC/ViewManager/VSC/VB/NotificationsLayer/NotificationsView/SC/PC/MC"] layout_mode = 2 mouse_filter = 2 theme_override_constants/separation = 8 [node name="LogsView" parent="MC/ViewManager/VSC" instance=ExtResource("4")] layout_mode = 2 [node name="Test" type="Node" parent="."] script = ExtResource("16_4rltv") [node name="ManualAudioOutput" type="Node" parent="Test"] script = ExtResource("17_bg4s2") [node name="Network" type="Node2D" parent="."] script = ExtResource("17_ailes") [node name="Game" type="Node2D" parent="Network"] [node name="NetworkSpawn" type="Node2D" parent="Network/Game"] [node name="MapSpawner" type="MultiplayerSpawner" parent="Network/Game"] _spawnable_scenes = PackedStringArray("uid://d25abb5oqolnx", "uid://cq20ci5f675b8", "uid://cutu8g5suwurd") spawn_path = NodePath("../NetworkSpawn") [node name="PlayerSpawner" type="MultiplayerSpawner" parent="Network/Game"] _spawnable_scenes = PackedStringArray("uid://d25l4fa1sffqk") spawn_path = NodePath("../NetworkSpawn") [node name="CanvasLayer" type="CanvasLayer" parent="Network/Game"] visible = false [node name="NatType" parent="Network/Game/CanvasLayer" instance=ExtResource("18_vylep")] offset_left = -196.0 text = "NAT TYPE: Moderate" [node name="Ping" parent="Network/Game/CanvasLayer" instance=ExtResource("19_enhih")] anchors_preset = 3 anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 offset_left = -305.0 offset_top = -30.0 offset_right = -213.0 offset_bottom = -7.0 grow_horizontal = 0 grow_vertical = 0 [node name="PlayersScore" parent="Network/Game/CanvasLayer" instance=ExtResource("20_i3cca")] [node name="Joysticks" type="Control" parent="Network/Game/CanvasLayer"] visible = false layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("21_einjf") [node name="MoveJoystick" type="Control" parent="Network/Game/CanvasLayer/Joysticks"] anchors_preset = 0 offset_left = 181.0 offset_top = 390.0 offset_right = 221.0 offset_bottom = 430.0 pivot_offset = Vector2(20, 20) script = ExtResource("21_5h21x") knob_color = Color(0.560955, 0.560955, 0.560955, 1) base_color = Color(0.560784, 0.560784, 0.560784, 1) anti_aliased = true mode = 1 use_input_actions = true action_left = "move_left" action_right = "move_right" action_up = "move_up" action_down = "move_down" [node name="ShootJoystick" type="Control" parent="Network/Game/CanvasLayer/Joysticks"] anchors_preset = 0 offset_left = 938.0 offset_top = 401.0 offset_right = 978.0 offset_bottom = 441.0 pivot_offset = Vector2(20, 20) script = ExtResource("21_5h21x") knob_color = Color(0.560784, 0.560784, 0.560784, 1) base_color = Color(0.560784, 0.560784, 0.560784, 1) anti_aliased = true mode = 1 ================================================ FILE: sample/addons/epic-online-services-godot/base_class.gd ================================================ ## Copyright (c) 2022-present Delano Lourenco ## https://github.com/3ddelano/dataclasses-godot ## MIT License ## See License.md class_name BaseClass extends Dataclass func _init(p_name: String, p_options: Dictionary = {}): p_options["print_newline"] = true p_options["print_exclude"] = ["base_class.gd", "_log"] super._init(p_name, p_options) ================================================ FILE: sample/addons/epic-online-services-godot/base_class.gd.uid ================================================ uid://g1ij604ak57v ================================================ FILE: sample/addons/epic-online-services-godot/bin/.gitignore ================================================ * !.gitignore ================================================ FILE: sample/addons/epic-online-services-godot/dataclass.gd ================================================ ## Copyright (c) 2022-present Delano Lourenco ## https://github.com/3ddelano/dataclasses-godot ## MIT License ## See License.md class_name Dataclass extends RefCounted const __DEFAULT_OPTIONS__ = { # Whether to sort properties when printing "sort_keys": true, # Whether to include properties with null values in the to_dict() method "include_null_in_dict": true, # Whether to include properties with null values when printing "include_null_in_print": true, # Whether to print properties on newlines when printing "print_newline": false, # Names of properties to exclude when printing "print_exclude": [] } const __TYPE_STRING_CAPITAL = ["TYPE_NIL", "TYPE_BOOL", "TYPE_INT", "TYPE_FLOAT", "TYPE_STRING", "TYPE_VECTOR2", "TYPE_VECTOR2I", "TYPE_RECT2", "TYPE_RECT2I", "TYPE_VECTOR3", "TYPE_VECTOR3I", "TYPE_TRANSFORM2D", "TYPE_VECTOR4", "TYPE_VECTOR4I", "TYPE_PLANE", "TYPE_QUATERNION", "TYPE_AABB", "TYPE_BASIS", "TYPE_TRANSFORM3D", "TYPE_PROJECTION", "TYPE_COLOR", "TYPE_STRING_NAME", "TYPE_NODE_PATH", "TYPE_RID", "TYPE_OBJECT", "TYPE_CALLABLE", "TYPE_SIGNAL", "TYPE_DICTIONARY", "TYPE_ARRAY", "TYPE_PACKED_BYTE_ARRAY", "TYPE_PACKED_INT32_ARRAY", "TYPE_PACKED_INT64_ARRAY", "TYPE_PACKED_FLOAT32_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY", "TYPE_PACKED_STRING_ARRAY", "TYPE_PACKED_VECTOR2_ARRAY", "TYPE_PACKED_VECTOR3_ARRAY", "TYPE_PACKED_COLOR_ARRAY", "TYPE_MAX"] const __TYPE_STRING_NORMAL = ["Nil", "Bool", "Int", "Float", "String", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Vector4", "Vector4i", "Plane", "Quaternion", "AABB", "Basis", "Transform3D", "Projection", "Color", "StringName", "NodePath", "Rid", "Object", "Callable", "Signal", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray", "PackedVector2Array", "PackedVector3Array", "PackedColorArray", "Max"] func from_dict(p_dict: Dictionary): for key in p_dict: if not key in self: printerr("Dataclass:from_dict: Error: Key \"%s\" not found in dataclass \"%s\"" % [key, __name__]) continue # Check for types match var typeof_expected = typeof(get(key)) var typeof_received = typeof(p_dict[key]) if typeof_expected != TYPE_NIL and typeof_received != typeof_expected and ( (not typeof_expected in [TYPE_INT, TYPE_FLOAT]) and (not typeof_received in [TYPE_INT, TYPE_FLOAT]) ): printerr("Dataclass:from_dict: Warning: Key \"%s\" has type %s but expected type %s in dataclass \"%s\"" % [key, __type_to_string(typeof_received), __type_to_string(typeof_expected), __name__]) set(key, p_dict[key]) return self func to_dict() -> Dictionary: var ret = {} var all_props = __get_props() for prop in all_props: var value = get(prop.name) if value != null or __options__.include_null_in_dict: ret[prop.name] = value return ret var __name__: String var __options__: Dictionary func _init(p_name: String, p_options: Dictionary = {}): __name__ = p_name __options__ = __DEFAULT_OPTIONS__.duplicate() set_meta("is_dataclass", true) # Override options for key in p_options: __options__[key] = p_options[key] func get_class() -> String: return __name__ func __get_props() -> Array: var all_props = get_property_list() all_props = all_props.slice(3, all_props.size() - 4) if __options__.sort_keys: all_props.sort_custom(func sort_ascending(a, b): return a.name < b.name) return all_props func __type_to_string(p_type: int, p_capital = true) -> String: if p_capital: return __TYPE_STRING_CAPITAL[p_type] return __TYPE_STRING_NORMAL[p_type] func __variant_to_string(value, indent_level = 0, visited_objects = []) -> String: var type = typeof(value) var newline = "" var tab = "" var dict_join = ", " if __options__.print_newline: tab = "\t" newline = "\n" dict_join = ",\n" match type: TYPE_STRING: return "\"" + value.c_escape() + "\"" TYPE_NIL, TYPE_INT, TYPE_FLOAT, TYPE_BOOL: return str(value) TYPE_QUATERNION, TYPE_VECTOR2, TYPE_RECT2, TYPE_VECTOR3, TYPE_TRANSFORM2D, TYPE_BASIS: return "%s%s" % [__type_to_string(type, false), value] TYPE_RID: return "RID(%s)" % value.get_id() TYPE_OBJECT: if value == null: return "Object(null)" elif (not value is Script) and value.has_meta("is_dataclass") and value.get_meta("is_dataclass"): # Check for circular reference to prevent infinite recursion if value in visited_objects: return "%s()" % value.__name__ # Create new visited_objects array with current object added visited_objects.append(value) # Manually construct the string representation to avoid calling _to_string() var all_props = value.__get_props() var props = PackedStringArray() for prop in all_props: if prop.name in value.__options__.print_exclude: continue var prop_value = value.get(prop.name) var prop_type = typeof(prop_value) if prop_value != null or value.__options__.include_null_in_print: props.append("%s = %s" % [prop.name, __variant_to_string(prop_value, indent_level + 1, visited_objects)]) var separator = ", " var prop_newline = "" var prop_newline_end = "" if value.__options__.print_newline: prop_newline = "\n\t" prop_newline_end = "\n" var result = value.__name__ + "(" + prop_newline + (separator + prop_newline).join(props) + prop_newline_end + ")" return result.indent(tab.repeat(indent_level)).trim_prefix(tab.repeat(indent_level)) return str(value) TYPE_DICTIONARY: var keys = value.keys() if keys.size() == 0: return "{}" var dict_string = PackedStringArray() for key in keys: dict_string.append("%s: %s" % [key, __variant_to_string(value[key], 0, visited_objects)]) dict_string = "{" + newline + dict_join.join(dict_string).indent(tab) + newline + "}" return dict_string.indent(tab.repeat(indent_level)).trim_prefix(tab.repeat(indent_level)) TYPE_ARRAY, TYPE_PACKED_BYTE_ARRAY, TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_INT64_ARRAY, TYPE_PACKED_FLOAT32_ARRAY, TYPE_PACKED_FLOAT64_ARRAY, TYPE_PACKED_STRING_ARRAY, TYPE_PACKED_VECTOR2_ARRAY, TYPE_PACKED_VECTOR3_ARRAY, TYPE_PACKED_COLOR_ARRAY: var ret = PackedStringArray() for elm in value: ret.append(__variant_to_string(elm, 1, visited_objects)) ret = "[" + ", ".join(ret) + "]" if type == TYPE_ARRAY: return ret return __type_to_string(type, false) + "(" + ret + ")" TYPE_COLOR: return "%s%s" % [__type_to_string(type, false), value] _: return "%s(%s)" % [__type_to_string(type, false), value] func _to_string() -> String: var all_props = __get_props() var props = PackedStringArray() for prop in all_props: if prop.name in __options__.print_exclude: continue var value = get(prop.name) var type = typeof(value) if value != null or __options__.include_null_in_print: props.append("%s = %s" % [prop.name, __variant_to_string(value, 1, [self])]) var separator = ", " var newline = "" var newline_end = "" if __options__.print_newline: newline = "\n\t" newline_end = "\n" return __name__ + "(" + newline + (separator + newline).join(props) + newline_end + ")" ================================================ FILE: sample/addons/epic-online-services-godot/dataclass.gd.uid ================================================ uid://d4hmupfn3ooqp ================================================ FILE: sample/addons/epic-online-services-godot/eos.gd ================================================ # Copyright (c) 2023-present Delano Lourenco # https://github.com/3ddelano/epic-online-services-godot/ # MIT License ## The main class to interact with Epic Online Services class_name EOS extends RefCounted static func get_instance(): return IEOS ## Pretty prints the [enum Result] code and its string representation.[br] ## [code]p_result[/code] is a [enum Result] or a [Dictionary] with a [code]result_code[/code] key static func print_result(p_result) -> void: print_rich("[b]EOS_Result[/b]:%s[code](%s)[/code]" % [result_str(p_result), p_result]) ## Returns a string representation of the [enum Result] code.[br] ## [code]p_result[/code] is a [enum Result] or a [Dictionary] with a [code]result_code[/code] key static func result_str(p_result) -> String: if typeof(p_result) == TYPE_DICTIONARY: p_result = p_result["result_code"] var idx := Result.values().find(p_result) return Result.keys()[idx] ## Returns whether the operation was completed.[br] ## [code]p_result[/code] is a [enum Result] or a [Dictionary] with a [code]result_code[/code] key static func is_operation_complete(p_result) -> bool: if typeof(p_result) == TYPE_DICTIONARY: p_result = p_result["result_code"] return IEOS.is_operation_complete(p_result) ## Returns whether the operation was successful.[br] ## [code]p_result[/code] is a [enum Result] or a [Dictionary] with a [code]result_code[/code] key static func is_success(p_result) -> bool: if typeof(p_result) == TYPE_BOOL: return p_result == true if typeof(p_result) == TYPE_DICTIONARY: p_result = p_result["result_code"] return p_result == Result.Success class Achievements: const UNLOCK_TIME_UNDEFINED = -1 class CopyAchievementDefinitionV2ByAchievementIdOptions extends BaseClass: func _init(): super._init("CopyAchievementDefinitionV2ByAchievementIdOptions") var achievement_id: String class CopyAchievementDefinitionV2ByIndexOptions extends BaseClass: func _init(): super._init("CopyAchievementDefinitionV2ByIndexOptions") var achievement_index: int class CopyPlayerAchievementByAchievementIdOptions extends BaseClass: func _init(): super._init("CopyPlayerAchievementByAchievementIdOptions") var achievement_id: String var local_user_id = EOSGRuntime.local_product_user_id var target_user_id = EOSGRuntime.local_product_user_id class CopyPlayerAchievementByIndexOptions extends BaseClass: func _init(): super._init("CopyPlayerAchievementByIndexOptions") var achievement_index: int var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String class GetAchievementDefinitionCountOptions extends BaseClass: func _init(): super._init("GetAchievementDefinitionCountOptions") class QueryDefinitionsOptions extends BaseClass: func _init(): super._init("QueryDefinitionsOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class GetPlayerAchievementCountOptions extends BaseClass: func _init(): super._init("GetPlayerAchievementCountOptions") var user_id: String class QueryPlayerAchievementsOptions extends BaseClass: func _init(): super._init("QueryPlayerAchievementsOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id = EOSGRuntime.local_product_user_id var client_data = null class UnlockAchievementsOptions extends BaseClass: func _init(): super._init("UnlockAchievementsOptions") var user_id: String var achievement_ids: Array # Array[String] var client_data = null class AchievementsInterface: static func query_definitions(options: QueryDefinitionsOptions) -> void: IEOS.achievements_interface_query_definitions(options) static func get_achievement_definition_count(options: GetAchievementDefinitionCountOptions) -> int: return IEOS.achievements_interface_get_achievement_definition_count(options) static func copy_achievement_definition_v2_by_achievement_id( options: CopyAchievementDefinitionV2ByAchievementIdOptions ) -> Dictionary: return IEOS.achievements_interface_copy_achievement_definition_v2_by_achievement_id( options ) static func copy_achievement_definition_v2_by_index( options: CopyAchievementDefinitionV2ByIndexOptions ) -> Dictionary: return IEOS.achievements_interface_copy_achievement_definition_v2_by_index(options) static func query_player_achievements(options: QueryPlayerAchievementsOptions) -> void: IEOS.achievements_interface_query_player_achievements(options) static func get_player_achievement_count(options: GetPlayerAchievementCountOptions) -> int: return IEOS.achievements_interface_get_player_achievement_count(options) static func copy_player_achievement_by_achievement_id( options: CopyPlayerAchievementByAchievementIdOptions ) -> Dictionary: return IEOS.achievements_interface_copy_player_achievement_by_achievement_id(options) static func copy_player_achievement_by_index(options: CopyPlayerAchievementByIndexOptions) -> Dictionary: return IEOS.achievements_interface_copy_player_achievement_by_index(options) static func unlock_achievements(options: UnlockAchievementsOptions) -> void: IEOS.achievements_interface_unlock_achievements(options) class Connect: const CONNECT_TIME_UNDEFINED = -1 class Credentials extends BaseClass: func _init(): super._init("Credentials") var type: ExternalCredentialType = -1 var token = null class UserLoginInfo extends BaseClass: func _init(): super._init("UserLoginInfo") var display_name: String var nsa_id_token: String class LoginOptions extends BaseClass: func _init(): super._init("LoginOptions") var credentials: Credentials var user_login_info: UserLoginInfo var client_data = null class LogoutOptions extends BaseClass: func _init(): super._init("LogoutOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class CopyIdTokenOptions extends BaseClass: func _init(): super._init("CopyIdTokenOptions") var local_user_id = EOSGRuntime.local_product_user_id class CopyProductUserExternalAccountByAccountIdOptions extends BaseClass: func _init(): super._init("CopyProductUserExternalAccountByAccountIdOptions") var target_user_id = EOSGRuntime.local_product_user_id var account_id: String class CopyProductUserExternalAccountByAccountTypeOptions extends BaseClass: func _init(): super._init("CopyProductUserExternalAccountByAccountTypeOptions") var target_user_id = EOSGRuntime.local_product_user_id var account_id_type: ExternalAccountType class CopyProductUserExternalAccountByIndexOptions extends BaseClass: func _init(): super._init("CopyProductUserExternalAccountByIndexOptions") var target_user_id = EOSGRuntime.local_product_user_id var external_account_info_index: int class CopyProductUserInfoOptions extends BaseClass: func _init(): super._init("CopyProductUserInfoOptions") var target_user_id = EOSGRuntime.local_product_user_id class CreateDeviceIdOptions extends BaseClass: func _init(): super._init("CreateDeviceIdOptions") var device_model: String var client_data = null class DeleteDeviceIdOptions extends BaseClass: func _init(): super._init("DeleteDeviceIdOptions") var client_data = null class CreateUserOptions extends BaseClass: func _init(): super._init("CreateUserOptions") var continuance_token: EOSGContinuanceToken var client_data = null class QueryExternalAccountMappingsOptions extends BaseClass: func _init(): super._init("QueryExternalAccountMappingsOptions") var local_user_id = EOSGRuntime.local_product_user_id var account_id_type: ExternalAccountType var external_account_ids: Array # Array[String] class GetExternalAccountMappingsOptions extends BaseClass: func _init(): super._init("GetExternalAccountMappingsOptions") var account_id_type: ExternalAccountType var local_user_id = EOSGRuntime.local_product_user_id var target_external_user_id: String class GetProductUserExternalAccountCountOptions extends BaseClass: func _init(): super._init("GetProductUserExternalAccountCountOptions") var target_user_id = EOSGRuntime.local_product_user_id class QueryProductUserIdMappingsOptions extends BaseClass: func _init(): super._init("QueryProductUserIdMappingsOptions") var local_user_id = EOSGRuntime.local_product_user_id var product_user_ids: Array # Array[String] class GetProductUserIdMappingOptions extends BaseClass: func _init(): super._init("GetProductUserIdMappingOptions") var account_id_type: ExternalAccountType var local_user_id = EOSGRuntime.local_product_user_id var target_product_user_id = EOSGRuntime.local_product_user_id class LinkAccountOptions extends BaseClass: func _init(): super._init("LinkAccountOptions") var continuance_token = null # ContinuanceTokenWrapper var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class IdToken extends BaseClass: func _init(): super._init("IdToken") var product_user_id: String var json_web_token: String class VerifyIdTokenOptions extends BaseClass: func _init(): super._init("VerifyIdTokenOptions") var id_token: IdToken var client_data = null class TransferDeviceIdAccountOptions extends BaseClass: func _init(): super._init("TransferDeviceIdAccountOptions") var primary_local_user_id = EOSGRuntime.local_product_user_id var local_device_user_id = EOSGRuntime.local_product_user_id var product_user_id_to_preserve: String var client_data = null class UnlinkAccountOptions extends BaseClass: func _init(): super._init("UnlinkAccountOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class ConnectInterface: static func login(options: LoginOptions) -> void: IEOS.connect_interface_login(options) static func logout(options: LogoutOptions) -> void: IEOS.connect_interface_logout(options) static func copy_id_token(options: CopyIdTokenOptions) -> Dictionary: return IEOS.connect_interface_copy_id_token(options) static func copy_product_user_external_account_by_account_id(options: CopyProductUserExternalAccountByAccountIdOptions) -> Dictionary: return IEOS.connect_interface_copy_product_user_external_account_by_account_id(options) static func copy_product_user_external_account_by_account_type(options: CopyProductUserExternalAccountByAccountTypeOptions) -> Dictionary: return IEOS.connect_interface_copy_product_user_external_account_by_account_type(options) static func copy_product_user_external_account_by_index(options: CopyProductUserExternalAccountByIndexOptions) -> Dictionary: return IEOS.connect_interface_copy_product_user_external_account_by_index(options) static func copy_product_user_info(options: CopyProductUserInfoOptions) -> Dictionary: return IEOS.connect_interface_copy_product_user_info(options) static func create_device_id(options: CreateDeviceIdOptions) -> void: IEOS.connect_interface_create_device_id(options) static func create_user(options: CreateUserOptions) -> void: IEOS.connect_interface_create_user(options) static func delete_device_id(options: DeleteDeviceIdOptions) -> void: IEOS.connect_interface_delete_device_id(options) static func query_external_account_mappings(options: QueryExternalAccountMappingsOptions) -> void: IEOS.connect_interface_query_external_account_mappings(options) static func get_external_account_mapping(options: GetExternalAccountMappingsOptions) -> String: return IEOS.connect_interface_get_external_account_mapping(options) static func get_logged_in_user_by_index(index: int) -> String: return IEOS.connect_interface_get_logged_in_user_by_index(index) static func get_logged_in_users_count() -> int: return IEOS.connect_interface_get_logged_in_users_count() static func get_login_status(local_user_id := EOSGRuntime.local_product_user_id) -> LoginStatus: return IEOS.connect_interface_get_login_status(local_user_id) static func get_product_user_external_account_count(options: GetProductUserExternalAccountCountOptions = GetProductUserExternalAccountCountOptions.new()) -> int: return IEOS.connect_interface_get_product_user_external_account_count(options) static func query_product_user_id_mappings(options: QueryProductUserIdMappingsOptions) -> void: IEOS.connect_interface_query_product_user_id_mappings(options) static func get_product_user_id_mapping(options: GetProductUserIdMappingOptions) -> Dictionary: return IEOS.connect_interface_get_product_user_id_mapping(options) static func link_account(options: LinkAccountOptions) -> void: IEOS.connect_interface_link_account(options) static func verify_id_token(options: VerifyIdTokenOptions) -> void: IEOS.connect_interface_verify_id_token(options) static func transfer_device_id_account(options: TransferDeviceIdAccountOptions) -> void: IEOS.connect_interface_transfer_device_id_account(options) static func unlink_account(options: UnlinkAccountOptions) -> void: IEOS.connect_interface_unlink_account(options) class Auth: enum ScopeFlags { NoFlags = 0x0, BasicProfile = 0x1, FriendsList = 0x2, Presence = 0x4, FriendsManagement = 0x8, Email = 0x10, Country = 0x20, } enum AuthTokenType {Client = 0, User = 1} enum LinkAccountFlags {NoFlags = 0x0, NintendoNsaId = 0x1} enum LoginCredentialType { None = -1, Password = 0, ExchangeCode = 1, PersistentAuth = 2, DeviceCode = 3, Developer = 4, RefreshToken = 5, AccountPortal = 6, ExternalAuth = 7 } enum LoginFlags { None = 0, NoUserInterface = 1 } class Credentials extends BaseClass: func _init(): super._init("Credentials") var external_type: ExternalCredentialType = -1 var id: String var token: String var type: LoginCredentialType = -1 class LoginOptions extends BaseClass: func _init(): super._init("LoginOptions") var credentials: Credentials var login_flags: LoginFlags = LoginFlags.None var scope_flags: ScopeFlags = ScopeFlags.NoFlags var client_data = null class LogoutOptions extends BaseClass: func _init(): super._init("LogoutOptions") var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class CopyIdTokenOptions extends BaseClass: func _init(): super._init("CopyIdTokenOptions") var account_id = EOSGRuntime.local_epic_account_id class CopyUserAuthTokenOptions extends BaseClass: func _init(): super._init("CopyUserAuthTokenOptions") class DeletePersistentAuthOptions extends BaseClass: func _init(): super._init("DeletePersistentAuthOptions") var refresh_token = null var client_data = null class LinkAccountOptions extends BaseClass: func _init(): super._init("LinkAccountOptions") var continuance_token: EOSGContinuanceToken var link_account_flags: LinkAccountFlags = LinkAccountFlags.NoFlags var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class QueryIdTokenOptions extends BaseClass: func _init(): super._init("QueryIdTokenOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_account_id = EOSGRuntime.local_epic_account_id var client_data = null class IdToken extends BaseClass: func _init(): super._init("IdToken") var account_id: String var json_web_token: String class VerifyIdTokenOptions extends BaseClass: func _init(): super._init("VerifyIdTokenOptions") var id_token: IdToken var client_data = null class Token extends BaseClass: func _init(): super._init("Token") var access_token: String var account_id: String var app: String var auth_type: int var client_id: String var expires_at: String var expires_in: float var refresh_expires_at: String var refresh_expires_in: float var refresh_token: String class VerifyUserAuthOptions extends BaseClass: func _init(): super._init("VerifyUserAuthOptions") var auth_token: Token var client_data = null class AuthInterface: static func login(options: LoginOptions) -> void: IEOS.auth_interface_login(options) static func logout(options: LogoutOptions) -> void: IEOS.auth_interface_logout(options) static func copy_id_token(options: CopyIdTokenOptions) -> Dictionary: return IEOS.auth_interface_copy_id_token(options) static func copy_user_auth_token(options: CopyUserAuthTokenOptions, local_user_id := EOSGRuntime.local_epic_account_id) -> Dictionary: var func_result: Dictionary = IEOS.auth_interface_copy_user_auth_token( options, local_user_id ) var token: Token = Token.new() if func_result.token: var token_dict = func_result.token token.access_token = token_dict.access_token token.account_id = token_dict.account_id token.app = token_dict.app token.auth_type = token_dict.auth_type token.client_id = token_dict.client_id token.expires_at = token_dict.expires_at token.expires_in = token_dict.expires_in token.refresh_expires_at = token_dict.refresh_expires_at token.refresh_expires_in = token_dict.refresh_expires_in token.refresh_token = token_dict.refresh_token return {result_code = func_result.result_code, token = token} static func delete_persistent_auth(options: DeletePersistentAuthOptions) -> void: IEOS.auth_interface_delete_persistent_auth(options) static func get_logged_in_account_by_index(index: int) -> String: return IEOS.auth_interface_get_logged_in_account_by_index(index) static func get_logged_in_accounts_count() -> int: return IEOS.auth_interface_get_logged_in_accounts_count() static func get_login_status(local_user_id := EOSGRuntime.local_epic_account_id) -> LoginStatus: return IEOS.auth_interface_get_login_status(local_user_id) static func get_merged_account_by_index(local_user_id: String, index: int) -> String: return IEOS.auth_interface_get_merged_account_by_index(local_user_id, index) static func get_merged_accounts_count(local_user_id := EOSGRuntime.local_epic_account_id) -> int: return IEOS.auth_interface_get_merged_accounts_count(local_user_id) static func get_selected_account_id(local_user_id := EOSGRuntime.local_epic_account_id) -> Dictionary: return IEOS.auth_interface_get_selected_account_id(local_user_id) static func link_account(options: LinkAccountOptions) -> void: IEOS.auth_interface_link_account(options) static func query_id_token(options: QueryIdTokenOptions) -> void: IEOS.auth_interface_query_id_token(options) static func verify_id_token(options: VerifyIdTokenOptions) -> void: IEOS.auth_interface_verify_id_token(options) static func verify_user_auth(options: VerifyUserAuthOptions) -> void: IEOS.auth_interface_verify_user_auth(options) class CustomInvites: enum ResquestToJoinResponse { Accepted = 0, Rejected = 1, } class SetCustomInviteOptions extends BaseClass: func _init(): super._init("SetCustomInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var payload: String class SendCustomInviteOptions extends BaseClass: func _init(): super._init("SendCustomInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_ids: Array # Array[String] var client_data = null class FinalizeInviteOptions extends BaseClass: func _init(): super._init("FinalizeInviteOptions") var custom_invite_id: String var local_user_id = EOSGRuntime.local_product_user_id var processing_result: Result var target_user_id: String class SendRequestToJoinOptions extends BaseClass: func _init(): super._init("SendRequestToJoinOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var client_data = null class AcceptRequestToJoinOptions extends BaseClass: func _init(): super._init("AcceptRequestToJoinOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var client_data = null class RejectRequestToJoinOptions extends BaseClass: func _init(): super._init("RejectRequestToJoinOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var client_data = null class CustomInvitesInterface: static func set_custom_invite(options: SetCustomInviteOptions) -> Result: return IEOS.custom_invites_interface_set_custom_invite(options) static func send_custom_invite(options: SendCustomInviteOptions) -> void: IEOS.custom_invites_interface_send_custom_invite(options) static func finalize_invite(options: FinalizeInviteOptions) -> Result: return IEOS.custom_invites_interface_finalize_invite(options) static func send_request_to_join(options: SendRequestToJoinOptions) -> void: IEOS.custom_invites_interface_send_request_to_join(options) static func accept_request_to_join(options: SendRequestToJoinOptions) -> void: IEOS.custom_invites_interface_accept_request_to_join(options) static func reject_request_to_join(options: SendRequestToJoinOptions) -> void: IEOS.custom_invites_interface_reject_request_to_join(options) class Stats: const STATS_TIME_UNDEFINED = -1 class CopyStatByIndexOptions extends BaseClass: func _init(): super._init("CopyStatByIndexOptions") var target_user_id = EOSGRuntime.local_product_user_id var stat_index: int class CopyStatByNameOptions extends BaseClass: func _init(): super._init("CopyStatByNameOptions") var target_user_id = EOSGRuntime.local_product_user_id var name: String class GetStatsCountOptions extends BaseClass: func _init(): super._init("GetStatsCountOptions") var target_user_id = EOSGRuntime.local_product_user_id class IngestStatOptions extends BaseClass: func _init(): super._init("IngestStatOptions") var local_user_id = EOSGRuntime.local_product_user_id var stats: Array # Array[Dictionary] {stat_name: String, ingest_amount: int} var target_user_id = EOSGRuntime.local_product_user_id var client_data = null class QueryStatsOptions extends BaseClass: func _init(): super._init("QueryStatsOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id = EOSGRuntime.local_product_user_id var stat_names: Array # Array[String] var start_time = STATS_TIME_UNDEFINED var end_time = STATS_TIME_UNDEFINED var client_data = null class StatsInterface: static func copy_stat_by_index(options: CopyStatByIndexOptions) -> Dictionary: return IEOS.stats_interface_copy_stat_by_index(options) static func copy_stat_by_name(options: CopyStatByNameOptions) -> Dictionary: return IEOS.stats_interface_copy_stat_by_name(options) static func get_stats_count(options: GetStatsCountOptions) -> int: return IEOS.stats_interface_get_stats_count(options) static func ingest_stat(options: IngestStatOptions) -> void: IEOS.stats_interface_ingest_stat(options) static func query_stats(options: QueryStatsOptions) -> void: IEOS.stats_interface_query_stats(options) class Platform: enum PlatformFlags { None = 0x0, LoadingInEditor = 0x00001, DisableOverlay = 0x00002, DisableSocialOverlay = 0x00004, Reserved1 = 0x00008, WindowsEnableOverlayD3D9 = 0x00010, WindowsEnableOverlayD3D10 = 0x00020, WindowsEnableOverlayOpengl = 0x00040, ConsoleEnableOverlayAutomaticUnloading = 0x00080, } enum ApplicationStatus { BackgroundConstrained = 0, BackgroundUnconstrained = 1, BackgroundSuspended = 2, Foreground = 3 } enum NetworkStatus { Disabled = 0, Offline = 1, Online = 2 } enum DesktopCrossplayStatus { OK = 0, ApplicationNotBootstrapped = 1, ServiceNotInstalled = 2, ServiceStartFailed = 3, ServiceNotRunning = 4, OverlayDisabled = 5, OverlayNotInstalled = 6, OverlayTrustCheckFailed = 7, OverlayLoadFailed = 8 } enum RTCBackgroundMode { LeaveRooms = 0, KeepRoomsAlive = 1, } class InitializeOptions extends BaseClass: func _init(): super._init("InitializeOptions") var product_name: String var product_version: String class RTCOptions extends BaseClass: func _init(): super._init("RTCOptions") var background_mode = null ## See [enum EOS.Platform.RTCBackgroundMode] class CreateOptions extends BaseClass: func _init(): super._init("CreateOptions") var client_id: String var client_secret: String var deployment_id: String var encryption_key: String var product_id: String var sandbox_id: String var cache_directory = ProjectSettings.globalize_path("user://eosg-cache") var flags: int = 0 ## See [enum EOS.Platform.PlatformFlags] var is_server: bool var override_country_code: String var override_locale_code: String var tick_budget_in_milliseconds: int var task_network_timeout_seconds = null # float var rtc_options := RTCOptions.new() class PlatformInterface: static func create(options: CreateOptions) -> bool: return IEOS.platform_interface_create(options) static func get_active_country_code(user_id: String) -> Dictionary: return IEOS.platform_interface_get_active_country_code(user_id) static func get_active_locale_code(user_id: String) -> Dictionary: return IEOS.platform_interface_get_active_locale_code(user_id) static func get_override_country_code() -> Dictionary: return IEOS.platform_interface_get_override_country_code() static func get_override_locale_code() -> Dictionary: return IEOS.platform_interface_get_override_locale_code() static func set_override_country_code(country_code: String) -> Result: return IEOS.platform_interface_set_override_country_code(country_code) static func set_override_locale_code(locale_code: String) -> Result: return IEOS.platform_interface_set_override_locale_code(locale_code) static func check_for_launcher_and_restart() -> Result: return IEOS.platform_interface_check_for_launcher_and_restart() static func initialize(options: InitializeOptions) -> Result: return IEOS.platform_interface_initialize(options) static func get_desktop_crossplay_status_info() -> Dictionary: return IEOS.platform_interface_get_desktop_crossplay_status_info() static func set_application_status(status: ApplicationStatus) -> Result: return IEOS.platform_interface_set_application_status(status) static func get_application_status() -> ApplicationStatus: return IEOS.platform_interface_get_application_status() static func set_network_status(status: NetworkStatus) -> Result: return IEOS.platform_interface_set_network_status(status) static func get_network_status() -> NetworkStatus: return IEOS.platform_interface_get_network_status() static func release() -> void: IEOS.platform_interface_release() static func shutdown() -> Result: return IEOS.platform_interface_shutdown() class Ecom: enum ItemType { ## This entitlement is intended to persist. Durable = 0, ## This entitlement is intended to be transient and redeemed. Consumable = 1, ## This entitlement has a type that is not currently intended for an in-game store. Other = 2 } enum OwnershipStatus { ## The catalog item is not owned by the local user NotOwned = 0, ## The catalog item is owned by the local user Owned = 1 } enum CheckoutOrientation { ## Current orientation will be used Default = 0, ## Portrait orientation Portrait = 1, ## Landscape orientation Landscape = 2 } class CheckoutOptions extends BaseClass: func _init(): super._init("CheckoutOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entries: Array # Array[Dictionary] {offer_id: String} var override_catalog_namespace: String var preferred_orientation = CheckoutOrientation.Default class CopyEntitlementByIdOptions extends BaseClass: func _init(): super._init("CopyEntitlementByIdOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_id: String class CopyEntitlementByIndexOptions extends BaseClass: func _init(): super._init("CopyEntitlementByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_index: int class CopyEntitlementByNameAndIndexOptions extends BaseClass: func _init(): super._init("CopyEntitlementByNameAndIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_name: String var index: int class CopyItemByIdOptions extends BaseClass: func _init(): super._init("CopyItemByIdOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String class CopyItemImageInfoByIndexOptions extends BaseClass: func _init(): super._init("CopyItemImageInfoByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String var image_info_index: int class CopyItemReleaseByIndexOptions extends BaseClass: func _init(): super._init("CopyItemReleaseByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String var release_index: int class CopyOfferByIdOptions extends BaseClass: func _init(): super._init("CopyOfferByIdOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_id: String class CopyOfferByIndexOptions extends BaseClass: func _init(): super._init("CopyOfferByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_index: int class CopyOfferImageInfoByIndexOptions extends BaseClass: func _init(): super._init("CopyOfferImageInfoByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_id: String var image_info_index: int class CopyOfferItemByIndexOptions extends BaseClass: func _init(): super._init("CopyOfferItemByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_id: String var item_index: int class CopyTransactionByIdOptions extends BaseClass: func _init(): super._init("CopyTransactionByIdOptions") var local_user_id = EOSGRuntime.local_epic_account_id var transaction_id: String class CopyTransactionByIndexOptions extends BaseClass: func _init(): super._init("CopyTransactionByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var transaction_index: int class GetEntitlementsByNameCountOptions extends BaseClass: func _init(): super._init("GetEntitlementsByNameCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_name: String class GetEntitlementsCountOptions extends BaseClass: func _init(): super._init("GetEntitlementsCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String class GetItemImageInfoCountOptions extends BaseClass: func _init(): super._init("GetItemImageInfoCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String class GetItemReleaseCountOptions extends BaseClass: func _init(): super._init("GetItemReleaseCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var item_id: String class GetOfferCountOptions extends BaseClass: func _init(): super._init("GetOfferCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id class GetOfferImageInfoCountOptions extends BaseClass: func _init(): super._init("GetOfferImageInfoCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_id: String class GetOfferItemCountOptions extends BaseClass: func _init(): super._init("GetOfferItemCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var offer_id: String class GetTransactionCountOptions extends BaseClass: func _init(): super._init("GetTransactionCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id class QueryEntitlementsOptions extends BaseClass: func _init(): super._init("QueryEntitlementsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_names: Array # Array[String] var include_redeemed: bool var override_catalog_namespace: String class QueryOffersOptions extends BaseClass: func _init(): super._init("QueryOffersOptions") var local_user_id = EOSGRuntime.local_epic_account_id var override_catalog_namespace: String var client_data = null class QueryOwnershipOptions extends BaseClass: func _init(): super._init("QueryOwnershipOptions") var local_user_id = EOSGRuntime.local_epic_account_id var catalog_item_ids: Array # Array[String] var catalog_namespace: String var client_data = null class QueryOwnershipTokenOptions extends BaseClass: func _init(): super._init("QueryOwnershipTokenOptions") var local_user_id = EOSGRuntime.local_epic_account_id var catalog_item_ids: Array # Array[String] var catalog_namespace = null # String var client_data = null class RedeemEntitlementsOptions extends BaseClass: func _init(): super._init("RedeemEntitlementsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var entitlement_ids: Array # Array[String] var client_data = null class GetLastRedeemedEntitlementsCountOptions extends BaseClass: func _init(): super._init("GetLastRedeemedEntitlementsCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id class CopyLastRedeemedEntitlementByIndexOptions extends BaseClass: func _init(): super._init("CopyLastRedeemedEntitlementByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var redeemed_entitlement_index: String class EcomInterface: static func checkout(options: CheckoutOptions) -> void: IEOS.ecom_interface_checkout(options) static func copy_entitlement_by_id(options: CopyEntitlementByIdOptions) -> Dictionary: return IEOS.ecom_interface_copy_entitlement_by_id(options) static func copy_entitlement_by_index(options: CopyEntitlementByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_entitlement_by_index(options) static func copy_entitlement_by_name_and_index(options: CopyEntitlementByNameAndIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_entitlement_by_name_and_index(options) static func copy_item_by_id(options: CopyItemByIdOptions) -> Dictionary: return IEOS.ecom_interface_copy_item_by_id(options) static func copy_item_image_info_by_index(options: CopyItemImageInfoByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_item_image_info_by_index(options) static func copy_item_release_by_index(options: CopyItemReleaseByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_item_release_by_index(options) static func copy_offer_by_id(options: CopyOfferByIdOptions) -> Dictionary: return IEOS.ecom_interface_copy_offer_by_id(options) static func copy_offer_by_index(options: CopyOfferByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_offer_by_index(options) static func copy_offer_image_info_by_index(options: CopyOfferImageInfoByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_offer_image_info_by_index(options) static func copy_offer_item_by_index(options: CopyOfferItemByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_offer_item_by_index(options) static func copy_transaction_by_id(options: CopyTransactionByIdOptions) -> Dictionary: return IEOS.ecom_interface_copy_transaction_by_id(options) static func copy_transaction_by_index(options: CopyTransactionByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_transaction_by_index(options) static func get_entitlements_by_name_count(options: GetEntitlementsByNameCountOptions) -> int: return IEOS.ecom_interface_get_entitlements_by_name_count(options) static func get_entitlements_count(options: GetEntitlementsCountOptions) -> int: return IEOS.ecom_interface_get_entitlements_count(options) static func get_item_image_info_count(options: GetItemImageInfoCountOptions) -> int: return IEOS.ecom_interface_get_item_image_info_count(options) static func get_item_release_count(options: GetItemReleaseCountOptions) -> int: return IEOS.ecom_interface_get_item_release_count(options) static func get_offer_count(options: GetOfferCountOptions) -> int: return IEOS.ecom_interface_get_offer_count(options) static func get_offer_image_info_count(options: GetOfferImageInfoCountOptions) -> int: return IEOS.ecom_interface_get_offer_image_info_count(options) static func get_offer_item_count(options: GetOfferItemCountOptions) -> int: return IEOS.ecom_interface_get_offer_item_count(options) static func get_transaction_count(options: GetTransactionCountOptions) -> int: return IEOS.ecom_interface_get_transaction_count(options) static func query_entitlements(options: QueryEntitlementsOptions) -> void: IEOS.ecom_interface_query_entitlements(options) static func query_offers(options: QueryOffersOptions) -> void: IEOS.ecom_interface_query_offers(options) static func query_ownership(options: QueryOwnershipOptions) -> void: IEOS.ecom_interface_query_ownership(options) static func query_ownership_token(options: QueryOwnershipTokenOptions) -> void: IEOS.ecom_interface_query_ownership_token(options) static func redeem_entitlements(options: RedeemEntitlementsOptions) -> void: IEOS.ecom_interface_redeem_entitlements(options) static func get_last_redeemed_entitlements_count(options: GetLastRedeemedEntitlementsCountOptions) -> int: return IEOS.ecom_interface_get_last_redeemed_entitlements_count(options) static func copy_last_redeemed_entitlement_by_index(options: CopyLastRedeemedEntitlementByIndexOptions) -> Dictionary: return IEOS.ecom_interface_copy_last_redeemed_entitlement_by_index(options) class Friends: enum FriendsStatus {NotFriends = 0, InviteSent = 1, InviteReceived = 2, Friends = 3} class AcceptInviteOptions extends BaseClass: func _init(): super._init("AcceptInviteOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var client_data = null class GetFriendAtIndexOptions extends BaseClass: func _init(): super._init("GetFriendAtIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var index: int class GetFriendsCountOptions extends BaseClass: func _init(): super._init("GetFriendsCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id class GetStatusOptions extends BaseClass: func _init(): super._init("GetStatusOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class QueryFriendsOptions extends BaseClass: func _init(): super._init("QueryFriendsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class RejectInviteOptions extends BaseClass: func _init(): super._init("RejectInviteOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var client_data = null class SendInviteOptions extends BaseClass: func _init(): super._init("SendInviteOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var client_data = null class FriendsInterface: static func accept_invite(options: AcceptInviteOptions) -> void: IEOS.friends_interface_accept_invite(options) static func get_friend_at_index(options: GetFriendAtIndexOptions) -> String: return IEOS.friends_interface_get_friend_at_index(options) static func get_friends_count(options: GetFriendsCountOptions) -> int: return IEOS.friends_interface_get_friends_count(options) static func get_status(options: GetStatusOptions) -> EOS.Friends.FriendsStatus: return IEOS.friends_interface_get_status(options) static func query_friends(options: QueryFriendsOptions) -> void: IEOS.friends_interface_query_friends(options) static func reject_invite(options: RejectInviteOptions) -> void: IEOS.friends_interface_reject_invite(options) static func send_invite(options: SendInviteOptions) -> void: IEOS.friends_interface_send_invite(options) class KWS: enum KWSPermissionStatus {Granted = 0, Rejected = 1, Pending = 2} class CopyPermissionByIndexOptions extends BaseClass: func _init(): super._init("CopyPermissionByIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var index: int class CreateUserOptions extends BaseClass: func _init(): super._init("CreateUserOptions") var local_user_id = EOSGRuntime.local_product_user_id ## Date of birth in ISO8601 form (YYYY-MM-DD) var date_of_birth: String var parent_email: String class GetPermissionByKeyOptions extends BaseClass: func _init(): super._init("GetPermissionByKeyOptions") var local_user_id = EOSGRuntime.local_product_user_id var key: String class GetPermissionsCountOptions extends BaseClass: func _init(): super._init("GetPermissionsCountOptions") var local_user_id = EOSGRuntime.local_product_user_id class QueryAgeGateOptions extends BaseClass: func _init(): super._init("QueryAgeGateOptions") class QueryPermissionsOptions extends BaseClass: func _init(): super._init("QueryPermissionsOptions") var local_user_id = EOSGRuntime.local_product_user_id class RequestPermissionsOptions extends BaseClass: func _init(): super._init("RequestPermissionsOptions") var local_user_id = EOSGRuntime.local_product_user_id var permission_keys: Array # Array[String] class UpdateParentEmailOptions extends BaseClass: func _init(): super._init("UpdateParentEmailOptions") var local_user_id = EOSGRuntime.local_product_user_id var parent_email: String class KWSInterface: static func copy_permission_by_index(options: CopyPermissionByIndexOptions) -> Dictionary: return IEOS.kws_interface_copy_permission_by_index(options) static func create_user(options: CreateUserOptions) -> void: IEOS.kws_interface_create_user(options) static func get_permission_by_key(options: GetPermissionByKeyOptions) -> Dictionary: return IEOS.kws_interface_get_permission_by_key(options) static func get_permissions_count(options: GetPermissionsCountOptions) -> int: return IEOS.kws_interface_get_permissions_count(options) static func query_age_gate(options: QueryAgeGateOptions) -> void: IEOS.kws_interface_query_age_gate(options) static func query_permissions(options: QueryPermissionsOptions) -> void: IEOS.kws_interface_query_permissions(options) static func request_permissions(options: RequestPermissionsOptions) -> void: IEOS.kws_interface_request_permissions(options) static func update_parent_email(options: UpdateParentEmailOptions) -> void: IEOS.kws_interface_update_parent_email(options) class Leaderboards: const LEADERBOARD_TIME_UNDEFINED = -1 enum LeaderboardAggregation {Min = 0, Max = 1, Sum = 2, Latest = 3} class CopyLeaderboardDefinitionByIndexOptions extends BaseClass: func _init(): super._init("CopyLeaderboardDefinitionByIndexOptions") var leaderboard_index: int class CopyLeaderboardDefinitionByLeaderboardId extends BaseClass: func _init(): super._init("CopyLeaderboardDefinitionByLeaderboardId") var leaderboard_id: String class CopyLeaderboardRecordByIndexOptions extends BaseClass: func _init(): super._init("CopyLeaderboardRecordByIndexOptions") var leaderboard_record_index: int class CopyLeaderboardRecordByUserIdOptions extends BaseClass: func _init(): super._init("CopyLeaderboardRecordByUserIdOptions") var user_id: int class CopyLeaderboardUserScoreByIndexOptions extends BaseClass: func _init(): super._init("CopyLeaderboardUserScoreByIndexOptions") var leaderboard_user_score_index: int var stat_name: String class CopyLeaderboardUserScoreByUserIdOptions extends BaseClass: func _init(): super._init("CopyLeaderboardUserScoreByUserIdOptions") var user_id: String var stat_name: String class GetLeaderboardDefinitionCountOptions extends BaseClass: func _init(): super._init("GetLeaderboardDefinitionCountOptions") class GetLeaderboardRecordCountOptions extends BaseClass: func _init(): super._init("GetLeaderboardRecordCountOptions") class GetLeaderboardUserScoreCountOptions extends BaseClass: func _init(): super._init("GetLeaderboardUserScoreCountOptions") var stat_name: String class QueryLeaderboardDefinitionsOptions extends BaseClass: func _init(): super._init("QueryLeaderboardDefinitionsOptions") var local_user_id = EOSGRuntime.local_product_user_id var start_time = LEADERBOARD_TIME_UNDEFINED var end_time = LEADERBOARD_TIME_UNDEFINED var client_data = null class QueryLeaderboardRanksOptions extends BaseClass: func _init(): super._init("QueryLeaderboardRanksOptions") var local_user_id = EOSGRuntime.local_product_user_id var leaderboard_id: String var client_data = null class QueryLeaderboardUserScoresOptions extends BaseClass: func _init(): super._init("QueryLeaderboardUserScoresOptions") var local_user_id = EOSGRuntime.local_product_user_id var user_ids: Array # Array[String] var stat_info: Array[Dictionary] # [{stat_name: String, aggregation: LeaderboardAggregation}] var start_time = null # String var end_time = null # String var client_data = null class LeaderboardsInterface: static func copy_leaderboard_definition_by_index(options: CopyLeaderboardDefinitionByIndexOptions) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_definition_by_index(options) static func copy_leaderboard_definition_by_leaderboard_id(options: CopyLeaderboardDefinitionByLeaderboardId) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_definition_by_leaderboard_id(options) static func copy_leaderboard_record_by_index(options: CopyLeaderboardRecordByIndexOptions) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_record_by_index(options) static func copy_leaderboard_record_by_user_id(options: CopyLeaderboardRecordByUserIdOptions) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_record_by_user_id(options) static func copy_leaderboard_user_score_by_index(options: CopyLeaderboardUserScoreByIndexOptions) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_user_score_by_index(options) static func copy_leaderboard_user_score_by_user_id(options: CopyLeaderboardUserScoreByUserIdOptions) -> Dictionary: return IEOS.leaderboards_interface_copy_leaderboard_user_score_by_user_id(options) static func get_leaderboard_definition_count(options: GetLeaderboardDefinitionCountOptions) -> int: return IEOS.leaderboards_interface_get_leaderboard_definition_count(options) static func get_leaderboard_record_count(options: GetLeaderboardRecordCountOptions) -> int: return IEOS.leaderboards_interface_get_leaderboard_record_count(options) static func get_leaderboard_user_score_count(options: GetLeaderboardUserScoreCountOptions) -> int: return IEOS.leaderboards_interface_get_leaderboard_user_score_count(options) static func query_leaderboard_definitions(options: QueryLeaderboardDefinitionsOptions) -> void: IEOS.leaderboards_interface_query_leaderboard_definitions(options) static func query_leaderboard_ranks(options: QueryLeaderboardRanksOptions) -> void: IEOS.leaderboards_interface_query_leaderboard_ranks(options) static func query_leaderboard_user_scores(options: QueryLeaderboardUserScoresOptions) -> void: IEOS.leaderboards_interface_query_leaderboard_user_scores(options) class Lobby: enum LobbyAttributeVisibility { Public = 0, Private = 1} enum LobbyMemberStatus { Joined = 0, Left = 1, Disconnected = 2, Kicked = 3, Promoted = 4, Closed = 5 } enum LobbyPermissionLevel { PublicAdvertised = 0, JoinViaPresence = 1, InviteOnly = 2} enum LobbyRTCRoomJoinActionType { ## Join RTC Room as soon as user joins the lobby AutomaticJoin = 0, ## Do not join RTC Room when joining the lobby. User must manually call Join RTC Room ManualJoin = 1 } const SEARCH_BUCKET_ID = "bucket" const SEARCH_MINCURRENT_MEMBERS = "mincurrentmembers" const SEARCH_MINSLOTSAVAILABLE = "minslotsavailable" class CreateLobbyOptions extends BaseClass: func _init(): super._init("CreateLobbyOptions") var bucket_id: String var lobby_id: String var disable_host_migration: bool var max_lobby_members: int var rejoin_after_kick_requires_invite: bool var crossplay_opt_out: bool var enable_rtc_room: bool var allow_invites := true var enable_join_by_id := true var local_user_id := EOSGRuntime.local_product_user_id var permission_level: LobbyPermissionLevel = LobbyPermissionLevel.PublicAdvertised var presence_enabled := true ## (Optional) Allows the local application to set local audio options for the RTC Room if it is enabled. Set this to a [Dictionary] to override the defaults.[br] ## A [Dictionary] with keys: [br] ## - flags: A bitwise-or union of [enum EOS.RTC.JoinRoomFlags],[br] ## - use_manual_audio_input: [bool],[br] ## - use_manual_audio_output: [bool],[br] ## - local_audio_device_input_starts_muted: [bool] var local_rtc_options = null var rtc_room_join_action_type = LobbyRTCRoomJoinActionType.AutomaticJoin var client_data = null class DestroyLobbyOptions extends BaseClass: func _init(): super._init("DestroyLobbyOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String var client_data = null class JoinLobbyOptions extends BaseClass: func _init(): super._init("JoinLobbyOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_details: EOSGLobbyDetails var presence_enabled: bool ## (Optional) Allows the local application to set local audio options for the RTC Room if it is enabled. Set this to a [Dictionary] to override the defaults.[br] ## A [Dictionary] with keys: [br] ## - flags: A bitwise-or union of [enum EOS.RTC.JoinRoomFlags],[br] ## - use_manual_audio_input: [bool],[br] ## - use_manual_audio_output: [bool],[br] ## - local_audio_device_input_starts_muted: [bool] var local_rtc_options = null var rtc_room_join_action_type = LobbyRTCRoomJoinActionType.AutomaticJoin var client_data = null class JoinLobbyByIdOptions extends BaseClass: func _init(): super._init("JoinLobbyByIdOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String var presence_enabled: bool ## (Optional) Allows the local application to set local audio options for the RTC Room if it is enabled. Set this to a [Dictionary] to override the defaults.[br] ## A [Dictionary] with keys: [br] ## - flags: A bitwise-or union of [enum EOS.RTC.JoinRoomFlags],[br] ## - use_manual_audio_input: [bool],[br] ## - use_manual_audio_output: [bool],[br] ## - local_audio_device_input_starts_muted: [bool] var local_rtc_options = null var rtc_room_join_action_type = LobbyRTCRoomJoinActionType.AutomaticJoin var client_data = null class LeaveLobbyOptions extends BaseClass: func _init(): super._init("LeaveLobbyOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String var client_data = null class UpdateLobbyModificationOptions extends BaseClass: func _init(): super._init("UpdateLobbyModificationOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String var client_data = null class UpdateLobbyOptions extends BaseClass: func _init(): super._init("UpdateLobbyOptions") var lobby_modification: EOSGLobbyModification var client_data = null class PromoteMemberOptions extends BaseClass: func _init(): super._init("PromoteMemberOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var lobby_id: String var client_data = null class KickMemberOptions extends BaseClass: func _init(): super._init("KickMemberOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var lobby_id: String var client_data = null class HardMuteMemberOptions extends BaseClass: func _init(): super._init("HardMuteMemberOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var lobby_id: String var hard_mute: bool var client_data = null class SendInviteOptions extends BaseClass: func _init(): super._init("SendInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id: String var lobby_id: String var client_data = null class RejectInviteOptions extends BaseClass: func _init(): super._init("RejectInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var invite_id: String var client_data = null class QueryInvitesOptions extends BaseClass: func _init(): super._init("QueryInvitesOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class GetInviteCountOptions extends BaseClass: func _init(): super._init("GetInviteCountOptions") var local_user_id = EOSGRuntime.local_product_user_id class GetInviteIdByIndexOptions extends BaseClass: func _init(): super._init("GetInviteIdByIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var index: int class CreateLobbySearchOptions extends BaseClass: func _init(): super._init("CreateLobbySearchOptions") var max_results := 10 class CopyLobbyDetailsByInviteIdOptions extends BaseClass: func _init(): super._init("CopyLobbyDetailsByInviteIdOptions") var invite_id: String class CopyLobbyDetailsByUiEventIdOptions extends BaseClass: func _init(): super._init("CopyLobbyDetailsByUiEventIdOptions") var ui_event_id: String class CopyLobbyDetailsOptions extends BaseClass: func _init(): super._init("CopyLobbyDetailsOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String class GetRtcRoomNameOptions extends BaseClass: func _init(): super._init("GetRtcRoomNameOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String class IsRtcRoomConnectedOptions extends BaseClass: func _init(): super._init("IsRtcRoomConnectedOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String class JoinRTCRoomOptions extends BaseClass: func _init(): super._init("JoinRTCRoomOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String ## (Optional) Allows the local application to set local audio options for the RTC Room if it is enabled. Set this to a [Dictionary] to override the defaults.[br] ## A [Dictionary] with keys: [br] ## - flags: A bitwise-or union of [enum EOS.RTC.JoinRoomFlags],[br] ## - use_manual_audio_input: [bool],[br] ## - use_manual_audio_output: [bool],[br] ## - local_audio_device_input_starts_muted: [bool] var local_rtc_options = null class LeaveRTCRoomOptions extends BaseClass: func _init(): super._init("LeaveRTCRoomOptions") var local_user_id = EOSGRuntime.local_product_user_id var lobby_id: String class LobbyInterface: static func create_lobby(options: CreateLobbyOptions) -> void: IEOS.lobby_interface_create_lobby(options) static func destroy_lobby(options: DestroyLobbyOptions) -> void: IEOS.lobby_interface_destroy_lobby(options) static func join_lobby(options: JoinLobbyOptions) -> void: IEOS.lobby_interface_join_lobby(options) static func join_lobby_by_id(options: JoinLobbyByIdOptions) -> void: IEOS.lobby_interface_join_lobby_by_id(options) static func leave_lobby(options: LeaveLobbyOptions) -> void: IEOS.lobby_interface_leave_lobby(options) static func update_lobby_modification(options: UpdateLobbyModificationOptions) -> Dictionary: return IEOS.lobby_interface_update_lobby_modification(options) static func update_lobby(options: UpdateLobbyOptions) -> void: IEOS.lobby_interface_update_lobby(options) static func promote_member(options: PromoteMemberOptions) -> void: IEOS.lobby_interface_promote_member(options) static func kick_member(options: KickMemberOptions) -> void: IEOS.lobby_interface_kick_member(options) static func hard_mute_member(options: HardMuteMemberOptions) -> void: IEOS.lobby_interface_hard_mute_member(options) static func send_invite(options: SendInviteOptions) -> void: IEOS.lobby_interface_send_invite(options) static func reject_invite(options: RejectInviteOptions) -> void: IEOS.lobby_interface_reject_invite(options) static func query_invites(options: QueryInvitesOptions) -> void: IEOS.lobby_interface_query_invites(options) static func get_invite_count(options: GetInviteCountOptions) -> int: return IEOS.lobby_interface_get_invite_count(options) static func get_invite_id_by_index(options: GetInviteIdByIndexOptions) -> Dictionary: return IEOS.lobby_interface_get_invite_id_by_index(options) static func create_lobby_search(options: CreateLobbySearchOptions) -> Dictionary: return IEOS.lobby_interface_create_lobby_search(options) static func copy_lobby_details_by_invite_id(options: CopyLobbyDetailsByInviteIdOptions) -> Dictionary: return IEOS.lobby_interface_copy_lobby_details_by_invite_id(options) static func copy_lobby_details_by_ui_event_id(options: CopyLobbyDetailsByUiEventIdOptions) -> Dictionary: return IEOS.lobby_interface_copy_lobby_details_by_ui_event_id(options) static func copy_lobby_details(options: CopyLobbyDetailsOptions) -> Dictionary: return IEOS.lobby_interface_copy_lobby_details(options) static func get_rtc_room_name(options: GetRtcRoomNameOptions) -> Dictionary: return IEOS.lobby_interface_get_rtc_room_name(options) static func is_rtc_room_connected(options: IsRtcRoomConnectedOptions) -> Dictionary: return IEOS.lobby_interface_is_rtc_room_connected(options) class Metrics: enum MetricsAccountIdType {Epic = 0, External = 1} enum UserControllerType {Unknown = 0, MouseKeyboard = 1, GamepadControl = 2, TouchControl = 3} class BeginPlayerSessionOptions extends BaseClass: func _init(): super._init("BeginPlayerSessionOptions") var account_id_type: MetricsAccountIdType var account_id: String var controller_type: UserControllerType var display_name: String var game_session_id = null # String var server_ip = null # String class EndPlayerSessionOptions extends BaseClass: func _init(): super._init("EndPlayerSessionOptions") var account_id_type: MetricsAccountIdType var account_id: String class MetricsInterface: static func begin_player_session(options: BeginPlayerSessionOptions) -> Result: return IEOS.metrics_interface_begin_player_session(options) static func end_player_session(options: EndPlayerSessionOptions) -> Result: return IEOS.metrics_interface_end_player_session(options) class Mods: enum ModEnumerationType {Installed = 0, AllAvailable} class CopyModInfoOptions extends BaseClass: func _init(): super._init("CopyModInfoOptions") var local_user_id = EOSGRuntime.local_epic_account_id var type: ModEnumerationType class EnumerateModsOptions extends BaseClass: func _init(): super._init("EnumerateModsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var type: ModEnumerationType class InstallModOptions extends BaseClass: func _init(): super._init("InstallModOptions") var local_user_id = EOSGRuntime.local_epic_account_id var mod: Dictionary class UninstallModOptions extends BaseClass: func _init(): super._init("UninstallModOptions") var local_user_id = EOSGRuntime.local_epic_account_id var mod: Dictionary class UpdateModOptions extends BaseClass: func _init(): super._init("UpdateModOptions") var local_user_id = EOSGRuntime.local_epic_account_id var mod: Dictionary class ModsInterface: static func copy_mod_info(options: CopyModInfoOptions) -> Dictionary: return IEOS.mods_interface_copy_mod_info(options) static func enumerate_mods(options: EnumerateModsOptions) -> void: IEOS.mods_interface_enumerate_mods(options) static func install_mod(options: InstallModOptions) -> void: IEOS.mods_interface_install_mod(options) static func uninstall_mod(options: UninstallModOptions) -> void: IEOS.mods_interface_uninstall_mod(options) static func update_mod(options: UpdateModOptions) -> void: IEOS.mods_interface_update_mod(options) class P2P: enum ConnectionClosedReason { Unknown = 0, ClosedByLocalUser = 1, ClosedByPeer = 2, TimedOut = 3, TooManyConnections = 4, InvalidMessage = 5, InvalidData = 6, ConnectionFailed = 7, ConnectionClosed = 8, NegotiationFailed = 9, UnexpectedError = 10 } enum ConnectionEstablishedType {NewConnection = 0, Reconnection = 1} enum NATType {Unknown = 0, Open = 1, Moderate = 2, Strict = 3} enum PacketReliability {UnreliableUnordered = 0, ReliableUnordered = 1, ReliableOrdered = 2} enum RelayControl {NoRelays = 0, AllowRelays = 1, ForceRelays = 2} enum NetworkType {NoConnection = 0, DirectConnection = 1, RelayedConnection = 2} enum Mode {None = 0, Server = 1, Client = 2, Mesh = 3} enum ConnectionStatus {Disconnected = 0, Connecting = 1, Connected = 2} class SetPortRangeOptions extends BaseClass: func _init(): super._init("SetPortRangeOptions") var port: int var max_additional_ports_to_try: int class SetPacketQueueSizeOptions extends BaseClass: func _init(): super._init("SetPacketQueueSizeOptions") var incoming_packet_queue_max_size_bytes: int var outgoing_packet_queue_max_size_bytes: int class P2PInterface: static func get_packet_queue_info() -> Dictionary: return IEOS.p2p_interface_get_packet_queue_info() static func get_port_range() -> Dictionary: return IEOS.p2p_interface_get_port_range() static func get_nat_type() -> Dictionary: return IEOS.p2p_interface_get_nat_type() static func get_relay_control() -> Dictionary: return IEOS.p2p_interface_get_relay_control() static func query_nat_type() -> void: IEOS.p2p_interface_query_nat_type() static func set_packet_queue_size(options: SetPacketQueueSizeOptions) -> EOS.Result: return IEOS.p2p_interface_set_packet_queue_size(options) static func set_port_range(options: SetPortRangeOptions) -> EOS.Result: return IEOS.p2p_interface_set_port_range(options) static func set_relay_control(relay_control: RelayControl) -> EOS.Result: return IEOS.p2p_interface_set_relay_control(relay_control) class Presence: enum Status {Offline = 0, Online = 1, Away = 2, ExtendedAway = 3, DoNotDisturb = 4} class CopyPresenceOptions extends BaseClass: func _init(): super._init("CopyPresenceOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class CreatePresenceModificationOptions extends BaseClass: func _init(): super._init("CopyPresenceOptions") var local_user_id = EOSGRuntime.local_epic_account_id class PresenceModificationSetDataOptions extends BaseClass: func _init(): super._init("PresenceModificationSetDataOptions") var records: Array[Dictionary] # {key: String, value: String} class PresenceModificationDeleteDataOptions extends BaseClass: func _init(): super._init("PresenceModificationDeleteDataOptions") var records: Array # Array[String] class PresenceModificationSetJoinInfoOptions extends BaseClass: func _init(): super._init("PresenceModificationSetJoinInfoOptions") var join_info: String class PresenceModificationSetRawRichTextOptions extends BaseClass: func _init(): super._init("PresenceModificationSetRawRichTextOptions") var rich_text: String class PresenceModificationSetStatusOptions extends BaseClass: func _init(): super._init("PresenceModificationSetStatusOptions") var status: int class GetJoinInfoOptions extends BaseClass: func _init(): super._init("GetJoinInfoOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class HasPresenceOptions extends BaseClass: func _init(): super._init("HasPresenceOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class QueryPresenceOptions extends BaseClass: func _init(): super._init("QueryPresenceOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var client_data = null class SetPresenceOptions extends BaseClass: func _init(): super._init("SetPresenceOptions") var local_user_id = EOSGRuntime.local_epic_account_id var presence_modification: RefCounted # PresenceModificationWrapper var client_data = null class PresenceInterface: static func copy_presence(options: CopyPresenceOptions) -> Dictionary: return IEOS.presence_interface_copy_presence(options) static func create_presence_modification(options: CreatePresenceModificationOptions) -> Dictionary: return IEOS.presence_interface_create_presence_modification(options) static func get_join_info(options: GetJoinInfoOptions) -> Dictionary: return IEOS.presence_interface_get_join_info(options) static func has_presence(options: HasPresenceOptions) -> bool: return IEOS.presence_interface_has_presence(options) static func query_presence(options: QueryPresenceOptions) -> void: IEOS.presence_interface_query_presence(options) static func set_presence(options: SetPresenceOptions) -> void: IEOS.presence_interface_set_presence(options) class Reports: enum PlayerReportsCategory { Invalid = 0, Cheating = 1, Exploiting = 2, OffensiveProfile = 3, VerbalAbuse = 4, Scamming = 5, Spamming = 6, Other = 7 } class SendPlayerBehaviorReportOptions extends BaseClass: func _init(): super._init("SendPlayerBehaviorReportOptions") var category: PlayerReportsCategory var context: String var message: String var reported_user_id: String var reporter_user_id = EOSGRuntime.local_product_user_id var client_data = null class ReportsInterface: static func send_player_behavior_report(options: SendPlayerBehaviorReportOptions) -> void: IEOS.reports_interface_send_player_behavior_report(options) class ProgressionSnapshot: class BeginSnapshotOptions extends BaseClass: func _init(): super._init("BeginSnapshotOptions") var local_user_id = EOSGRuntime.local_product_user_id class AddProgressionOptions extends BaseClass: func _init(): super._init("AddProgressionOptions") var key: String var snapshot_id: int var value: String class SubmitSnapshotOptions extends BaseClass: func _init(): super._init("SubmitSnapshotOptions") var snapshot_id: int var client_data = null class DeleteSnapshotOptions extends BaseClass: func _init(): super._init("DeleteSnapshotOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class EndSnapshotOptions extends BaseClass: func _init(): super._init("EndSnapshotOptions") var snapshot_id: int class ProgressionSnapshotInterface: static func begin_snapshot(options: BeginSnapshotOptions) -> Dictionary: return IEOS.progression_snapshot_interface_begin_snapshot(options) static func add_progression(options: AddProgressionOptions) -> Result: return IEOS.progression_snapshot_interface_add_progression(options) static func submit_snapshot(options: SubmitSnapshotOptions) -> void: IEOS.progression_snapshot_interface_submit_snapshot(options) static func delete_snapshot(options: DeleteSnapshotOptions) -> void: IEOS.progression_snapshot_interface_delete_snapshot(options) static func end_snapshot(options: EndSnapshotOptions) -> void: IEOS.progression_snapshot_interface_end_snapshot(options) class UI: const ModifierShift = 16 const KeyTypeMask = (1 << ModifierShift) - 1 const ModifierMask = ~KeyTypeMask const Shift = 1 << ModifierShift const Control_ = 2 << ModifierShift const Alt = 4 << ModifierShift const Meta = 8 << ModifierShift const ValidModifierMask = Shift | Control_ | Alt | Meta enum NotificationLocation {TopLeft, TopRight, BottomLeft, BottomRight} enum KeyCombination { ModifierShift = 16, KeyTypeMask = (1 << ModifierShift) - 1, ModifierMask = ~KeyTypeMask, Shift = 1 << ModifierShift, Control_ = 2 << ModifierShift, Alt = 4 << ModifierShift, Meta = 8 << ModifierShift, ValidModifierMask = Shift | Control_ | Alt | Meta, None = 0, Space, Backspace, Tab, Escape, PageUp, PageDown, End, Home, Insert, Delete, Left, Up, Right, Down, Key0, Key1, Key2, Key3, Key4, Key5, Key6, Key7, Key8, Key9, KeyA, KeyB, KeyC, KeyD, KeyE, KeyF, KeyG, KeyH, KeyI, KeyJ, KeyK, KeyL, KeyM, KeyN, KeyO, KeyP, KeyQ, KeyR, KeyS, KeyT, KeyU, KeyV, KeyW, KeyX, KeyY, KeyZ, Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9, NumpadAsterisk, NumpadPlus, NumpadMinus, NumpadPeriod, NumpadDivide, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, OemPlus, OemComma, OemMinus, OemPeriod, Oem1, Oem2, Oem3, Oem4, Oem5, Oem6, Oem7, Oem8, MaxKeyType } enum InputStateButtonFlags { None = 0, DPadLeft = (1 << 0), DPadRight = (1 << 1), DPadDown = (1 << 2), DPadUp = (1 << 3), FaceButtonLeft = (1 << 4), FaceButtonRight = (1 << 5), FaceButtonBottom = (1 << 6), FaceButtonTop = (1 << 7), LeftShoulder = (1 << 8), RightShoulder = (1 << 9), LeftTrigger = (1 << 10), RightTrigger = (1 << 11), SpecialLeft = (1 << 12), SpecialRight = (1 << 13), LeftThumbstick = (1 << 14), RightThumbstick = (1 << 15), } class AcknowledgeEventIdOptions extends BaseClass: func _init(): super._init("AcknowledgeEventIdOptions") var ui_event_id: int var result: int class GetFriendsVisibleOptions extends BaseClass: func _init(): super._init("GetFriendsVisibleOptions") var local_user_id = EOSGRuntime.local_epic_account_id class GetToggleFriendsKeyOptions extends BaseClass: func _init(): super._init("GetToggleFriendsKeyOptions") class HideFriendsOptions extends BaseClass: func _init(): super._init("HideFriendsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class SetDisplayPreferenceOptions extends BaseClass: func _init(): super._init("SetDisplayPreferenceOptions") var notification_location: NotificationLocation class SetToggleFriendsKeyOptions extends BaseClass: func _init(): super._init("SetToggleFriendsKeyOptions") var key_combination: KeyCombination class ShowFriendsOptions extends BaseClass: func _init(): super._init("ShowFriendsOptions") var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class GetFriendsExclusiveInputOptions extends BaseClass: func _init(): super._init("GetFriendsExclusiveInputOptions") var local_user_id = EOSGRuntime.local_epic_account_id class ShowBlockPlayerOptions extends BaseClass: func _init(): super._init("ShowBlockPlayerOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id: String class ShowReportPlayerOptions extends BaseClass: func _init(): super._init("ShowReportPlayerOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id: String class PauseSocialOverlayOptions extends BaseClass: func _init(): super._init("PauseSocialOverlayOptions") var is_paused: bool class IsSocialOverlayPausedOptions extends BaseClass: func _init(): super._init("IsSocialOverlayPausedOptions") class ReportInputStateOptions extends BaseClass: func _init(): super._init("ReportInputStateOptions") var button_down_flags: InputStateButtonFlags var gamepad_index: int var left_stick_x: float var left_stick_y: float var left_trigger: float var mouse_pos_x: int var mouse_pos_y: int var right_stick_x: float var right_stick_y: float var right_trigger: float class PrePresentOptions extends BaseClass: func _init(): super._init("PrePresentOptions") class ShowNativeProfileOptions extends BaseClass: func _init(): super._init("ShowNativeProfileOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id: String class UIInterface: static func acknowledge_event_id(options: AcknowledgeEventIdOptions) -> Result: return IEOS.ui_interface_acknowledge_event_id(options) static func get_friends_visible(options: GetFriendsVisibleOptions) -> bool: return IEOS.ui_interface_get_friends_visible(options) static func get_notification_location_preference() -> EOS.UI.NotificationLocation: return IEOS.ui_interface_get_notification_location_preference() static func get_toggle_friends_key(options: GetToggleFriendsKeyOptions) -> EOS.UI.KeyCombination: return IEOS.ui_interface_get_toggle_friends_key(options) static func hide_friends(options: HideFriendsOptions) -> void: IEOS.ui_interface_hide_friends(options) static func is_valid_key_combination(key_combination: EOS.UI.KeyCombination) -> bool: return IEOS.ui_interface_is_valid_key_combination(key_combination) static func set_display_preference(options: SetDisplayPreferenceOptions) -> EOS.UI.NotificationLocation: return IEOS.ui_interface_set_display_preference(options) static func set_toggle_friends_key(options: SetToggleFriendsKeyOptions) -> Result: return IEOS.ui_interface_set_toggle_friends_key(options) static func show_friends(options: ShowFriendsOptions) -> void: IEOS.ui_interface_show_friends(options) static func get_friends_exclusive_input(options: GetFriendsExclusiveInputOptions) -> bool: return IEOS.ui_interface_get_friends_exclusive_input(options) static func show_block_player(options: ShowBlockPlayerOptions) -> void: IEOS.ui_interface_show_block_player(options) static func show_report_player(options: ShowReportPlayerOptions) -> void: IEOS.ui_interface_show_report_player(options) static func pause_social_overlay(options: PauseSocialOverlayOptions) -> Result: return IEOS.ui_interface_pause_social_overlay(options) static func is_social_overlay_paused(options: IsSocialOverlayPausedOptions) -> bool: return IEOS.ui_interface_is_social_overlay_paused(options) static func set_toggle_friends_button(options: SetToggleFriendsKeyOptions) -> Result: return IEOS.ui_interface_set_toggle_friends_button(options) static func report_input_state(options: ReportInputStateOptions) -> Result: return IEOS.ui_interface_report_input_state(options) static func pre_present(options: PrePresentOptions) -> Result: return IEOS.ui_interface_pre_present(options) static func show_native_profile(options: ShowNativeProfileOptions) -> void: IEOS.ui_interface_show_native_profile(options) class UserInfo: class CopyExternalUserInfoByAccountIdOptions extends BaseClass: func _init(): super._init("CopyExternalUserInfoByAccountIdOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var account_id: String class CopyExternalUserInfoByAccountTypeOptions extends BaseClass: func _init(): super._init("CopyExternalUserInfoByAccountTypeOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var account_type: ExternalAccountType class CopyExternalUserInfoByIndexOptions extends BaseClass: func _init(): super._init("CopyExternalUserInfoByIndexOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var index: int class CopyUserInfoOptions extends BaseClass: func _init(): super._init("CopyUserInfoOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class GetExternalUserInfoCountOptions extends BaseClass: func _init(): super._init("GetExternalUserInfoCountOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class QueryUserInfoOptions extends BaseClass: func _init(): super._init("QueryUserInfoOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var client_data = null class QueryUserInfoByDisplayNameOptions extends BaseClass: func _init(): super._init("QueryUserInfoByDisplayNameOptions") var local_user_id = EOSGRuntime.local_epic_account_id var display_name: String var client_data = null class QueryUserInfoByExternalAccountOptions extends BaseClass: func _init(): super._init("QueryUserInfoByExternalAccountOptions") var account_type: ExternalAccountType var external_account_id: String var local_user_id = EOSGRuntime.local_epic_account_id var client_data = null class CopyBestDisplayNameOptions extends BaseClass: func _init(): super._init("CopyBestDisplayNameOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id class CopyBestDisplayNameWithPlatformOptions extends BaseClass: func _init(): super._init("CopyBestDisplayNameWithPlatformOptions") var local_user_id = EOSGRuntime.local_epic_account_id var target_user_id = EOSGRuntime.local_epic_account_id var target_platform_type: OnlinePlatformType class GetLocalPlatformTypeOptions extends BaseClass: func _init(): super._init("GetLocalPlatformTypeOptions") class UserInfoInterface: static func copy_external_user_info_by_account_id(options: CopyExternalUserInfoByAccountIdOptions) -> Dictionary: return IEOS.user_info_interface_copy_external_user_info_by_account_id(options) static func copy_external_user_info_by_account_type(options: CopyExternalUserInfoByAccountTypeOptions) -> Dictionary: return IEOS.user_info_interface_copy_external_user_info_by_account_type(options) static func copy_external_user_info_by_index(options: CopyExternalUserInfoByIndexOptions) -> Dictionary: return IEOS.user_info_interface_copy_external_user_info_by_index(options) static func copy_user_info(options: CopyUserInfoOptions) -> Dictionary: return IEOS.user_info_interface_copy_user_info(options) static func get_external_user_info_count(options: GetExternalUserInfoCountOptions) -> int: return IEOS.user_info_interface_get_external_user_info_count(options) static func query_user_info(options: QueryUserInfoOptions) -> void: IEOS.user_info_interface_query_user_info(options) static func query_user_info_by_display_name(options: QueryUserInfoByDisplayNameOptions) -> void: IEOS.user_info_interface_query_user_info_by_display_name(options) static func query_user_info_by_external_account(options: QueryUserInfoByExternalAccountOptions) -> void: IEOS.user_info_interface_query_user_info_by_external_account(options) static func copy_best_display_name(options: CopyBestDisplayNameOptions) -> Dictionary: return IEOS.user_info_interface_copy_best_display_name(options) static func copy_best_display_name_with_platform(options: CopyBestDisplayNameWithPlatformOptions) -> Dictionary: return IEOS.user_info_interface_copy_best_display_name_with_platform(options) static func get_local_platform_type(options: GetLocalPlatformTypeOptions = GetLocalPlatformTypeOptions.new()) -> OnlinePlatformType: return IEOS.user_info_interface_get_local_platform_type(options) class Logging: enum LogCategory { Core = 0, Auth = 1, Friends = 2, Presence = 3, UserInfo = 4, HttpSerialization = 5, Ecom = 6, P2P = 7, Sessions = 8, RateLimiter = 9, PlayerDataStorage = 10, Analytics = 11, Messaging = 12, Connect = 13, Overlay = 14, Achievements = 15, Stats = 16, Ui = 17, Lobby = 18, Leaderboards = 19, Keychain = 20, IntegratedPlatform = 21, TitleStorage = 22, Mods = 23, AntiCheat = 24, Reports = 25, Sanctions = 26, ProgressionSnapshots = 27, Kws = 28, Rtc = 29, RTCAdmin = 30, CustomInvites = 31, Http = 41, AllCategories = 0x7fffffff } enum LogLevel { Off = 0, Fatal = 100, Error = 200, Warning = 300, Info = 400, Verbose = 500, VeryVerbose = 600 } class LogMessage extends BaseClass: func _init(): super._init("LogMessage") var category: String var level: LogLevel var message: String static func from(msg_dict: Dictionary) -> LogMessage: var msg = LogMessage.new() msg.category = msg_dict.get("category") msg.level = msg_dict.get("level") msg.message = msg_dict.get("message") return msg func _to_string() -> String: return "%s | %s | %s" % [category, level, message] static func set_log_level(log_category: EOS.Logging.LogCategory, log_level: EOS.Logging.LogLevel) -> Result: return IEOS.logging_interface_set_log_level(log_category, log_level) class TitleStorage: class QueryFileOptions extends BaseClass: func _init(): super._init("QueryFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var client_data = null class QueryFileListOptions extends BaseClass: func _init(): super._init("QueryFileListOptions") var local_user_id = EOSGRuntime.local_product_user_id var list_of_tags: Array # [String] var client_data = null class CopyFileMetadataByFilenameOptions extends BaseClass: func _init(): super._init("CopyFileMetadataByFilenameOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String class GetFileMetadataCountOptions extends BaseClass: func _init(): super._init("GetFileMetadataCountOptions") var local_user_id = EOSGRuntime.local_product_user_id class CopyFileMetadataAtIndexOptions extends BaseClass: func _init(): super._init("CopyFileMetadataAtIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var index: int class DeleteCacheOptions extends BaseClass: func _init(): super._init("DeleteCacheOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class ReadFileOptions extends BaseClass: func _init(): super._init("ReadFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var read_chunk_length_bytes = 4096 var client_data = null class TitleStorageInterface: static func query_file(options: QueryFileOptions) -> void: IEOS.titlestorage_interface_query_file(options) static func query_file_list(options: QueryFileListOptions) -> void: IEOS.titlestorage_interface_query_file_list(options) static func copy_file_metadata_by_filename(options: CopyFileMetadataByFilenameOptions) -> Dictionary: return IEOS.titlestorage_interface_copy_file_metadata_by_filename(options) static func get_file_metadata_count(options: GetFileMetadataCountOptions) -> int: return IEOS.titlestorage_interface_get_file_metadata_count(options) static func copy_file_metadata_at_index(options) -> Dictionary: return IEOS.titlestorage_interface_copy_file_metadata_at_index(options) static func delete_cache(options: DeleteCacheOptions) -> Result: return IEOS.titlestorage_interface_delete_cache(options) static func read_file(options: ReadFileOptions) -> Variant: return IEOS.titlestorage_interface_read_file(options) class PlayerDataStorage: class QueryFileOptions extends BaseClass: func _init(): super._init("QueryFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var client_data = null class QueryFileListOptions extends BaseClass: func _init(): super._init("QueryFileListOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class CopyFileMetadataByFilenameOptions extends BaseClass: func _init(): super._init("CopyFileMetadataByFilenameOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String class GetFileMetadataCountOptions extends BaseClass: func _init(): super._init("GetFileMetadataCountOptions") var local_user_id = EOSGRuntime.local_product_user_id class CopyFileMetadataAtIndexOptions extends BaseClass: func _init(): super._init("CopyFileMetadataAtIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var index: int class DuplicateFileOptions extends BaseClass: func _init(): super._init("DuplicateFileOptions") var destination_filename: String var local_user_id = EOSGRuntime.local_product_user_id var source_filename: String var client_data = null class DeleteFileOptions extends BaseClass: func _init(): super._init("DeleteFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var client_data = null class DeleteCacheOptions extends BaseClass: func _init(): super._init("DeleteCacheOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class ReadFileOptions extends BaseClass: func _init(): super._init("ReadFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var read_chunk_length_bytes = 4096 var client_data = null class WriteFileOptions extends BaseClass: func _init(): super._init("WriteFileOptions") var local_user_id = EOSGRuntime.local_product_user_id var filename: String var data: PackedByteArray var chunk_length_bytes = 4096 var client_data = null var written_buffer_length_bytes = 0 class PlayerDataStorageInterface: static func query_file(options: QueryFileOptions) -> void: IEOS.playerdatastorage_interface_query_file(options) static func query_file_list(options: QueryFileListOptions) -> void: IEOS.playerdatastorage_interface_query_file_list(options) static func copy_file_metadata_by_filename(options: CopyFileMetadataByFilenameOptions) -> Dictionary: return IEOS.playerdatastorage_interface_copy_file_metadata_by_filename(options) static func get_file_metadata_count(options: GetFileMetadataCountOptions) -> Dictionary: return IEOS.playerdatastorage_interface_get_file_metadata_count(options) static func copy_file_metadata_at_index(options) -> Dictionary: return IEOS.playerdatastorage_interface_copy_file_metadata_at_index(options) static func duplicate_file(options: DuplicateFileOptions) -> void: IEOS.playerdatastorage_interface_duplicate_file(options) static func delete_file(options: DeleteFileOptions) -> void: IEOS.playerdatastorage_interface_delete_file(options) static func delete_cache(options: DeleteCacheOptions) -> Result: return IEOS.playerdatastorage_interface_delete_cache(options) static func read_file(options: ReadFileOptions) -> Variant: return IEOS.playerdatastorage_interface_read_file(options) static func write_file(options: WriteFileOptions) -> Variant: return IEOS.playerdatastorage_interface_write_file(options) class Sanctions: enum AppealReason { Invalid = 0, IncorrectSanction = 1, CompromisedAccount = 2, UnfairPunishment = 3, AppealForForgiveness = 4, } class QueryActivePlayerSanctionsOptions extends BaseClass: func _init(): super._init("QueryActivePlayerSanctionsOptions") var local_user_id = EOSGRuntime.local_product_user_id var target_user_id = EOSGRuntime.local_product_user_id var client_data = null class GetPlayerSanctionCountOptions extends BaseClass: func _init(): super._init("GetPlayerSanctionCountOptions") var target_user_id = EOSGRuntime.local_product_user_id var client_data = null class CopyPlayerSanctionByIndexOptions extends BaseClass: func _init(): super._init("CopyPlayerSanctionByIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var sanction_index: int var target_user_id = EOSGRuntime.local_product_user_id var client_data = null class CreatePlayerSanctionAppealOptions extends BaseClass: func _init(): super._init("CreatePlayerSanctionAppealOptions") var local_user_id = EOSGRuntime.local_product_user_id var reason: AppealReason var reference_id: String var client_data = null class SanctionsInterface: static func query_active_player_sanctions(options: QueryActivePlayerSanctionsOptions) -> void: IEOS.sanctions_interface_query_active_player_sanctions(options) static func get_player_sanction_count(options: GetPlayerSanctionCountOptions) -> int: return IEOS.sanctions_interface_get_player_sanction_count(options) static func copy_player_sanction_by_index(options: CopyPlayerSanctionByIndexOptions) -> Variant: return IEOS.sanctions_interface_copy_player_sanction_by_index(options) static func create_player_sanction_appeal(options: CreatePlayerSanctionAppealOptions) -> void: IEOS.sanctions_interface_create_player_sanction_appeal(options) class Sessions: enum OnlineSessionState { NoSession, Creating, Pending, Starting, InProgress, Ending, Ended, Destroying } enum SessionAttributeAdvertisementType { DontAdvertise, Advertise } enum OnlineSessionPermissionLevel { PublicAdvertised, JoinViaPresence, InviteOnly } const SEARCH_BUCKET_ID = "bucket" const SEARCH_EMPTY_SERVERS_ONLY = "emptyonly" const SEARCH_NONEMPTY_SERVERS_ONLY = "nonemptyonly" const SEARCH_MINSLOTSAVAILABLE = "minslotsavailable" class CopyActiveSessionDetailsOptions extends BaseClass: func _init(): super._init("CopyActiveSessionDetailsOptions") var session_name: String class CopySessionDetailsByInviteIdOptions extends BaseClass: func _init(): super._init("CopySessionDetailsByInviteIdOptions") var invite_id: String class CopySessionDetailsByUiEventIdOptions extends BaseClass: func _init(): super._init("CopySessionDetailsByUiEventIdOptions") var ui_event_id: String class CopySessionDetailsForPresenceOptions extends BaseClass: func _init(): super._init("CopySessionDetailsForPresenceOptions") var local_user_id = EOSGRuntime.local_product_user_id class CreateSessionModificationOptions extends BaseClass: func _init(): super._init("CreateSessionModificationOptions") var allowed_platform_ids = [] # Array[int] var bucket_id: String var local_user_id = EOSGRuntime.local_product_user_id var max_players: int var presence_enabled: bool var sanctions_enabled: bool var session_id: String var session_name: String class CreateSessionSearchOptions extends BaseClass: func _init(): super._init("CreateSessionSearchOptions") var max_search_results := 10 class GetInviteIdByIndexOptions extends BaseClass: func _init(): super._init("GetInviteIdByIndexOptions") var local_user_id = EOSGRuntime.local_product_user_id var index: int class UpdateSessionModificationOptions extends BaseClass: func _init(): super._init("UpdateSessionModificationOptions") var session_name: String class DumpSessionStateOptions extends BaseClass: func _init(): super._init("DumpSessionStateOptions") var session_name: String class GetInviteCountOptions extends BaseClass: func _init(): super._init("GetInviteCountOptions") var local_user_id = EOSGRuntime.local_product_user_id class IsUserInSessionOptions extends BaseClass: func _init(): super._init("IsUserInSessionOptions") var session_name: String var target_user_id = EOSGRuntime.local_product_user_id class DestroySessionOptions extends BaseClass: func _init(): super._init("DestroySessionOptions") var session_name: String var client_data = null class EndSessionOptions extends BaseClass: func _init(): super._init("EndSessionOptions") var session_name: String var client_data = null class JoinSessionOptions extends BaseClass: func _init(): super._init("JoinSessionOptions") var local_user_id = EOSGRuntime.local_product_user_id var presence_enabled: bool var session_details: EOSGSessionDetails var session_name: String var client_data = null class QueryInvitesOptions extends BaseClass: func _init(): super._init("QueryInvitesOptions") var local_user_id = EOSGRuntime.local_product_user_id var client_data = null class RegisterPlayersOptions extends BaseClass: func _init(): super._init("RegisterPlayersOptions") var session_name: String var players_to_register: Array # Array[String] var client_data = null class RejectInviteOptions extends BaseClass: func _init(): super._init("RejectInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var invite_id: String var client_data = null class SendInviteOptions extends BaseClass: func _init(): super._init("SendInviteOptions") var local_user_id = EOSGRuntime.local_product_user_id var session_name: String var target_user_id: String var client_data = null class StartSessionOptions extends BaseClass: func _init(): super._init("StartSessionOptions") var session_name: String var client_data = null class UnregisterPlayersOptions extends BaseClass: func _init(): super._init("UnregisterPlayersOptions") var session_name: String var players_to_unregister: Array # Array[String] var client_data = null class UpdateSessionOptions extends BaseClass: func _init(): super._init("UpdateSessionOptions") var session_modification: EOSGSessionModification var client_data = null class SessionsInterface: static func copy_active_session_details(options: CopyActiveSessionDetailsOptions) -> Dictionary: return IEOS.sessions_interface_copy_active_session_details(options) static func copy_session_details_by_invite_id(options: CopySessionDetailsByInviteIdOptions) -> Dictionary: return IEOS.sessions_interface_copy_session_details_by_invite_id(options) static func copy_session_details_by_ui_event_id(options: CopySessionDetailsByUiEventIdOptions) -> Dictionary: return IEOS.sessions_interface_copy_session_details_by_ui_event_id(options) static func copy_session_details_for_presence(options: CopySessionDetailsForPresenceOptions) -> Dictionary: return IEOS.sessions_interface_copy_session_details_for_presence(options) static func create_session_modification(options: CreateSessionModificationOptions) -> Dictionary: return IEOS.sessions_interface_create_session_modification(options) static func create_session_search(options: CreateSessionSearchOptions) -> Dictionary: return IEOS.sessions_interface_create_session_search(options) static func get_invite_id_by_index(options: GetInviteIdByIndexOptions) -> Dictionary: return IEOS.sessions_interface_get_invite_id_by_index(options) static func update_session_modification(options: UpdateSessionModificationOptions) -> Dictionary: return IEOS.sessions_interface_update_session_modification(options) static func dump_session_state(options: DumpSessionStateOptions) -> int: return IEOS.sessions_interface_dump_session_state(options) static func get_invite_count(options: GetInviteCountOptions) -> int: return IEOS.sessions_interface_get_invite_count(options) static func is_user_in_session(options: IsUserInSessionOptions) -> int: return IEOS.sessions_interface_is_user_in_session(options) static func destroy_session(options: DestroySessionOptions) -> void: IEOS.sessions_interface_destroy_session(options) static func end_session(options: EndSessionOptions) -> void: IEOS.sessions_interface_end_session(options) static func join_session(options: JoinSessionOptions) -> void: IEOS.sessions_interface_join_session(options) static func query_invites(options: QueryInvitesOptions) -> void: IEOS.sessions_interface_query_invites(options) static func register_players(options: RegisterPlayersOptions) -> void: IEOS.sessions_interface_register_players(options) static func reject_invite(options: RejectInviteOptions) -> void: IEOS.sessions_interface_reject_invite(options) static func send_invite(options: SendInviteOptions) -> void: IEOS.sessions_interface_send_invite(options) static func start_session(options: StartSessionOptions) -> void: IEOS.sessions_interface_start_session(options) static func unregister_players(options: UnregisterPlayersOptions) -> void: IEOS.sessions_interface_unregister_players(options) static func update_session(options: UpdateSessionOptions) -> void: IEOS.sessions_interface_update_session(options) class RTC: enum ParticipantStatus { Joined, Left, } enum JoinRoomFlags { EnableEcho = 0x01, EnableDataChannel = 0x04 } class AddNotifyDisconnectedOptions extends BaseClass: func _init(): super._init("AddNotifyDisconnectedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyParticipantStatusChangedOptions extends BaseClass: func _init(): super._init("AddNotifyParticipantStatusChangedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyRoomStatisticsUpdatedOptions extends BaseClass: func _init(): super._init("AddNotifyRoomStatisticsUpdatedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class SetRoomSettingOptions extends BaseClass: func _init(): super._init("SetRoomSettingOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var setting_key: String var setting_value: String class SetSettingOptions extends BaseClass: func _init(): super._init("SetSettingOptions") var setting_key: String var setting_value: String class BlockParticipantOptions extends BaseClass: func _init(): super._init("BlockParticipantOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var participant_id: String var blocked: bool var client_data = null class JoinRoomOptions extends BaseClass: func _init(): super._init("JoinRoomOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var client_base_url: String var participant_token: String var participant_id: String var flags: int # JoinRoomFlags var manual_audio_input_enabled: bool var manual_audio_output_enabled: bool var client_data = null class LeaveRoomOptions extends BaseClass: func _init(): super._init("LeaveRoomOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var client_data = null class RTCInterface: static func add_notify_disconnected(options: AddNotifyDisconnectedOptions) -> int: return IEOS.rtc_interface_add_notify_disconnected(options) static func add_notify_participant_status_changed(options: AddNotifyParticipantStatusChangedOptions) -> int: return IEOS.rtc_interface_add_notify_participant_status_changed(options) static func add_notify_room_statistics_updated(options: AddNotifyRoomStatisticsUpdatedOptions) -> int: return IEOS.rtc_interface_add_notify_room_statistics_updated(options) static func set_room_setting(options: SetRoomSettingOptions) -> int: return IEOS.rtc_interface_set_room_setting(options) static func set_setting(options: SetSettingOptions) -> int: return IEOS.rtc_interface_set_setting(options) static func block_participant(options: BlockParticipantOptions) -> void: IEOS.rtc_interface_block_participant(options) static func join_room(options: JoinRoomOptions) -> void: IEOS.rtc_interface_join_room(options) static func leave_room(options: LeaveRoomOptions) -> void: IEOS.rtc_interface_leave_room(options) static func remove_notify_disconnected(notification_id: int) -> void: IEOS.rtc_interface_remove_notify_disconnected(notification_id) static func remove_notify_participant_status_changed(notification_id: int) -> void: IEOS.rtc_interface_remove_notify_participant_status_changed(notification_id) static func remove_notify_room_statistics_updated(notification_id: int) -> void: IEOS.rtc_interface_remove_notify_room_statistics_updated(notification_id) class RTCAudio: enum AudioStatus { ## Audio unsupported by the source (no devices) Unsupported = 0, ## Audio enabled Enabled = 1, ## Audio disabled Disabled = 2, ## Audio disabled by the administrator AdminDisabled = 3, ## Audio channel is disabled temporarily for both sending and receiving NotListeningDisabled = 4 } enum AudioInputStatus { ## The device is not in used right now (e.g: you are alone in the room). In such cases, the hardware resources are not allocated. Idle = 0, ## The device is being used and capturing audio Recording = 1, ## The SDK is in a recording state, but actually capturing silence because the device is exclusively being used by the platform at the moment. ## This only applies to certain platforms. RecordingSilent = 2, ## The SDK is in a recording state, but actually capturing silence because the device is disconnected (e.g: the microphone is not plugged in). ## This only applies to certain platforms. RecordingDisconnected = 3, ## Something failed while trying to use the device Failed = 4 } enum AudioOutputStatus { ## The device is not in used right now (e.g: you are alone in the room). In such cases, the hardware resources are not allocated. Idle = 0, ## Device is in use Playing = 1, ## Something failed while trying to use the device Failed = 2 } class CopyInputDeviceInformationByIndexOptions extends BaseClass: func _init(): super._init("CopyInputDeviceInformationByIndexOptions") var device_index: int class CopyOutputDeviceInformationByIndexOptions extends BaseClass: func _init(): super._init("CopyOutputDeviceInformationByIndexOptions") var device_index: int class AddNotifyAudioBeforeRenderOptions extends BaseClass: func _init(): super._init("AddNotifyAudioBeforeRenderOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var unmixed_audio: bool class AddNotifyAudioBeforeSendOptions extends BaseClass: func _init(): super._init("AddNotifyAudioBeforeSendOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyAudioInputStateOptions extends BaseClass: func _init(): super._init("AddNotifyAudioInputStateOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyAudioOutputStateOptions extends BaseClass: func _init(): super._init("AddNotifyAudioOutputStateOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyParticipantUpdatedOptions extends BaseClass: func _init(): super._init("AddNotifyParticipantUpdatedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class GetInputDevicesCountOptions extends BaseClass: func _init(): super._init("GetInputDevicesCountOptions") class GetOutputDevicesCountOptions extends BaseClass: func _init(): super._init("GetOutputDevicesCountOptions") class SendAudioOptions extends BaseClass: func _init(): super._init("SendAudioOptions") var local_user_id: String = HAuth.product_user_id var room_name: String var frames: PackedInt32Array var sample_rate = 48000 var channels = 1 class QueryInputDevicesInformationOptions extends BaseClass: func _init(): super._init("QueryInputDevicesInformationOptions") var client_data = null class QueryOutputDevicesInformationOptions extends BaseClass: func _init(): super._init("QueryOutputDevicesInformationOptions") var client_data = null class RegisterPlatformUserOptions extends BaseClass: func _init(): super._init("RegisterPlatformUserOptions") var platform_user_id: String var client_data = null class SetInputDeviceSettingsOptions extends BaseClass: func _init(): super._init("SetInputDeviceSettingsOptions") var local_user_id = EOSGRuntime.local_product_user_id var real_device_id: String var platform_aec: bool var client_data = null class SetOutputDeviceSettingsOptions extends BaseClass: func _init(): super._init("SetOutputDeviceSettingsOptions") var local_user_id = EOSGRuntime.local_product_user_id var real_device_id: String var client_data = null class UnregisterPlatformUserOptions extends BaseClass: func _init(): super._init("UnregisterPlatformUserOptions") var platform_user_id: String var client_data = null class UpdateParticipantVolumeOptions extends BaseClass: func _init(): super._init("UpdateParticipantVolumeOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var participant_id: String var volume: float var client_data = null class UpdateReceivingOptions extends BaseClass: func _init(): super._init("UpdateReceivingOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var participant_id: String var audio_enabled: bool var client_data = null class UpdateReceivingVolumeOptions extends BaseClass: func _init(): super._init("UpdateReceivingVolumeOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var participant_id: String var volume: float var client_data = null class UpdateSendingOptions extends BaseClass: func _init(): super._init("UpdateSendingOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var audio_status: AudioStatus var client_data = null class UpdateSendingVolumeOptions extends BaseClass: func _init(): super._init("UpdateSendingVolumeOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var volume: float var client_data = null class RTCAudioInterface: static func copy_input_device_information_by_index(options: CopyInputDeviceInformationByIndexOptions) -> Dictionary: return IEOS.rtc_audio_interface_copy_input_device_information_by_index(options) static func copy_output_device_information_by_index(options: CopyOutputDeviceInformationByIndexOptions) -> Dictionary: return IEOS.rtc_audio_interface_copy_output_device_information_by_index(options) static func add_notify_audio_before_render(options: AddNotifyAudioBeforeRenderOptions) -> int: return IEOS.rtc_audio_interface_add_notify_audio_before_render(options) static func add_notify_audio_before_send(options: AddNotifyAudioBeforeSendOptions) -> int: return IEOS.rtc_audio_interface_add_notify_audio_before_send(options) static func add_notify_audio_input_state(options: AddNotifyAudioInputStateOptions) -> int: return IEOS.rtc_audio_interface_add_notify_audio_input_state(options) static func add_notify_audio_output_state(options: AddNotifyAudioOutputStateOptions) -> int: return IEOS.rtc_audio_interface_add_notify_audio_output_state(options) static func add_notify_participant_updated(options: AddNotifyParticipantUpdatedOptions) -> int: return IEOS.rtc_audio_interface_add_notify_participant_updated(options) static func get_input_devices_count(options := GetInputDevicesCountOptions.new()) -> int: return IEOS.rtc_audio_interface_get_input_devices_count(options) static func get_output_devices_count(options := GetOutputDevicesCountOptions.new()) -> int: return IEOS.rtc_audio_interface_get_output_devices_count(options) static func send_audio(options: SendAudioOptions) -> int: return IEOS.rtc_audio_interface_send_audio(options) static func query_input_devices_information(options := QueryInputDevicesInformationOptions.new()) -> void: IEOS.rtc_audio_interface_query_input_devices_information(options) static func query_output_devices_information(options := QueryOutputDevicesInformationOptions.new()) -> void: IEOS.rtc_audio_interface_query_output_devices_information(options) static func register_platform_user(options: RegisterPlatformUserOptions) -> void: IEOS.rtc_audio_interface_register_platform_user(options) static func remove_notify_audio_before_render(notification_id: int) -> void: IEOS.rtc_audio_interface_remove_notify_audio_before_render(notification_id) static func remove_notify_audio_before_send(notification_id: int) -> void: IEOS.rtc_audio_interface_remove_notify_audio_before_send(notification_id) static func remove_notify_audio_input_state(notification_id: int) -> void: IEOS.rtc_audio_interface_remove_notify_audio_input_state(notification_id) static func remove_notify_audio_output_state(notification_id: int) -> void: IEOS.rtc_audio_interface_remove_notify_audio_output_state(notification_id) static func remove_notify_participant_updated(notification_id: int) -> void: IEOS.rtc_audio_interface_remove_notify_participant_updated(notification_id) static func set_input_device_settings(options: SetInputDeviceSettingsOptions) -> void: IEOS.rtc_audio_interface_set_input_device_settings(options) static func set_output_device_settings(options: SetOutputDeviceSettingsOptions) -> void: IEOS.rtc_audio_interface_set_output_device_settings(options) static func unregister_platform_user(options: UnregisterPlatformUserOptions) -> void: IEOS.rtc_audio_interface_unregister_platform_user(options) static func update_participant_volume(options: UpdateParticipantVolumeOptions) -> void: IEOS.rtc_audio_interface_update_participant_volume(options) static func update_receiving(options: UpdateReceivingOptions) -> void: IEOS.rtc_audio_interface_update_receiving(options) static func update_receiving_volume(options: UpdateReceivingVolumeOptions) -> void: IEOS.rtc_audio_interface_update_receiving_volume(options) static func update_sending(options: UpdateSendingOptions) -> void: IEOS.rtc_audio_interface_update_sending(options) static func update_sending_volume(options: UpdateSendingVolumeOptions) -> void: IEOS.rtc_audio_interface_update_sending_volume(options) class RTCData: const MAX_PACKET_SIZE_BYTES = 1170 enum DataStatus { ## Data unsupported Unsupported = 0, ## Data enabled Enabled = 1, ## Data disabled Disabled = 2 } class AddNotifyDataReceivedOptions extends BaseClass: func _init(): super._init("AddNotifyDataReceivedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class AddNotifyParticipantUpdatedOptions extends BaseClass: func _init(): super._init("AddNotifyParticipantUpdatedOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String class SendDataOptions extends BaseClass: func _init(): super._init("SendDataOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var data: PackedByteArray class UpdateReceivingOptions extends BaseClass: func _init(): super._init("UpdateReceivingOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var participant_id: String var data_enabled: bool class UpdateSendingOptions extends BaseClass: func _init(): super._init("UpdateSendingOptions") var local_user_id = EOSGRuntime.local_product_user_id var room_name: String var data_enabled: bool class RTCDataInterface: static func add_notify_data_received(options: AddNotifyDataReceivedOptions) -> int: return IEOS.rtc_data_interface_add_notify_data_received(options); static func add_notify_participant_updated(options: AddNotifyParticipantUpdatedOptions) -> int: return IEOS.rtc_data_interface_add_notify_participant_updated(options); static func send_data(options: SendDataOptions) -> EOS.Result: return IEOS.rtc_data_interface_send_data(options); static func remove_notify_data_received(notification_id: int) -> void: IEOS.rtc_data_interface_remove_notify_data_received(notification_id) static func remove_notify_participant_updated(notification_id: int) -> void: IEOS.rtc_data_interface_remove_notify_participant_updated(notification_id) static func update_receiving(options: UpdateReceivingOptions) -> void: IEOS.rtc_data_interface_update_receiving(options); static func update_sending(options: UpdateSendingOptions) -> void: IEOS.rtc_data_interface_update_sending(options); class AntiCheatServer: class BeginSessionOptions extends BaseClass: func _init(): super._init("BeginSessionOptions") var register_timeout_seconds := 60 var server_name: String var local_user_id: String var enable_gameplay_data: bool class EndSessionOptions extends BaseClass: func _init(): super._init("EndSessionOptions") class RegisterClientOptions extends BaseClass: func _init(): super._init("RegisterClientOptions") var client_handle: String var client_type: AntiCheatCommonClientType var client_platform: AntiCheatCommonClientPlatform var ip_address: String var user_id: String class UnregisterClientOptions extends BaseClass: func _init(): super._init("UnregisterClientOptions") var client_handle: String class ReceiveMessageFromClientOptions extends BaseClass: func _init(): super._init("ReceiveMessageFromClientOptions") var client_handle: String var data: PackedByteArray class SetClientDetailsOptions extends BaseClass: func _init(): super._init("SetClientDetailsOptions") var client_handle: String var client_flags: AntiCheatCommonClientFlags var client_input: AntiCheatCommonClientInput class SetGameSessionIdOptions extends BaseClass: func _init(): super._init("SetGameSessionIdOptions") var game_session_id: String class SetClientNetworkStateOptions extends BaseClass: func _init(): super._init("SetClientNetworkStateOptions") var client_handle: String var is_network_active: bool class GetProtectMessageOutputLengthOptions extends BaseClass: func _init(): super._init("GetProtectMessageOutputLengthOptions") var data_length_bytes: int class ProtectMessageOptions extends BaseClass: func _init(): super._init("ProtectMessageOptions") var client_handle: String var data: PackedByteArray var out_buffer_size_bytes: int class UnprotectMessageOptions extends BaseClass: func _init(): super._init("UnprotectMessageOptions") var client_handle: String var data: PackedByteArray var out_buffer_size_bytes: int class RegisterEventOptions extends BaseClass: func _init(): super._init("RegisterEventOptions") var event_id: String var event_name: String var event_type: AntiCheatCommonEventType var param_defs: Array # {param_type: AntiCheatCommonEventParamType, param_name: String} class LogEventOptions extends BaseClass: func _init(): super._init("LogEventOptions") var client_handle: String var event_id: String var params: Array # Supported types int, String, Vector3, float, Quaternion class LogGameRoundStartOptions extends BaseClass: func _init(): super._init("LogGameRoundStartOptions") var session_identifier: String var level_name: String var mode_name: String var round_time_seconds: int var competition_type: AntiCheatCommonGameRoundCompetitionType class LogGameRoundEndOptions extends BaseClass: func _init(): super._init("LogGameRoundEndOptions") var winning_team_id: String class LogPlayerSpawnOptions extends BaseClass: func _init(): super._init("LogPlayerSpawnOptions") var spawned_player_handle: String var team_id: String var character_id: String class LogPlayerDespawnOptions extends BaseClass: func _init(): super._init("LogPlayerDespawnOptions") var despawned_player_handle: String class LogPlayerReviveOptions extends BaseClass: func _init(): super._init("LogPlayerReviveOptions") var revived_player_handle: String var reviver_player_handle: String class LogPlayerTickOptions extends BaseClass: func _init(): super._init("LogPlayerTickOptions") var player_handle: String var player_position: Vector3 var player_view_rotation: Quaternion var player_health: float var player_movement_state: AntiCheatCommonPlayerMovementState var player_view_position: Vector3 var is_player_view_zoomed: bool class LogPlayerUseWeaponOptions extends BaseClass: func _init(): super._init("LogPlayerUseWeaponOptions") var player_handle: String var player_position: Vector3 var player_view_rotation: Quaternion var weapon_name: String var is_player_view_zoomed: bool var is_melee_attacK: bool class LogPlayerUseAbilityOptions extends BaseClass: func _init(): super._init("LogPlayerUseAbilityOptions") var player_handle: String var ability_id: int var ability_duration_ms: int var ability_cooldown_ms: int class LogPlayerTakeDamageOptions extends BaseClass: func _init(): super._init("LogPlayerTakeDamageOptions") var victim_player_handle: String var victim_player_position: Vector3 var victim_player_view_rotation: Quaternion var attacker_player_handle: String var attacker_player_position: Vector3 var attacker_player_view_rotation: Quaternion var damage_taken: float var health_remaining: float var damage_source: AntiCheatCommonPlayerTakeDamageSource var damage_type: AntiCheatCommonPlayerTakeDamageType var damage_result: AntiCheatCommonPlayerTakeDamageResult var tim_since_player_use_weapon_ms: int var damage_position: Vector3 var attacked_player_view_position: Vector3 var player_use_weapon_data: LogPlayerUseWeaponOptions var is_hitscan_attack: bool var has_line_of_sight: bool var is_critical_hit: bool class AntiCheatServerInterface: static func begin_session(options: BeginSessionOptions) -> EOS.Result: return IEOS.anticheat_server_interface_begin_session(options) static func end_session() -> EOS.Result: return IEOS.anticheat_server_interface_end_session(EndSessionOptions.new()) static func register_client(options: RegisterClientOptions) -> EOS.Result: return IEOS.anticheat_server_interface_register_client(options) static func unregister_client(options: UnregisterClientOptions) -> EOS.Result: return IEOS.anticheat_server_interface_unregister_client(options) static func receive_message_from_client(options: ReceiveMessageFromClientOptions) -> EOS.Result: return IEOS.anticheat_server_interface_receive_message_from_client(options) static func set_client_details(options: SetClientDetailsOptions) -> EOS.Result: return IEOS.anticheat_server_interface_set_client_details(options) static func set_game_session_id(options: SetGameSessionIdOptions) -> EOS.Result: return IEOS.anticheat_server_interface_set_game_session_id(options) static func set_client_network_state(options: SetClientNetworkStateOptions) -> EOS.Result: return IEOS.anticheat_server_interface_set_client_network_state(options) static func get_protect_message_output_length(options: GetProtectMessageOutputLengthOptions) -> Dictionary: return IEOS.anticheat_server_interface_get_protect_message_output_length(options) static func protect_message(options: ProtectMessageOptions) -> Dictionary: return IEOS.anticheat_server_interface_protect_message(options) static func unprotect_message(options: UnprotectMessageOptions) -> Dictionary: return IEOS.anticheat_server_interface_unprotect_message(options) static func register_event(options: RegisterEventOptions) -> EOS.Result: return IEOS.anticheat_server_interface_register_event(options) static func log_event(options: LogEventOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_event(options) static func log_game_round_start(options: LogGameRoundStartOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_game_round_start(options) static func log_game_round_end(options: LogGameRoundEndOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_game_round_end(options) static func log_player_spawn(options: LogPlayerSpawnOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_spawn(options) static func log_player_despawn(options: LogPlayerDespawnOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_despawn(options) static func log_player_revive(options: LogPlayerReviveOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_revive(options) static func log_player_tick(options: LogPlayerTickOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_tick(options) static func log_player_use_weapon(options: LogPlayerUseWeaponOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_use_weapon(options) static func log_player_use_ability(options: LogPlayerUseAbilityOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_use_ability(options) static func log_player_take_damage(options: LogPlayerTakeDamageOptions) -> EOS.Result: return IEOS.anticheat_server_interface_log_player_take_damage(options) class AntiCheatClient: class BeginSessionOptions extends BaseClass: func _init(): super._init("BeginSessionOptions") var local_user_id: String var mode: AntiCheatClientMode class EndSessionOptions extends BaseClass: func _init(): super._init("EndSessionOptions") class AddExternalIntegrityCatalogOptions extends BaseClass: func _init(): super._init("AddExternalIntegrityCatalogOptions") var path_to_file_bin: String class ReceiveMessageFromServerOptions extends BaseClass: func _init(): super._init("ReceiveMessageFromServerOptions") var data: PackedByteArray class GetProtectMessageOutputLengthOptions extends BaseClass: func _init(): super._init("GetProtectMessageOutputLengthOptions") var data_length_bytes: int class ProtectMessageOptions extends BaseClass: func _init(): super._init("ProtectMessageOptions") var data: PackedByteArray var out_buffer_size_bytes: int class UnprotectMessageOptions extends BaseClass: func _init(): super._init("UnprotectMessageOptions") var data: PackedByteArray var out_buffer_size_bytes: int class RegisterPeerOptions extends BaseClass: func _init(): super._init("RegisterPeerOptions") var peer_handle: String var client_type: AntiCheatCommonClientType var client_platform: AntiCheatCommonClientPlatform var authentication_timeout: int = 60 var ip_address: String var peer_product_user_id: String class UnregisterPeerOptions extends BaseClass: func _init(): super._init("UnregisterPeerOptions") var peer_handle: String class ReceiveMessageFromPeerOptions extends BaseClass: func _init(): super._init("ReceiveMessageFromPeerOptions") var peer_handle: String var data: PackedByteArray class AntiCheatClientInterface: static func begin_session(options: BeginSessionOptions) -> EOS.Result: return IEOS.anticheat_client_interface_begin_session(options) static func end_session() -> EOS.Result: return IEOS.anticheat_client_interface_end_session(EndSessionOptions.new()) static func add_external_integrity_catalog(options: AddExternalIntegrityCatalogOptions) -> EOS.Result: return IEOS.anticheat_client_interface_add_external_integrity_catalog(options) static func receive_message_from_server(options: ReceiveMessageFromServerOptions) -> EOS.Result: return IEOS.anticheat_client_interface_receive_message_from_server(options) static func get_protect_message_output_length(options: GetProtectMessageOutputLengthOptions) -> Dictionary: return IEOS.anticheat_client_interface_get_protect_message_output_length(options) static func protect_message(options: ProtectMessageOptions) -> Dictionary: return IEOS.anticheat_client_interface_protect_message(options) static func unprotect_message(options: UnprotectMessageOptions) -> Dictionary: return IEOS.anticheat_client_interface_unprotect_message(options) static func register_peer(options: RegisterPeerOptions) -> EOS.Result: return IEOS.anticheat_client_interface_register_peer(options) static func unregister_peer(options: UnregisterPeerOptions) -> EOS.Result: return IEOS.anticheat_client_interface_unregister_peer(options) static func receive_message_from_peer(options: ReceiveMessageFromPeerOptions) -> EOS.Result: return IEOS.anticheat_client_interface_receive_message_from_peer(options) class Version: class VersionInterface: static func get_version() -> String: return IEOS.version_interface_get_version() static func get_constants() -> Dictionary: return IEOS.version_interface_get_constants() const NotificationIdInvalid = 0 enum Result { Success = 0, NoConnection = 1, InvalidCredentials = 2, InvalidUser = 3, InvalidAuth = 4, AccessDenied = 5, MissingPermissions = 6, TokenNotAccount = 7, TooManyRequests = 8, AlreadyPending = 9, InvalidParameters = 10, InvalidRequest = 11, UnrecognizedResponse = 12, IncompatibleVersion = 13, NotConfigured = 14, AlreadyConfigured = 15, NotImplemented = 16, Canceled = 17, NotFound = 18, OperationWillRetry = 19, NoChange = 20, VersionMismatch = 21, LimitExceeded = 22, Disabled = 23, DuplicateNotAllowed = 24, InvalidSandboxId = 26, TimedOut = 27, PartialResult = 28, MissingRole = 29, MissingFeature = 30, InvalidSandbox = 31, InvalidDeployment = 32, InvalidProduct = 33, InvalidProductUserID = 34, ServiceFailure = 35, CacheDirectoryMissing = 36, CacheDirectoryInvalid = 37, InvalidState = 38, RequestInProgress = 39, ApplicationSuspended = 40, NetworkDisconnected = 41, InsufficientOutputBuffer = 42, AuthAccountLocked = 1001, AuthAccountLockedForUpdate = 1002, AuthInvalidRefreshToken = 1003, AuthInvalidToken = 1004, AuthAuthenticationFailure = 1005, AuthInvalidPlatformToken = 1006, AuthWrongAccount = 1007, AuthWrongClient = 1008, AuthFullAccountRequired = 1009, AuthHeadlessAccountRequired = 1010, AuthPasswordResetRequired = 1011, AuthPasswordCannotBeReused = 1012, AuthExpired = 1013, AuthScopeConsentRequired = 1014, AuthApplicationNotFound = 1015, AuthScopeNotFound = 1016, AuthAccountFeatureRestricted = 1017, AuthAccountPortalLoadError = 1018, AuthCorrectiveActionRequired = 1019, AuthPinGrantCode = 1020, AuthPinGrantExpired = 1021, AuthPinGrantPending = 1022, AuthExternalAuthNotLinked = 1030, AuthExternalAuthRevoked = 1032, AuthExternalAuthInvalid = 1033, AuthExternalAuthRestricted = 1034, AuthExternalAuthCannotLogin = 1035, AuthExternalAuthExpired = 1036, AuthExternalAuthIsLastLoginType = 1037, AuthExchangeCodeNotFound = 1040, AuthOriginatingExchangeCodeSessionExpired = 1041, AuthAccountNotActive = 1050, AuthMFARequired = 1060, AuthParentalControls = 1070, AuthNoRealId = 1080, AuthUserInterfaceRequired = 1090, FriendsInviteAwaitingAcceptance = 2000, FriendsNoInvitation = 2001, FriendsAlreadyFriends = 2003, FriendsNotFriends = 2004, FriendsTargetUserTooManyInvites = 2005, FriendsLocalUserTooManyInvites = 2006, FriendsTargetUserFriendLimitExceeded = 2007, FriendsLocalUserFriendLimitExceeded = 2008, PresenceDataInvalid = 3000, PresenceDataLengthInvalid = 3001, PresenceDataKeyInvalid = 3002, PresenceDataKeyLengthInvalid = 3003, PresenceDataValueInvalid = 3004, PresenceDataValueLengthInvalid = 3005, PresenceRichTextInvalid = 3006, PresenceRichTextLengthInvalid = 3007, PresenceStatusInvalid = 3008, PresenceRichTextNotSupported = 3009, PresenceTemplateNotSupported = 3010, PresenceTemplateIdInvalid = 3011, PresenceTemplateTypeInvalid = 3012, PresenceTemplateKeyInvalid = 3013, PresenceTemplateValueInvalid = 3014, PresenceTemplateNotFound = 3015, PresenceTemplateInvalidVariableInput = 3016, PresenceTemplateLocalizationServerError = 3017, PresenceTemplateUnknownError = 3018, EcomEntitlementStale = 4000, EcomCatalogOfferStale = 4001, EcomCatalogItemStale = 4002, EcomCatalogOfferPriceInvalid = 4003, EcomCheckoutLoadError = 4004, EcomPurchaseProcessing = 4005, SessionsSessionInProgress = 5000, SessionsTooManyPlayers = 5001, SessionsNoPermission = 5002, SessionsSessionAlreadyExists = 5003, SessionsInvalidLock = 5004, SessionsInvalidSession = 5005, SessionsSandboxNotAllowed = 5006, SessionsInviteFailed = 5007, SessionsInviteNotFound = 5008, SessionsUpsertNotAllowed = 5009, SessionsAggregationFailed = 5010, SessionsHostAtCapacity = 5011, SessionsSandboxAtCapacity = 5012, SessionsSessionNotAnonymous = 5013, SessionsOutOfSync = 5014, SessionsTooManyInvites = 5015, SessionsPresenceSessionExists = 5016, SessionsDeploymentAtCapacity = 5017, SessionsNotAllowed = 5018, SessionsPlayerSanctioned = 5019, PlayerDataStorageFilenameInvalid = 6000, PlayerDataStorageFilenameLengthInvalid = 6001, PlayerDataStorageFilenameInvalidChars = 6002, PlayerDataStorageFileSizeTooLarge = 6003, PlayerDataStorageFileSizeInvalid = 6004, PlayerDataStorageFileHandleInvalid = 6005, PlayerDataStorageDataInvalid = 6006, PlayerDataStorageDataLengthInvalid = 6007, PlayerDataStorageStartIndexInvalid = 6008, PlayerDataStorageRequestInProgress = 6009, PlayerDataStorageUserThrottled = 6010, PlayerDataStorageEncryptionKeyNotSet = 6011, PlayerDataStorageUserErrorFromDataCallback = 6012, PlayerDataStorageFileHeaderHasNewerVersion = 6013, PlayerDataStorageFileCorrupted = 6014, ConnectExternalTokenValidationFailed = 7000, ConnectUserAlreadyExists = 7001, ConnectAuthExpired = 7002, ConnectInvalidToken = 7003, ConnectUnsupportedTokenType = 7004, ConnectLinkAccountFailed = 7005, ConnectExternalServiceUnavailable = 7006, ConnectExternalServiceConfigurationFailure = 7007, SocialOverlayLoadError = 8000, InconsistentVirtualMemoryFunctions = 8001, LobbyNotOwner = 9000, LobbyInvalidLock = 9001, LobbyLobbyAlreadyExists = 9002, LobbySessionInProgress = 9003, LobbyTooManyPlayers = 9004, LobbyNoPermission = 9005, LobbyInvalidSession = 9006, LobbySandboxNotAllowed = 9007, LobbyInviteFailed = 9008, LobbyInviteNotFound = 9009, LobbyUpsertNotAllowed = 9010, LobbyAggregationFailed = 9011, LobbyHostAtCapacity = 9012, LobbySandboxAtCapacity = 9013, LobbyTooManyInvites = 9014, LobbyDeploymentAtCapacity = 9015, LobbyNotAllowed = 9016, LobbyMemberUpdateOnly = 9017, LobbyPresenceLobbyExists = 9018, LobbyVoiceNotEnabled = 9019, LobbyPlatformNotAllowed = 9020, TitleStorageUserErrorFromDataCallback = 10000, TitleStorageEncryptionKeyNotSet = 10001, TitleStorageFileCorrupted = 10002, TitleStorageFileHeaderHasNewerVersion = 10003, ModsModSdkProcessIsAlreadyRunning = 11000, ModsModSdkCommandIsEmpty = 11001, ModsModSdkProcessCreationFailed = 11002, ModsCriticalError = 11003, ModsToolInternalError = 11004, ModsIPCFailure = 11005, ModsInvalidIPCResponse = 11006, ModsURILaunchFailure = 11007, ModsModIsNotInstalled = 11008, ModsUserDoesNotOwnTheGame = 11009, ModsOfferRequestByIdInvalidResult = 11010, ModsCouldNotFindOffer = 11011, ModsOfferRequestByIdFailure = 11012, ModsPurchaseFailure = 11013, ModsInvalidGameInstallInfo = 11014, ModsCannotGetManifestLocation = 11015, ModsUnsupportedOS = 11016, AntiCheatClientProtectionNotAvailable = 12000, AntiCheatInvalidMode = 12001, AntiCheatClientProductIdMismatch = 12002, AntiCheatClientSandboxIdMismatch = 12003, AntiCheatProtectMessageSessionKeyRequired = 12004, AntiCheatProtectMessageValidationFailed = 12005, AntiCheatProtectMessageInitializationFailed = 12006, AntiCheatPeerAlreadyRegistered = 12007, AntiCheatPeerNotFound = 12008, AntiCheatPeerNotProtected = 12009, AntiCheatClientDeploymentIdMismatch = 12010, AntiCheatDeviceIdAuthIsNotSupported = 12011, TooManyParticipants = 13000, RoomAlreadyExists = 13001, UserKicked = 13002, UserBanned = 13003, RoomWasLeft = 13004, ReconnectionTimegateExpired = 13005, ShutdownInvoked = 13006, UserIsInBlocklist = 13007, AllocationFailed = 13009, VoiceModerationModeMismatch = 13010, EmptyRecord = 13011, RoomOptionsMismatch = 13012, ProgressionSnapshotSnapshotIdUnavailable = 14000, ParentEmailMissing = 15000, UserGraduated = 15001, AndroidJavaVMNotStored = 17000, ReservedMustReferenceLocalVM = 17001, ReservedMustBeNull = 17002, PermissionRequiredPatchAvailable = 18000, PermissionRequiredSystemUpdate = 18001, PermissionAgeRestrictionFailure = 18002, PermissionAccountTypeFailure = 18003, PermissionChatRestriction = 18004, PermissionUGCRestriction = 18005, PermissionOnlinePlayRestricted = 18006, DesktopCrossplayApplicationNotBootstrapped = 19000, DesktopCrossplayServiceNotInstalled = 19001, DesktopCrossplayServiceStartFailed = 19002, DesktopCrossplayServiceNotRunning = 19003, CustomInvitesInviteFailed = 20000, UserInfoBestDisplayNameIndeterminate = 22000, OnNetworkRequestedDeprecatedCallbackNotSet = 23000, CacheStorageSizeKBNotMultipleOf16 = 23001, CacheStorageSizeKBBelowMinimumSize = 23002, CacheStorageSizeKBExceedsMaximumSize = 23003, CacheStorageIndexOutOfRangeRange = 23004, UnexpectedError = 0x7FFFFFFF } enum ExternalAccountType { Epic = 0, Steam = 1, Psn = 2, Xbl = 3, Discord = 4, Gog = 5, Nintendo = 6, Uplay = 7, Openid = 8, Apple = 9, Google = 10, Oculus = 11, Itchio = 12, Amazon = 13, Viveport = 14, } enum ExternalCredentialType { None = -1, Epic = 0, SteamAppTicket = 1, PsnIdToken = 2, XblXstsToken = 3, DiscordAccessToken = 4, GogSessionTicket = 5, NintendoIdToken = 6, NintendoNsaIdToken = 7, UplayAccessToken = 8, OpenidAccessToken = 9, DeviceidAccessToken = 10, AppleIdToken = 11, GoogleIdToken = 12, OculusUseridNonce = 13, ItchioJwt = 14, ItchioKey = 15, EpicIdToken = 16, AmazonAccessToken = 17, SteamSessionTicket = 18, ViveportUserToken = 19, } enum OnlinePlatformType { Unknown = 0, Epic = 100, PSN = 1000, Nintendo = 2000, XBL = 3000, Steam = 4000 } enum LoginStatus { NotLoggedIn = 0, UsingLocalProfile = 1, LoggedIn = 2 } enum AttributeType { Boolean = 0, Int64 = 1, Double = 2, String = 3 } enum ComparisonOp { Equal = 0, NotEqual = 1, GreaterThan = 2, GreaterThanOrEqual = 3, LessThan = 4, LessThanOrEqual = 5, Distance = 6, AnyOf = 7, NotAnyOf = 8, OneOf = 9, NotOneOf = 10, Contains = 11, RegexMatch = 12, Size = 13 } enum LogicalCombineOp { And = 0, Or = 1 } enum AntiCheatCommonClientType { ProtectedClient = 0, UnprotectedClient = 1, AIBot = 2 } enum AntiCheatCommonGameRoundCompetitionType { None = 0, Casual = 1, Ranked = 2, Competitive = 3 } enum AntiCheatCommonClientPlatform { Unknown = 0, Windows = 1, Mac = 2, Linux = 3, Xbox = 4, PlayStation = 5, Nintendo = 6, iOS = 7, Android = 8, } enum AntiCheatCommonClientAction { Invalid = 0, RemovePlayer = 1, } enum AntiCheatCommonClientActionReason { Invalid = 0, InternalError = 1, InvalidMessage = 2, AuthenticationFailed = 3, NullClient = 4, HeartbeatTimeout = 5, ClientViolation = 6, BackendViolation = 7, TemporaryCooldown = 8, TemporaryBanned = 9, PermanentBanned = 10, } enum AntiCheatCommonClientAuthStatus { Invalid = 0, LocalAuthComplete = 1, RemoteAuthComplete = 2, } enum AntiCheatCommonClientFlags { None = 0, Admin = (1 << 0), } enum AntiCheatCommonClientInput { Unknown = 0, MouseKeyboard = 1, Gamepad = 2, TouchInput = 3, } enum AntiCheatCommonEventType { Invalid = 0, GameEvent = 1, PlayerEvent = 2, } enum AntiCheatCommonEventParamType { Invalid = 0, ClientHandle = 1, String = 2, UInt32 = 3, Int32 = 4, UInt64 = 5, Int64 = 6, Vector3f = 7, Quat = 8, Float = 9, } enum AntiCheatCommonPlayerMovementState { None = 0, Crouching = 1, Prone = 2, Mounted = 3, Swimming = 4, Falling = 5, Flying = 6, OnLadder = 7, } enum AntiCheatCommonPlayerTakeDamageSource { None = 0, Player = 1, NonPlayerCharacter = 2, World = 3, } enum AntiCheatCommonPlayerTakeDamageType { None = 0, PointDamage = 1, RadialDamage = 2, DamageOverTime = 3, } enum AntiCheatCommonPlayerTakeDamageResult { None = 0, Downed_DEPRECATED = 1, Eliminated_DEPRECATED = 2, NormalToDowned = 3, NormalToEliminated = 4, DownedToEliminated = 5 } enum AntiCheatClientMode { Invalid = 0, ClientServer = 1, PeerToPeer = 2, } ================================================ FILE: sample/addons/epic-online-services-godot/eos.gd.uid ================================================ uid://idtj8sepvkjw ================================================ FILE: sample/addons/epic-online-services-godot/eosg.gdextension ================================================ [configuration] entry_symbol = "eosg_library_init" compatibility_minimum = 4.1 [libraries] linux.debug.x86_64 = "bin/linux/libeosg.linux.template_debug.dev.x86_64.so" linux.release.x86_64 = "bin/linux/libeosg.linux.template_release.x86_64.so" linux.debug.arm64 = "bin/linux/libeosg.linux.template_debug.dev.arm64.so" linux.release.arm64 = "bin/linux/libeosg.linux.template_release.arm64.so" windows.debug.x86_64 = "bin/windows/libeosg.windows.template_debug.dev.x86_64.dll" windows.release.x86_64 = "bin/windows/libeosg.windows.template_release.x86_64.dll" macos.debug = "bin/macos/libeosg.macos.template_debug.framework" macos.release = "bin/macos/libeosg.macos.template_release.framework" ios.debug = "bin/ios/libeosg.ios.template_debug.xcframework" ios.release = "bin/ios/libeosg.ios.template_release.xcframework" android.debug.arm64 = "bin/android/libeosg.android.template_debug.dev.arm64.so" android.release.arm64 = "bin/android/libeosg.android.template_release.arm64.so" android.debug.x86_64 = "bin/android/libeosg.android.template_debug.dev.x86_64.so" android.release.x86_64 = "bin/android/libeosg.android.template_release.x86_64.so" [dependencies] linux.debug.x86_64 = {"bin/linux/libEOSSDK-Linux-Shipping.so": ""} linux.release.x86_64 = {"bin/linux/libEOSSDK-Linux-Shipping.so": ""} linux.debug.arm64 = {"bin/linux/libEOSSDK-LinuxArm64-Shipping.so": ""} linux.release.arm64 = {"bin/linux/libEOSSDK-LinuxArm64-Shipping.so": ""} windows.debug.x86_64 = {"bin/windows/EOSSDK-Win64-Shipping.dll": "", "bin/windows/x64/xaudio2_9redist.dll": ""} windows.release.x86_64 = {"bin/windows/EOSSDK-Win64-Shipping.dll": "", "bin/windows/x64/xaudio2_9redist.dll": ""} macos.debug = {"bin/macos/libeosg.macos.template_debug.framework/libEOSSDK-Mac-Shipping.dylib": ""} macos.release = {"bin/macos/libeosg.macos.template_release.framework/libEOSSDK-Mac-Shipping.dylib": ""} ios.debug = {"bin/ios/libgodot-cpp.ios.template_debug.xcframework": "", "bin/ios/EOSSDK.xcframework": ""} ios.release = {"bin/ios/libgodot-cpp.ios.template_release.xcframework": "", "bin/ios/EOSSDK.xcframework": ""} ================================================ FILE: sample/addons/epic-online-services-godot/eosg.gdextension.uid ================================================ uid://dwyvkkxk32w36 ================================================ FILE: sample/addons/epic-online-services-godot/export_plugin.gd ================================================ # Copyright (c) 2023-present Delano Lourenco # https://github.com/3ddelano/epic-online-services-godot/ # MIT License @tool extends EditorExportPlugin func _get_name() -> String: return "Epic Online Services Godot 4.2+ (EOSG)" func _export_begin(features: PackedStringArray, is_debug: bool, path: String, flags: int): var target_platform = features[2] match target_platform: "windows": add_shared_object("res://addons/epic-online-services-godot/bin/windows/EOSSDK-Win64-Shipping.dll", [], "/") add_shared_object("res://addons/epic-online-services-godot/bin/windows/x64/xaudio2_9redist.dll", [], "/") "linux": add_shared_object("res://addons/epic-online-services-godot/bin/linux/libEOSSDK-Linux-Shipping.so", [], "/") "macos": add_shared_object("res://addons/epic-online-services-godot/bin/macos/libEOSSDK-Mac-Shipping.dylib", [], "/") ================================================ FILE: sample/addons/epic-online-services-godot/export_plugin.gd.uid ================================================ uid://bydaf5hf76858 ================================================ FILE: sample/addons/epic-online-services-godot/heos/hachievement_data.gd ================================================ ## Achievement data from Epic Online Services class_name HAchievementData extends BaseClass func _init() -> void: super._init("HAchievementData") ## Achievement ID that can be used to uniquely identify the achievement. var achievement_id: String ## Localized display name for the achievement when it has been unlocked. var unlocked_display_name: String ## Localized description for the achievement when it has been unlocked. var unlocked_description: String ## URL of an icon to display for the achievement when it is unlocked. This may be null if there is no data configured in the dev portal. var unlocked_icon_url: String ## Localized display name for the achievement when it is locked or hidden. var locked_display_name: String ## Localized description for the achievement when it is locked or hidden. var locked_description: String ## URL of an icon to display for the achievement when it is locked or hidden. This may be null if there is no data configured in the dev portal. var locked_icon_url: String ## Localized flavor text that can be used by the game in an arbitrary manner. This may be null if there is no data configured in the dev portal. var flavor_text: String ## Whether the achievement is hidden or not var is_hidden: bool ## Progress towards completing this achievement (as a percentage). var progress := 0.0 ## Either [EOS.Achievements.UNLOCK_TIME_UNDEFINED] if not unlocked or the unix timestamp of when the achievement was unlocked by the user var unlock_time := EOS.Achievements.UNLOCK_TIME_UNDEFINED ## Array of [Dictionary] having keys: name:String, current_value:int, threshold_value:int. var stats = [] ## Whether this achievement is unlocked func is_unlocked() -> bool: return unlock_time != EOS.Achievements.UNLOCK_TIME_UNDEFINED ## Get the appropriate display name based on whether the achievement is unlocked or not func get_display_name() -> String: if is_unlocked(): return unlocked_display_name return locked_display_name ## Get the appropriate description based on whether the achievement is unlocked or not func get_description() -> String: if is_unlocked(): return unlocked_description return locked_description ## Get the appropriate icon URL based on whether the achievement is unlocked or not func get_icon_url() -> String: if is_unlocked(): return unlocked_icon_url return locked_icon_url func update_stat(p_stat: Dictionary) -> void: var found = false for i in stats.size(): var stat = stats[i] if stat.name == p_stat.name: stats[i] = p_stat found = true break if not found: stats.append(p_stat) ================================================ FILE: sample/addons/epic-online-services-godot/heos/hachievement_data.gd.uid ================================================ uid://we6ap3yuu378 ================================================ FILE: sample/addons/epic-online-services-godot/heos/hachievements.gd ================================================ extends Node #region Signals ## Emitted when an achievement is unlocked ## data has the following keys: achievement_id:String, unlock_time:int, user_id:String signal achievement_unlocked(data: Dictionary) #endregion #region Public vars #endregion #region Private vars var _log = HLog.logger("HAchievements") var _achievements: Array[HAchievementData]= [] #endregion #region Built-in methods func _ready() -> void: IEOS.achievements_interface_achievements_unlocked_v2_callback.connect(_on_achievements_interface_achievements_unlocked_v2_callback) #endregion #region Public methods ## Returns an [Array] of [HAchievementData] representing all the achievements in the game func get_all_achievements_async() -> Array[HAchievementData]: _log.debug("Getting all achievements...") var opts = EOS.Achievements.QueryDefinitionsOptions.new() EOS.Achievements.AchievementsInterface.query_definitions(opts) var ret = await IEOS.achievements_interface_query_definitions_callback if not EOS.is_success(ret): _log.error("Failed to query achievements: result_code=%s" % EOS.result_str(ret)) return [] var get_count_opts = EOS.Achievements.GetAchievementDefinitionCountOptions.new() var achievement_count: int = EOS.Achievements.AchievementsInterface.get_achievement_definition_count(get_count_opts) _log.debug("Got all achievements: count=%s" % achievement_count) var achievements: Array[HAchievementData] = [] for i in achievement_count: var copy_opts = EOS.Achievements.CopyAchievementDefinitionV2ByIndexOptions.new() copy_opts.achievement_index = i var copy_ret = EOS.Achievements.AchievementsInterface.copy_achievement_definition_v2_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy achievement: result_code=%s" % EOS.result_str(copy_ret)) continue var definition: Dictionary = copy_ret.definition_v2 var data = HAchievementData.new() var stat_thresholds = definition.stat_thresholds definition.erase("stat_thresholds") data.from_dict(definition) for threshold_data in stat_thresholds: var stat = {name = threshold_data.name, current_value = 0, threshold_value = threshold_data.threshold} data.update_stat(stat) achievements.append(data) _achievements = achievements return achievements ## Returns an [Array] of [HAchievementData] representing all the player's achievements func get_player_achievements_async() -> Array[HAchievementData]: _log.debug("Getting player achievements...") var opts = EOS.Achievements.QueryPlayerAchievementsOptions.new() EOS.Achievements.AchievementsInterface.query_player_achievements(opts) var ret = await IEOS.achievements_interface_query_player_achievements_callback if not EOS.is_success(ret): _log.error("Failed to query player achievements: result_code=%s" % EOS.result_str(ret)) return [] var get_count_opts = EOS.Achievements.GetPlayerAchievementCountOptions.new() get_count_opts.user_id = HAuth.product_user_id var achievements_count = EOS.Achievements.AchievementsInterface.get_player_achievement_count(get_count_opts) _log.debug("Got player achievements: count=%s" % achievements_count) var achievements: Array[HAchievementData] = [] for i in achievements_count: var copy_opts = EOS.Achievements.CopyPlayerAchievementByIndexOptions.new() copy_opts.achievement_index = i copy_opts.target_user_id = HAuth.product_user_id var copy_ret = EOS.Achievements.AchievementsInterface.copy_player_achievement_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy player achievement: result_code=%s" % EOS.result_str(copy_ret)) continue var achievement_data = copy_ret.player_achievement var cached_achievement = null for achievement in _achievements: if achievement.achievement_id == achievement_data.achievement_id: cached_achievement = achievement var data: HAchievementData = HAchievementData.new() if cached_achievement == null else cached_achievement data.progress = achievement_data.progress data.unlock_time = achievement_data.unlock_time for stat in achievement_data.stat_infos: data.update_stat(stat) if cached_achievement == null: data.achievement_id = achievement_data.achievement_id data.flavor_text = achievement_data.flavor_text if data.is_unlocked(): data.unlocked_display_name = achievement_data.display_name data.unlocked_description = achievement_data.description data.unlocked_icon_url = achievement_data.icon_url else: data.locked_display_name = achievement_data.display_name data.locked_description = achievement_data.description data.locked_icon_url = achievement_data.icon_url achievements.append(data) return achievements func unlock_achievement_async(achievement_id: String) -> bool: _log.debug("Unlocking single achievement: achievement_id=%s" % achievement_id) return await unlock_achievements_async([achievement_id]) func unlock_achievements_async(achievement_ids: Array) -> bool: _log.debug("Unlocking multiple achievement(s): achievement_ids=%s" % str(achievement_ids)) var opts = EOS.Achievements.UnlockAchievementsOptions.new() opts.user_id = HAuth.product_user_id opts.achievement_ids = achievement_ids EOS.Achievements.AchievementsInterface.unlock_achievements(opts) var res: Dictionary = await IEOS.achievements_interface_unlock_achievements_callback if not EOS.is_success(res): _log.error("Failed to unlock achievement(s): result_code=%s" % EOS.result_str(res)) return false _log.debug("Unlocked %s achievement(s)" % res.achievements_count) return true #endregion #region Private methods func _on_achievements_interface_achievements_unlocked_v2_callback(data: Dictionary): _log.verbose("Got Achievement unlocked v2 callback") achievement_unlocked.emit(data) #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hachievements.gd.uid ================================================ uid://bis404dty5u5c ================================================ FILE: sample/addons/epic-online-services-godot/heos/hauth.gd ================================================ # Good article about the EOS login flows: https://eoshelp.epicgames.com/s/article/What-is-the-correct-login-flow-for-a-game-that-supports-crossplay extends Node #region Signals ## Emitted when the user logs in to Epic Game Services. If the [auto_connect_account] option is false, this will also be emitted when the user logs in to Epic Account Services signal logged_in ## Emitted when the user logs out of Epic Game Services signal logged_out ## Emitted when an error occurs while loggin in signal login_error(result_code: EOS.Result) ## Related to Epic Account Services signal logged_in_auth ## Related to Epic Account Services signal logged_out_auth ## Related to Epic Account Services signal login_auth_error(result_code: EOS.Result) ## Related to Epic Game Services signal logged_in_connect ## Related to Epic Game Services signal logged_out_connect ## Related to Epic Game Services signal login_connect_error(result_code: EOS.Result) ## Emitted when the display name is changed. Get the display name using HAuth.display_name. ## When user logs in, this event will be emitted when we receive the user's display name. ## When user logs out, this event will be emitted as display name will be empty string. signal display_name_changed ## Emitted when the external account info is changed. ## When user logs in, this event will be emitted when we receive the user's external account info. ## When user logs out, this event will be emitted as external account info will be empty. signal external_account_info_changed #endregion #region Public vars ## The epic account id of the logged in user (Used for Epic Account Services) var epic_account_id := "" ## The product user id of the logged in user (Used for Epic Game Services) var product_user_id := "" ## The display name of the logged in user var display_name: String ## Whether to automatically fetch the external account linked with Epic Game Services (default true) var auto_fetch_external_account := true ## The external account linked with Epic Game Services ## See [method get_external_account_by_type_async] for return type var external_account_info := {} ## Whether to automatically link an epic account for external identity provider (default true) var auto_link_account := true ## Whether to automatically login to Epic Game Services after logging in to Epic Account Services (default true) var auto_connect_account := true ## Default scope flags used when logging in with Epic Account Services[br] ## Flags from [enum EOS.Auth.ScopeFlags] var auth_login_scope_flags: int = EOS.Auth.ScopeFlags.BasicProfile | EOS.Auth.ScopeFlags.Presence | EOS.Auth.ScopeFlags.FriendsList ## Default login flags used when logging in with Epic Account Services.[br] ## Flags from [enum EOS.Auth.LoginFlags] var auth_login_flags: int = EOS.Auth.LoginFlags.None #endregion #region Private vars var _log = HLog.logger("HAuth") var _last_connect_login_opts: EOS.Connect.LoginOptions #endregion #region Built-in methods func _ready() -> void: IEOS.connect_interface_auth_expiration.connect(_on_connect_interface_auth_expiration) #endregion #region Public methods ## Login using the Epic Dev Auth tool func login_devtool_async(server_url: String, credential_name: String) -> bool: _log.debug("Logging in using Epic Dev Auth tool...") var opts = EOS.Auth.LoginOptions.new() opts.credentials = EOS.Auth.Credentials.new() opts.credentials.type = EOS.Auth.LoginCredentialType.Developer opts.credentials.id = server_url opts.credentials.token = credential_name opts.scope_flags = auth_login_scope_flags opts.login_flags = auth_login_flags return await login_async(opts) ## Login using Epic Account Portal func login_account_portal_async() -> bool: _log.debug("Logging in using Epic Account Portal...") var opts = EOS.Auth.LoginOptions.new() opts.credentials = EOS.Auth.Credentials.new() opts.credentials.type = EOS.Auth.LoginCredentialType.AccountPortal opts.scope_flags = auth_login_scope_flags opts.login_flags = auth_login_flags return await login_async(opts) ## Login using credentials provided by the Epic Games Launcher[br] ## To test this locally provide the cli argument -AUTH_PASSWORD= when running your game like [code]godot4 . -AUTH_PASSWORD=1234[/code][br] ## You can generate an exchange code by using the DevAuthTool and accessing the following link on a browser: [code]http://localhost://exchange_code[/code] func login_launcher_async() -> bool: _log.debug("Logging in using Epic Games Launcher...") var cli_opts = _get_command_line_options() var auth_password = cli_opts.get("AUTH_PASSWORD", "") if "" == auth_password: _log.error("Missing -AUTH_PASSWORD= cli argument. Please see usage docs.") return false var opts = EOS.Auth.LoginOptions.new() opts.credentials = EOS.Auth.Credentials.new() opts.credentials.type = EOS.Auth.LoginCredentialType.ExchangeCode opts.credentials.token = auth_password opts.scope_flags = auth_login_scope_flags opts.login_flags = auth_login_flags return await login_async(opts) ## Login using EOS Auth by either using an Identity provider or Epic games Account. ## Allows you to use Epic Account Services: Friends, Presence, Social Overlay, ECom, etc. ## This is the recommended way of logging in as you get many additional features compared to [login_game_services_async] func login_async(opts: EOS.Auth.LoginOptions) -> bool: _log.debug("Logging into Epic Account Services (AuthInterface)...") EOS.Auth.AuthInterface.login(opts) var auth_login_ret: Dictionary = await IEOS.auth_interface_login_callback var auth_res: EOS.Result = auth_login_ret.result_code if auth_res == EOS.Result.AuthMFARequired: _log.error("Auth requires MFA - This is not supported by the EOS SDK. Please use an account without MFA or use an alternative login method") _emit_login_auth_error(auth_res) return false if auth_res == EOS.Result.InvalidUser: if not auth_login_ret.continuance_token: _log.error("Auth login failed - Continuance token is invalid") _emit_login_auth_error(EOS.Result.InvalidState) return false if not auto_link_account: _log.error("Auth login failed - External account not connected") _emit_login_auth_error(EOS.Result.InvalidUser) return false _log.debug("External account not found. Proceeding to connect account...") var continue_success = await _continue_login_async(auth_login_ret.continuance_token) if not continue_success: return false elif not EOS.is_success(auth_res): _log.error("Failed to login with EOS Auth: result_code=%s" % EOS.result_str(auth_res)) _emit_login_auth_error(auth_res) return false if auth_login_ret.selected_account_id: epic_account_id = auth_login_ret.selected_account_id if epic_account_id: _log.info("Logged into Epic Account Services with Epic Account ID: %s" % epic_account_id) if not auto_connect_account: logged_in_auth.emit() logged_in.emit() return true return await _connect_account_async() ## Logout from EOS Auth and or EOS Connect func logout_async() -> EOS.Result: _log.verbose("Logging out from EOS...") var ret := EOS.Result.InvalidAuth var _logged_out = false if product_user_id: _log.debug("Logging out from EOS Connect") var logout_connect_opts = EOS.Connect.LogoutOptions.new() EOS.Connect.ConnectInterface.logout(logout_connect_opts) var logout_connect_ret = await IEOS.connect_interface_logout_callback ret = logout_connect_ret.result_code if not EOS.is_success(ret): _log.error("Failed to logout of EOS Connect. result_code=%s" % EOS.result_str(ret)) return ret else: _log.debug("Logged out from EOS connect: product_user_id=%s" % product_user_id) product_user_id = "" _logged_out = true logged_out_connect.emit() if epic_account_id: _log.debug("Logging out from EOS Auth") var logout_auth_opts = EOS.Auth.LogoutOptions.new() EOS.Auth.AuthInterface.logout(logout_auth_opts) var logout_auth_ret = await IEOS.auth_interface_logout_callback ret = logout_auth_ret.result_code if not EOS.is_success(ret): _log.error("Failed to logout of EOS Auth. result_code=%s" % EOS.result_str(ret)) else: _log.debug("Logged out from EOS Auth: epic_account_id=%s" % epic_account_id) epic_account_id = "" _logged_out = true logged_out_auth.emit() if _logged_out: display_name = "" external_account_info = {} display_name_changed.emit() external_account_info_changed.emit() logged_out.emit() return ret ## Login with EOS Connect by using external credentials func login_game_services_async(opts: EOS.Connect.LoginOptions) -> bool: _log.debug("Logging into Epic Game Services (ConnectInterface)...") _last_connect_login_opts = opts EOS.Connect.ConnectInterface.login(opts) var login_ret: Dictionary = await IEOS.connect_interface_login_callback var login_res: EOS.Result = login_ret.result_code if login_res == EOS.Result.InvalidUser: _log.debug("Epic Game Services user not found. Proceeding to create user...") var create_success := await _create_user_async(login_ret.continuance_token) if not create_success: return false elif not EOS.is_success(login_ret): _log.error("Failed to login to Epic Game Services: result_code=%s" % EOS.result_str(login_res)) _emit_login_connect_error(login_res) return false if login_ret.local_user_id: product_user_id = login_ret.local_user_id if product_user_id: _log.info("Logged into Epic Games Services with Product User ID: %s" % product_user_id) if auto_fetch_external_account and EOS.ExternalCredentialType.DeviceidAccessToken != opts.credentials.type: get_product_user_info_async() logged_in_connect.emit() logged_in.emit() return true ## Login automatically if an Epic refresh token is stored by a previous login with Epic Account Portal func login_persistent_auth_async() -> bool: _log.debug("Logging in with persistent auth...") var opts = EOS.Auth.LoginOptions.new() opts.credentials = EOS.Auth.Credentials.new() opts.credentials.type = EOS.Auth.LoginCredentialType.PersistentAuth opts.scope_flags = auth_login_scope_flags opts.login_flags = auth_login_flags return await login_async(opts) ## Delete the internally stored Epic refresh token func delete_persistent_auth_async(refresh_token := "") -> bool: _log.debug("Deleting persistent auth...") var opts = EOS.Auth.DeletePersistentAuthOptions.new() opts.refresh_token = refresh_token EOS.Auth.AuthInterface.delete_persistent_auth(opts) var ret = await IEOS.auth_interface_delete_persistent_auth_callback if not EOS.is_success(ret): _log.error("Failed to delete persistent auth: result_code=%s" % EOS.result_str(ret)) return false return true ## Login to Epic Game Services without any credentials. ## You must provide a user display name. func login_anonymous_async(p_user_display_name: String) -> bool: var user_display_name := p_user_display_name.strip_edges() if not p_user_display_name: _log.error("User display name is empty") return false _log.debug("Logging in anonymously...") EOS.Connect.ConnectInterface.delete_device_id(EOS.Connect.DeleteDeviceIdOptions.new()) var delete_ret = await IEOS.connect_interface_delete_device_id_callback if not EOS.is_success(delete_ret): _log.debug("Failed to delete device id: result_code=%s" % EOS.result_str(delete_ret)) var opts = EOS.Connect.CreateDeviceIdOptions.new() opts.device_model = " ".join(PackedStringArray([OS.get_name(), OS.get_model_name()])) EOS.Connect.ConnectInterface.create_device_id(opts) var create_ret = await IEOS.connect_interface_create_device_id_callback if not EOS.is_success(create_ret): _log.error("Failed to create device id: result_code=%s" % EOS.result_str(create_ret)) return false var login_opts = EOS.Connect.LoginOptions.new() login_opts.credentials = EOS.Connect.Credentials.new() login_opts.credentials.type = EOS.ExternalCredentialType.DeviceidAccessToken login_opts.credentials.token = null login_opts.user_login_info = EOS.Connect.UserLoginInfo.new() login_opts.user_login_info.display_name = user_display_name display_name = user_display_name display_name_changed.emit() return await login_game_services_async(login_opts) ## Get the user info from epic account id.[br] ## Returns a [Dictionary] with the following keys or empty dictionary if error occurred:[codeblock] ## user_id: String ## country: String ## display_name: String ## display_name_sanitized: String ## preferred_language: String ## nickname: String ## [/codeblock] func get_user_info_async(p_epic_account_id := epic_account_id) -> Dictionary: _log.verbose("Querying user info: epic_account_id=%s" % p_epic_account_id) var query_opts = EOS.UserInfo.QueryUserInfoOptions.new() query_opts.local_user_id = epic_account_id query_opts.target_user_id = p_epic_account_id EOS.UserInfo.UserInfoInterface.query_user_info(query_opts) var ret: Dictionary = await IEOS.user_info_interface_query_user_info_callback if not EOS.is_success(ret): _log.error("Failed to query user info: result_code=%s" % EOS.result_str(ret)) return {} var copy_opts = EOS.UserInfo.CopyUserInfoOptions.new() copy_opts.local_user_id = epic_account_id copy_opts.target_user_id = p_epic_account_id var copy_ret = EOS.UserInfo.UserInfoInterface.copy_user_info(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy user info: result_code=%s" % EOS.result_str(copy_ret)) return {} return copy_ret.user_info ## Get the external user account linked with Epic Game Services[br] ## Returns a [Dictionary] with the following keys or empty dictionary if error occurred: ## [codeblock] ## product_user_id: String - the product user ID of the external account ## display_name: String - external account display name or empty string ## account_id: String - external account id ## account_id_type: EOS.ExternalAccountType - type of external account ## last_login_time: int - unix timestamp when the user last logged in or -1 ## [/codeblock] func get_external_account_by_type_async(p_external_account_type: EOS.ExternalAccountType, p_product_user_id := product_user_id) -> Dictionary: _log.debug("Getting external account by type: external_account_type=%s product_user_id=%s" % [p_external_account_type, p_product_user_id]) var opts = EOS.Connect.QueryProductUserIdMappingsOptions.new() opts.product_user_ids = [p_product_user_id] EOS.Connect.ConnectInterface.query_product_user_id_mappings(opts) var ret = await IEOS.connect_interface_query_product_user_id_mappings_callback if not EOS.is_success(ret): _log.error("Failed to query product user id mappings: result_code=%s" % EOS.result_str(ret)) return {} var copy_opts = EOS.Connect.CopyProductUserExternalAccountByAccountTypeOptions.new() copy_opts.account_id_type = p_external_account_type copy_opts.target_user_id = p_product_user_id var copy_ret = EOS.Connect.ConnectInterface.copy_product_user_external_account_by_account_type(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy external account: result_code=%s" % EOS.result_str(copy_ret)) return {} _log.debug("Got external account info: product_user_id=%s" % p_product_user_id) var acc_info = copy_ret.external_account_info if not acc_info: acc_info = {} _log.error("Failed to get external account info") return acc_info ## Get all external accounts linked with Epic Games Services[br] ## Returns a [Dictionary] with same keys as [method get_external_account_by_type_async][br] func get_external_accounts_async(p_product_user_id := product_user_id) -> Array: _log.debug("Getting all external accounts: product_user_id=%s" % p_product_user_id) var opts = EOS.Connect.QueryProductUserIdMappingsOptions.new() opts.product_user_ids = [p_product_user_id] EOS.Connect.ConnectInterface.query_product_user_id_mappings(opts) var ret = await IEOS.connect_interface_query_product_user_id_mappings_callback if not EOS.is_success(ret): _log.error("Failed to query product user id mappings: result_code=%s" % EOS.result_str(ret)) return [] var count_opts = EOS.Connect.GetProductUserExternalAccountCountOptions.new() count_opts.target_user_id = p_product_user_id var count = EOS.Connect.ConnectInterface.get_product_user_external_account_count(count_opts) var ext_accs = [] for i in range(count): var copy_opts = EOS.Connect.CopyProductUserExternalAccountByIndexOptions.new() copy_opts.target_user_id = p_product_user_id copy_opts.external_account_info_index = i var copy_ret = EOS.Connect.ConnectInterface.copy_product_user_external_account_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy external account: result_code=%s, index=%s" % [EOS.result_str(copy_ret), i]) continue if copy_ret.external_account_info: ext_accs.append(copy_ret.external_account_info) return ext_accs ## Get the external account linked with Epic Game Services that the user most recently logged in with.[br] ## Returns a [Dictionary] with same keys as [method get_external_account_by_type_async] func get_product_user_info_async(p_product_user_id := product_user_id): _log.debug("Getting product user info: product_user_id=%s" % p_product_user_id) var opts = EOS.Connect.QueryProductUserIdMappingsOptions.new() opts.product_user_ids = [p_product_user_id] EOS.Connect.ConnectInterface.query_product_user_id_mappings(opts) var ret = await IEOS.connect_interface_query_product_user_id_mappings_callback if not EOS.is_success(ret): _log.error("Failed to query product user id mappings: result_code=%s" % EOS.result_str(ret)) return {} var copy_opts = EOS.Connect.CopyProductUserInfoOptions.new() copy_opts.target_user_id = p_product_user_id var copy_ret = EOS.Connect.ConnectInterface.copy_product_user_info(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy product user info: result_code=%s" % EOS.result_str(copy_ret)) return {} _log.debug("Got product user info: product_user_id=%s" % p_product_user_id) var ext_acc = copy_ret.external_account_info if not ext_acc: _log.error("Failed to get external account info") if ext_acc and ext_acc.product_user_id == product_user_id: external_account_info = ext_acc external_account_info_changed.emit() if ext_acc.display_name: display_name = ext_acc.display_name display_name_changed.emit() return ext_acc #endregion #region Private methods func _on_connect_interface_auth_expiration(data: Dictionary): _log.debug("Connect Auth Expiring...") _connect_account_async() func _connect_account_async() -> bool: _log.debug("Connecting account to Epic Game Services...") if _last_connect_login_opts: var opts = _last_connect_login_opts if opts and opts.credentials and EOS.ExternalCredentialType.DeviceidAccessToken == opts.credentials.type: return await login_game_services_async(opts) # Copy the user auth token var auth_token_ret = EOS.Auth.AuthInterface.copy_user_auth_token(EOS.Auth.CopyUserAuthTokenOptions.new(), epic_account_id) if not EOS.is_success(auth_token_ret): _log.error("Failed to get user auth token: result_code=%s" % EOS.result_str(auth_token_ret)) _emit_login_auth_error(auth_token_ret.result_code) return false var token = auth_token_ret.token var login_options = EOS.Connect.LoginOptions.new() login_options.credentials = EOS.Connect.Credentials.new() login_options.credentials.type = EOS.ExternalCredentialType.Epic login_options.credentials.token = token.access_token # Emit signal only if we are not refreshing the login if not epic_account_id: logged_in_auth.emit() return await login_game_services_async(login_options) func _continue_login_async(continuance_token: EOSGContinuanceToken) -> bool: _log.debug("Continuing login...") var link_opts = EOS.Auth.LinkAccountOptions.new() link_opts.continuance_token = continuance_token EOS.Auth.AuthInterface.link_account(link_opts) var link_ret: Dictionary = await IEOS.auth_interface_link_account_callback if not EOS.is_success(link_ret): _log.error("Failed to link account: result_code=%s" % EOS.result_str(link_ret)) _emit_login_auth_error(link_ret.result_code) return false if link_ret.selected_account_id: epic_account_id = link_ret.selected_account_id return true func _create_user_async(continuance_token: EOSGContinuanceToken) -> bool: _log.debug("Creating user...") var create_opts = EOS.Connect.CreateUserOptions.new() create_opts.continuance_token = continuance_token EOS.Connect.ConnectInterface.create_user(create_opts) var create_ret: Dictionary = await IEOS.connect_interface_create_user_callback if not EOS.is_success(create_ret): _log.error("Failed to create user: result_code=%s" % EOS.result_str(create_ret)) _emit_login_connect_error(create_ret.result_code) return false if create_ret.local_user_id: product_user_id = create_ret.local_user_id return true func _emit_login_auth_error(result_code: EOS.Result): login_auth_error.emit(result_code) login_error.emit(result_code) func _emit_login_connect_error(result_code: EOS.Result): login_connect_error.emit(result_code) login_error.emit(result_code) func _get_command_line_options(): var options = {} var args = OS.get_cmdline_args() for arg in args: arg = arg.trim_prefix("--").trim_prefix("-") var kvp = arg.split("=") if len(kvp) > 1: options[kvp[0]] = kvp[1] return options #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hauth.gd.uid ================================================ uid://786t1jiowvvg ================================================ FILE: sample/addons/epic-online-services-godot/heos/hcredentials.gd ================================================ ## Credentials required by Epic Online Services. Get these values from the [url=https://dev.epicgames.com/portal/en-US/]Dev Portal[/url] class_name HCredentials extends BaseClass func _init() -> void: super._init("HCredentials") ## Game name (max 64 characters)[br]Eg. "My Awesome Game" var product_name: String ## Game version[br]Eg. "1.0" var product_version: String ## Product ID of the game, provided by Epic Games var product_id: String ## Sandbox ID of the game, provided by Epic Games var sandbox_id: String ## Deployment ID of the game, provided by Epic Games var deployment_id: String ## Client ID to use for the game, provided by Epic Games var client_id: String ## Client Secret to use for the game, provided by Epic Games var client_secret: String ## (Optional) 256-bit Encryption Key for file encryption in hexadecimal format (64 hex characters) var encryption_key: String ================================================ FILE: sample/addons/epic-online-services-godot/heos/hcredentials.gd.uid ================================================ uid://dh3dlxnt075r2 ================================================ FILE: sample/addons/epic-online-services-godot/heos/hfriends.gd ================================================ extends Node #region Signals #endregion #region Public vars #endregion #region Private vars var _log = HLog.logger("HFriends") #endregion #region Built-in methods func _ready() -> void: pass #endregion #region Public methods ## Returns an [Array] of user info [Dictionary] representing all the user's friends ## See [HAuth.get_user_info_async] for the return type of user info func get_friends_async() -> Array: _log.debug("Getting friends...") var query_opts = EOS.Friends.QueryFriendsOptions.new() EOS.Friends.FriendsInterface.query_friends(query_opts) var ret = await IEOS.friends_interface_query_friends_callback if not EOS.is_success(ret): _log.error("Failed to query friends: result_code=%s" % EOS.result_str(ret)) return [] var get_count_opts = EOS.Friends.GetFriendsCountOptions.new() var friends_count: int = EOS.Friends.FriendsInterface.get_friends_count(get_count_opts) var user_infos = [] for i in friends_count: var opts = EOS.Friends.GetFriendAtIndexOptions.new() opts.index = i var friend_epic_account_id = EOS.Friends.FriendsInterface.get_friend_at_index(opts) var user_info = await HAuth.get_user_info_async(friend_epic_account_id) if user_info: user_infos.append(user_info) return user_infos #endregion #region Private methods #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hfriends.gd.uid ================================================ uid://uumqbey8ji5t ================================================ FILE: sample/addons/epic-online-services-godot/heos/hleaderboards.gd ================================================ extends Node #region Signals #endregion #region Public vars #endregion #region Private vars var _log = HLog.logger("HLeaderboards") #endregion #region Built-in methods func _ready() -> void: pass #endregion #region Public methods ## Returns an [Array] of [Dictionary] of all the leaderboards. ## Each leaderboard has the following data: ## leaderboard_id: String - unique id ## stat_name: String - name of stat used to rank the leaderboard ## aggregation: EOS.Leaderboards.LeaderboardAggregation - aggregation used to sort the leaderboard ## start_time: int - unix timestamp for the start time or [EOS.Leaderboard.LEADERBOARD_TIME_UNDEFINED] ## end_time: int - unix timestamp for the end time or [EOS.Leaderboard.LEADERBOARD_TIME_UNDEFINED] func get_leaderboard_definitions_async() -> Array: _log.debug("Getting leaderboards...") var query_opts = EOS.Leaderboards.QueryLeaderboardDefinitionsOptions.new() EOS.Leaderboards.LeaderboardsInterface.query_leaderboard_definitions(query_opts) var ret: Dictionary = await IEOS.leaderboards_interface_query_leaderboard_definitions_callback if not EOS.is_success(ret): _log.error("Failed to query leaderboards: result_code=%s" % EOS.result_str(ret)) return [] var count_opts = EOS.Leaderboards.GetLeaderboardDefinitionCountOptions.new() var count: int = EOS.Leaderboards.LeaderboardsInterface.get_leaderboard_definition_count(count_opts) _log.debug("Got leaderboards: count=%s" % count) var leaderboards = [] for i in count: var copy_opts = EOS.Leaderboards.CopyLeaderboardDefinitionByIndexOptions.new() copy_opts.leaderboard_index = i var copy_ret = EOS.Leaderboards.LeaderboardsInterface.copy_leaderboard_definition_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy leaderboard: result_code=%s" % EOS.result_str(copy_ret)) continue leaderboards.append(copy_ret.definition) return leaderboards ## Returns an [Array] of [Dictionary] of top records in the leaderboard. ## Each record has the following data: ## leaderboard_id: String - unique id ## user_id: String - product user id of user associated with this record ## rank: int - sorted position in the leaderboard ## score: int - leaderboard score ## user_display_name: String - latest display name for the user or empty string if the user doesn't have one func get_leaderboard_records_async(leaderboard_id: String) -> Array: _log.debug("Getting leaderboard ranks: leaderboard_id=%s" % leaderboard_id) var query_opts = EOS.Leaderboards.QueryLeaderboardRanksOptions.new() query_opts.leaderboard_id = leaderboard_id EOS.Leaderboards.LeaderboardsInterface.query_leaderboard_ranks(query_opts) var ret = await IEOS.leaderboards_interface_query_leaderboard_ranks_callback if not EOS.is_success(ret): _log.error("Failed to query leaderboard ranks: result_code=%s" % EOS.result_str(ret)) return [] var count_opts = EOS.Leaderboards.GetLeaderboardRecordCountOptions.new() var count = EOS.Leaderboards.LeaderboardsInterface.get_leaderboard_record_count(count_opts) var records = [] for i in count: var copy_opts = EOS.Leaderboards.CopyLeaderboardRecordByIndexOptions.new() copy_opts.leaderboard_record_index = i var copy_ret = EOS.Leaderboards.LeaderboardsInterface.copy_leaderboard_record_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy leaderboard record: result_code=%s" % EOS.result_str(copy_ret)) continue records.append(copy_ret.record) return records #endregion #region Private methods #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hleaderboards.gd.uid ================================================ uid://b06khhf20mplm ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobbies.gd ================================================ extends Node #region Public vars ## The maximum number of lobbies to return in search calls.[br] ## Except for [method create_search] and [method search_async] var max_search_results = 25 ## Whether to show the lobby in the user's presence var presence_enabled = true ## Local RTC options for the lobby[br] ## (Optional) You can override any of the below keys.[br] ## A [Dictionary] with keys:[br] ## - flags: A bitwise-or union of [enum EOS.RTC.JoinRoomFlags][br] ## - use_manual_audio_input: [bool][br] ## - use_manual_audio_output: [bool][br] ## - local_audio_device_input_starts_muted: [bool] var local_rtc_options = { flags = EOS.RTC.JoinRoomFlags.EnableDataChannel } #endregion #region Private vars var _log = HLog.logger("HLobbies") #endregion #region Public methods ## Create a new lobby. Returns [HLobby] or [null] func create_lobby_async(opts: EOS.Lobby.CreateLobbyOptions) -> HLobby: _log.debug("Creating lobby...") EOS.Lobby.LobbyInterface.create_lobby(opts) var ret = await IEOS.lobby_interface_create_lobby_callback if not EOS.is_success(ret): _log.error("Failed to create lobby: %s" % EOS.result_str(ret)) return null _log.debug("Lobby created: lobby_id=%s" % ret.lobby_id) var lobby = HLobby.new() lobby.init_from_id(ret.lobby_id) return lobby ## Join an existing lobby by id. It should only be used if the lobby has had Join-by-ID enabled. ## Additionally, Join-by-ID should only be enabled to support native invites on an integrated platform. ## Returns [HLobby] or [null] func join_by_id_async(lobby_id: String) -> HLobby: _log.debug("Joining lobby by id: lobby_id=%s" % lobby_id) var opts = EOS.Lobby.JoinLobbyByIdOptions.new() opts.lobby_id = lobby_id opts.presence_enabled = presence_enabled opts.local_rtc_options = local_rtc_options EOS.Lobby.LobbyInterface.join_lobby_by_id(opts) var ret = await IEOS.lobby_interface_join_lobby_by_id_callback if not EOS.is_success(ret): _log.error("Failed to join lobby: result_code=%s" % EOS.result_str(ret)) return null _log.debug("Lobby joined: lobby_id=%s" % lobby_id) var lobby = HLobby.new() lobby.init_from_id(lobby_id) return lobby ## Join an existing lobby e.g. one returned from search. ## Returns [HLobby] or [null] func join_async(lobby: HLobby): if not lobby: return null _log.debug("Joining lobby...") var opts = EOS.Lobby.JoinLobbyOptions.new() opts.lobby_details = lobby._lobby_details opts.presence_enabled = presence_enabled opts.local_rtc_options = local_rtc_options EOS.Lobby.LobbyInterface.join_lobby(opts) var ret = await IEOS.lobby_interface_join_lobby_callback if not EOS.is_success(ret): _log.error("Failed to join lobby: result_code=%s" % EOS.result_str(ret)) return null _log.debug("Lobby joined: lobby_id=%s" % ret.lobby_id) var new_lobby = HLobby.new() new_lobby.init_from_id(ret.lobby_id) return new_lobby ## Search for public lobbies that a user is in. ## Returns [Array] of [HLobby] or null func search_by_product_user_id_async(product_user_id: String): _log.debug("Searching for lobbies by product user id: product_user_id=%s" % product_user_id) var opts = EOS.Lobby.CreateLobbySearchOptions.new() opts.max_results = max_search_results var search: EOSGLobbySearch = create_search(opts) if not search: return null search.set_target_user_id(product_user_id) return await search_async(search) ## Search for public lobbies based on attributes.[br] ## The input argument can either be a [Dictionary] or an [Array] of [Dictionary].[br] ## (Note there is an implicit AND operation if multiple search attributes are provided)[br] ## Each search attribute is a Dictionary having the keys:[br] ## - key: [String] - the key of the attribute[br] ## - value: [String] - the value of the attribute[br] ## - comparison: [enum EOS.ComparisonOp] - Type of comparison to make (default is EOS.ComparisonOp.Equal)[br] ## Returns [Array] of [HLobby] or null func search_by_attribute_async(attributes): if typeof(attributes) == TYPE_DICTIONARY: attributes = [attributes] _log.debug("Searching for lobbies by attributes: attributes=%s" % str(attributes)) var opts = EOS.Lobby.CreateLobbySearchOptions.new() opts.max_results = max_search_results var search: EOSGLobbySearch = create_search(opts) if not search: return null for attr in attributes: if attr.has("comparison"): search.set_parameter(attr.key, attr.value, attr.comparison) else: search.set_parameter(attr.key, attr.value, EOS.ComparisonOp.Equal) return await search_async(search) ## Search for lobby by id. At most will return one lobby. ## Returns [Array] of [HLobby] or null func search_by_lobby_id_async(lobby_id: String): _log.debug("Searching for lobbies by lobby id: lobby_id=%s" % lobby_id) var opts = EOS.Lobby.CreateLobbySearchOptions.new() opts.max_results = max_search_results var search: EOSGLobbySearch = create_search(opts) if not search: return null search.set_lobby_id(lobby_id) return await search_async(search) ## Search for lobby by bucket id. ## Returns [Array] of [HLobby] or null func search_by_bucket_id_async(bucket_id: String): _log.debug("Searching for lobbies by bucket id: bucket_id=%s" % bucket_id) return await HLobbies.search_by_attribute_async({ key = EOS.Lobby.SEARCH_BUCKET_ID, value = bucket_id, comparison = EOS.ComparisonOp.Equal }) ## (Advanced) Create a new lobby search. Returns [EOSGLobbySearch] or null. Use [search_async] to perform the search. func create_search(opts: EOS.Lobby.CreateLobbySearchOptions): _log.debug("Creating lobby search...") var create_ret = EOS.Lobby.LobbyInterface.create_lobby_search(opts) if not EOS.is_success(create_ret): _log.error("Failed to create lobby search: result_code=%s" % EOS.result_str(create_ret)) return null return create_ret.lobby_search ## (Advanced) Perform the lobby search. Returns [Array] of [HLobby] or null func search_async(lobby_search: EOSGLobbySearch): _log.debug("Searching for lobbies...") lobby_search.find(HAuth.product_user_id) var search_ret = await IEOS.lobby_search_find_callback if not EOS.is_success(search_ret): _log.error("Failed to search for lobbies: result_code=%s" % EOS.result_str(search_ret)) return null var count = lobby_search.get_search_result_count() _log.debug("Found lobbies: count=%s" % count) var lobbies: Array[HLobby] = [] for search_idx in count: var copy_ret = lobby_search.copy_search_result_by_index(search_idx) if not EOS.is_success(copy_ret): _log.error("Failed to copy lobby search result by index: result_code=%s" % EOS.result_str(copy_ret)) continue var lobby = HLobby.new() var details = copy_ret.lobby_details lobby._init_from_details(details) lobbies.append(lobby) return lobbies #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobbies.gd.uid ================================================ uid://6iqvm33q0tb1 ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobby.gd ================================================ ## A high-level lobby wrapper for EOSG class_name HLobby extends BaseClass #region Signals ## Emitted when anything about the lobby changes. Except when kicked or lobby is destroyed. Connect to kicked_from_lobby for the latter. signal lobby_updated ## Emitted when the current user is kicked from lobby or the lobby was destroyed signal kicked_from_lobby ## Emitted when another member is promoted to the lobby owner. Use get_owner to get the new owner signal lobby_owner_changed ## Emitted when the current user in the lobby receives data through the RTC data channel signal rtc_data_received #endregion #region Public vars var lobby_id: String var owner_product_user_id: String var bucket_id: String var max_members: int var available_slots: int var members: Array[HLobbyMember] = [] var allow_invites: bool var permission_level: EOS.Lobby.LobbyPermissionLevel var presence_enabled: bool var allow_host_migration: bool ## [Array] of [Dictionary] having {"key": String, "value": Variant, "visibility": [enum EOS.Lobby.LobbyAttributeVisibility] } var attributes = [] var rtc_room_enabled: bool var rtc_room_name: String var rtc_room_connected: bool var allow_join_by_id: bool var rejoin_after_kick_requires_invite: bool var allowed_platform_ids: Array #endregion #region Private vars var _log = HLog.logger("HLobby") var _attributes_to_add = [] var _lobby_details: EOSGLobbyDetails var _connected_to_lobby_events = false var _connected_to_rtc_events = false var _notif_rtc_parti_status_changed = EOS.NotificationIdInvalid var _notif_rtc_audio_parti_updated = EOS.NotificationIdInvalid var _notif_rtc_data_parti_updated = EOS.NotificationIdInvalid var _notif_rtc_data_data_received = EOS.NotificationIdInvalid #endregion #region Built-in methods func _init() -> void: super._init("HLobby") func _notification(what: int) -> void: if what == NOTIFICATION_PREDELETE: _lobby_details = null #endregion #region Public methods ## Check whether the lobby is valid func is_valid() -> bool: return lobby_id != "" ## Check whether a user is the lobby owner ## By default it will check the current logged in user func is_owner(product_user_id = HAuth.product_user_id) -> bool: return owner_product_user_id == product_user_id ## Returns a [HLobbyMember] based on product user id or null func get_member_by_product_user_id(product_user_id: String) -> HLobbyMember: for mem in members: if mem.product_user_id == product_user_id: return mem return null ## Returns the current lobby member [HLobbyMember] or null func get_current_member() -> HLobbyMember: for mem in members: if mem.product_user_id == HAuth.product_user_id: return mem return null ## Returns [HLobbyMember] or null func get_owner() -> HLobbyMember: for mem in members: if mem.product_user_id == owner_product_user_id: return mem return null ## Intialize the lobby from a lobby_id func init_from_id(p_lobby_id: String): _log.debug("Initializing from lobby id: %s" % p_lobby_id) lobby_id = p_lobby_id _copy_lobby_data() if is_valid() and get_current_member() and not _connected_to_lobby_events: _connected_to_lobby_events = true IEOS.lobby_interface_lobby_update_received_callback.connect(_on_lobby_update_received_callback) IEOS.lobby_interface_lobby_member_update_received_callback.connect(_on_lobby_interface_lobby_member_update_received_callback) IEOS.lobby_interface_lobby_member_status_received_callback.connect(_on_lobby_member_status_received_callback) IEOS.lobby_interface_rtc_room_connection_changed_callback.connect(_on_lobby_interface_rtc_room_connection_changed_callback) IEOS.lobby_interface_leave_lobby_requested_callback.connect(_on_lobby_interface_leave_lobby_requested_callback) # RTC IEOS.rtc_interface_participant_status_changed.connect(_on_rtc_interface_participant_status_changed) IEOS.rtc_audio_participant_updated.connect(_on_rtc_audio_participant_updated) IEOS.rtc_data_participant_updated.connect(_on_rtc_data_participant_updated) IEOS.rtc_data_data_received.connect(_on_rtc_data_data_received) ## Returns [Dictionary] or empty [Dictionary] if not found. func get_attribute(key: String): for attr in attributes: if attr.key == key: return attr return {} ## Add an attribute to this lobby. Make sure to call update_async to actually update the lobby. Returns true if success. func add_attribute(key: String, value: Variant, visibility = EOS.Lobby.LobbyAttributeVisibility.Public) -> bool: var attr = make_attribute(key, value, visibility) _attributes_to_add.append(attr) return true ## Returns the current user's attribute as [Dictionary] or empty [Dictionary] if not found. func get_current_member_attribute(key: String): var member = get_member_by_product_user_id(HAuth.product_user_id) if not member: return {} for attr in member.attributes: if attr.key == key: return attr return {} ## Add an attribute to the current user in the lobby. Make sure to call update_async to actually update the lobby. Returns true if success. func add_current_member_attribute(key: String, value: Variant, visibility = EOS.Lobby.LobbyAttributeVisibility.Public) -> bool: var member = get_member_by_product_user_id(HAuth.product_user_id) if not member: return false var attr = make_attribute(key, value, visibility) member._attributes_to_add.append(attr) return true ## Helper function to create an attribute with key, value and visibility static func make_attribute(key: String, value: Variant, visibility = EOS.Lobby.LobbyAttributeVisibility.Public) -> Dictionary: return { key = key, value = value, visibility = visibility } ## Update the lobby, if any changes are pending func update_async() -> bool: _log.debug("Updating lobby: lobby_id=%s" % lobby_id) if not is_valid(): _log.error("Cannot update invalid lobby") return false var opts = EOS.Lobby.UpdateLobbyModificationOptions.new() opts.lobby_id = lobby_id var lobby_mod_ret = EOS.Lobby.LobbyInterface.update_lobby_modification(opts) if not EOS.is_success(lobby_mod_ret): _log.error("Failed to create lobby modification: result_code=%s" % EOS.result_str(lobby_mod_ret)) return false var lobby_mod: EOSGLobbyModification = lobby_mod_ret.lobby_modification if is_owner(HAuth.product_user_id): _log.debug("Updating lobby as owner...") if allowed_platform_ids.size() > 0: var set_allowed_platform_ids_ret = lobby_mod.set_allowed_platform_ids(allowed_platform_ids) if not EOS.is_success(set_allowed_platform_ids_ret): _log.error("Failed to set allowed platform ids on lobby modification: result_code=%s" % EOS.result_str(set_allowed_platform_ids_ret)) return false var set_bucket_ret = lobby_mod.set_bucket_id(bucket_id) if not EOS.is_success(set_bucket_ret): _log.error("Failed to set bucket id on lobby modification: result_code=%s" % EOS.result_str(set_bucket_ret)) return false var set_invites_allowed_ret = lobby_mod.set_invites_allowed(allow_invites) if not EOS.is_success(set_invites_allowed_ret): _log.error("Failed to set invites allowed on lobby modification: result_code=%s" % EOS.result_str(set_invites_allowed_ret)) return false var set_max_members_ret = lobby_mod.set_max_members(max_members) if not EOS.is_success(set_max_members_ret): _log.error("Failed to set max members on lobby modification: result_code=%s" % EOS.result_str(set_max_members_ret)) return false var set_perm_ret = lobby_mod.set_permission_level(permission_level) if not EOS.is_success(set_perm_ret): _log.error("Failed to set permission level on lobby modification: result_code=%s" % EOS.result_str(set_perm_ret)) return false var new_attrs = attributes.duplicate(true) new_attrs.append_array(_attributes_to_add) for attr in new_attrs: var add_ret = lobby_mod.add_attribute(attr.key, attr.value, attr.visibility) if not EOS.is_success(add_ret): _log.error("Failed to add attribute on lobby modification. Skipping this attribute: result_code=%s, key=%s, value=%s, visibility=%s" % [EOS.result_str(add_ret), attr.key, attr.value, attr.visibility]) _attributes_to_add = [] else: _log.debug("Updating lobby as user...") var member: HLobbyMember = get_member_by_product_user_id(HAuth.product_user_id) if member: var mem_attrs = member.attributes.duplicate(true) mem_attrs.append_array(member._attributes_to_add) for attr in mem_attrs: var add_ret = lobby_mod.add_member_attribute(attr.key, attr.value, attr.visibility) if not EOS.is_success(add_ret): _log.error("Failed to add member attribute on lobby modification. Skipping this attribute: result_code=%s, key=%s, value=%s, visibility=%s" % [EOS.result_str(add_ret), attr.key, attr.value, attr.visibility]) member._attributes_to_add = [] var update_opts = EOS.Lobby.UpdateLobbyOptions.new() update_opts.lobby_modification = lobby_mod EOS.Lobby.LobbyInterface.update_lobby(update_opts) var update_ret = await IEOS.lobby_interface_update_lobby_callback if not EOS.is_success(update_ret): _log.error("Failed to update lobby: result_code=%s. lobby_id=%s" % [EOS.result_str(update_ret), lobby_id]) return false _log.debug("Lobby updated successfully: lobby_id=%s" % lobby_id) _copy_lobby_data() lobby_updated.emit() return true ## Leave the lobby ## Returns true if the leave was successful func leave_async() -> bool: if not is_valid(): return false _log.debug("Leaving lobby: lobby_id=%s" % lobby_id) var opts = EOS.Lobby.LeaveLobbyOptions.new() opts.lobby_id = lobby_id EOS.Lobby.LobbyInterface.leave_lobby(opts) var ret = await IEOS.lobby_interface_leave_lobby_callback if not EOS.is_success(ret): _log.error("Failed to leave lobby: result_code=%s" % EOS.result_str(ret)) return false _log.debug("Leave lobby successful: lobby_id=%s" % lobby_id) _disconnect_from_signals() return true ## Destroy the lobby. Only the owner can call this. ## Returns true if the destroy was successful func destroy_async() -> bool: if not is_valid(): return false if not is_owner(): return false _log.debug("Destroying lobby: lobby_id=%s" % lobby_id) var opts = EOS.Lobby.DestroyLobbyOptions.new() opts.lobby_id = lobby_id EOS.Lobby.LobbyInterface.destroy_lobby(opts) var ret = await IEOS.lobby_interface_destroy_lobby_callback if not EOS.is_success(ret): _log.error("Failed to destroy lobby: result_code=%s" % EOS.result_str(ret)) return false _log.debug("Destroy lobby successful: lobby_id=%s" % lobby_id) lobby_id = "" _disconnect_from_signals() kicked_from_lobby.emit() return true ## Returns true if the current user can invite another user ## p_product_user_id: The product user id of the user to invite func can_invite(p_product_user_id: String) -> bool: if not is_valid(): return false if not get_current_member(): return false if (not allow_invites) or (not available_slots) or (members.size() >= max_members): return false # Check if in lobby var mem = get_member_by_product_user_id(p_product_user_id) if mem: return false return true ## Send data as a Variant to all users in the lobby's RTC data channel. ## Note: max packet size is [const EOS.RTCData.MAX_PACKET_SIZE_BYTES] bytes ## This does not encode objects by default. Use rtc_send_data_raw ## to encode arbitary data. func rtc_send_data(data: Variant) -> bool: return rtc_send_data_raw(var_to_bytes(data)) ## Send raw bytes as PackedByteArray to all users in the lobby's RTC data channel ## Note: max packet size is [const EOS.RTCData.MAX_PACKET_SIZE_BYTES] bytes func rtc_send_data_raw(data: PackedByteArray) -> bool: var opts = EOS.RTCData.SendDataOptions.new() opts.room_name = rtc_room_name opts.data = data var res = EOS.RTCData.RTCDataInterface.send_data(opts) return EOS.is_success(res) #endregion #region Private methods func _copy_lobby_data(): var copy_opts = EOS.Lobby.CopyLobbyDetailsOptions.new() copy_opts.lobby_id = lobby_id var copy_ret = EOS.Lobby.LobbyInterface.copy_lobby_details(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy lobby details: result_code=%s" % EOS.result_str(copy_ret)) return _init_from_details(copy_ret.lobby_details) func _init_from_details(lobby_details: EOSGLobbyDetails): _log.debug("Initializing from EOSGLobbyDetails") _lobby_details = lobby_details var copy_ret = lobby_details.copy_info() if not EOS.is_success(copy_ret): _log.error("Failed to copy lobby info: result_code=%s" % EOS.result_str(copy_ret)) return var details: Dictionary = copy_ret.lobby_details HLog._check_diff_and_set(self, "lobby_id", details.lobby_id) HLog._check_diff_and_set(self, "owner_product_user_id", details.lobby_owner_user_id) HLog._check_diff_and_set(self, "permission_level", details.permission_level) HLog._check_diff_and_set(self, "available_slots", details.available_slots) HLog._check_diff_and_set(self, "max_members", details.max_members) HLog._check_diff_and_set(self, "allow_invites", details.allow_invites) HLog._check_diff_and_set(self, "bucket_id", details.bucket_id) HLog._check_diff_and_set(self, "allow_host_migration", details.allow_host_migration) HLog._check_diff_and_set(self, "rtc_room_enabled", details.rtc_room_enabled) HLog._check_diff_and_set(self, "allow_join_by_id", details.allow_join_by_id) HLog._check_diff_and_set(self, "rejoin_after_kick_requires_invite", details.rejoin_after_kick_requires_invite) HLog._check_diff_and_set(self, "presence_enabled", details.presence_enabled) HLog._check_diff_and_set(self, "allowed_platform_ids", details.allowed_platform_ids) # Get attributes var attr_count = lobby_details.get_attribute_count() # _log.verbose("Got attributes: count=%s" % attr_count) var orig_attributes = attributes.duplicate(true) attributes = [] for attr_idx in attr_count: var copy_attr_ret = lobby_details.copy_attribute_by_index(attr_idx) if not EOS.is_success(copy_attr_ret): _log.error("Failed to copy EOSGLobbyDetails attribute by index: result_code=%s" % EOS.result_str(copy_attr_ret)) continue var attr = copy_attr_ret.attribute attr = make_attribute(attr.data.key, attr.data.value, attr.visibility) attributes.append(attr) HLog._check_attr_diff(orig_attributes, attributes, "lobby") # Get members var members_count = lobby_details.get_member_count() # _log.verbose("Got members: count=%s" % members_count) var old_members = members.duplicate() members = [] for member_idx in members_count: var member_product_user_id = lobby_details.get_member_by_index(member_idx) var member = old_members.filter(func(m): return m.product_user_id == member_product_user_id) if member.size() > 0: _log.verbose("Updating existing lobby member") member = member[0] else: _log.verbose("Creating new lobby member") member = HLobbyMember.new(self) member.product_user_id = member_product_user_id members.append(member) # Member attributes var mem_attr_count = lobby_details.get_member_attribute_count(member_product_user_id) # _log.verbose("Got member attributes: count=%s" % mem_attr_count) var orig_mem_attr = member.attributes.duplicate(true) member.attributes = [] for mem_attr_idx in mem_attr_count: var copy_mem_attr_ret = lobby_details.copy_member_attribute_by_index(member_product_user_id, mem_attr_idx) if not EOS.is_success(copy_mem_attr_ret): _log.error("Failed to copy EOSGLobbyDetails member attribute by index: result_code=%s" % EOS.result_str(copy_mem_attr_ret)) continue var attr = copy_mem_attr_ret.attribute member.attributes.append(make_attribute(attr.data.key, attr.data.value, attr.visibility)) HLog._check_attr_diff(orig_mem_attr, member.attributes, "member:%s" % member.product_user_id) _subscribe_rtc_events() func _on_lobby_update_received_callback(data: Dictionary): if not is_valid() or data.lobby_id != lobby_id: return _log.verbose("Got lobby update: lobby_id=%s" % lobby_id) _copy_lobby_data() lobby_updated.emit() func _on_lobby_interface_lobby_member_update_received_callback(data: Dictionary): if not is_valid() or data.lobby_id != lobby_id: return _log.verbose("Got lobby member update: lobby_id=%s" % lobby_id) _copy_lobby_data() lobby_updated.emit() func _on_lobby_member_status_received_callback(data: Dictionary): if not is_valid() or data.lobby_id != lobby_id: return var member_id = data.target_user_id var status = data.current_status _log.verbose("Got lobby member status: member_id=%s, status=%s" % [member_id, EOS.Lobby.LobbyMemberStatus.find_key(status)]) var update_lobby = true if HAuth.product_user_id == data.target_user_id: if status == EOS.Lobby.LobbyMemberStatus.Closed or status == EOS.Lobby.LobbyMemberStatus.Kicked or status == EOS.Lobby.LobbyMemberStatus.Disconnected: _log.debug("Kicked from lobby: lobby_id=%s" % lobby_id) lobby_id = "" _disconnect_from_signals() kicked_from_lobby.emit() update_lobby = false if update_lobby: _copy_lobby_data() lobby_updated.emit() if status == EOS.Lobby.LobbyMemberStatus.Promoted: _log.debug("Lobby owner changed: lobby_id=%s" % lobby_id) lobby_owner_changed.emit() func _on_lobby_interface_rtc_room_connection_changed_callback(data: Dictionary): if not is_valid() or data.lobby_id != lobby_id: return if data.local_user_id != HAuth.product_user_id: return var new_rtc_room_connected = data.is_connected var disconnect_reason = data.disconnect_reason _log.debug("Lobby rtc room connection changed: lobby_id=%s, is_connected=%s, disconnect_reason=%s" % [lobby_id, new_rtc_room_connected, EOS.result_str(disconnect_reason)]) HLog._check_diff_and_set(self, "rtc_room_connected", new_rtc_room_connected) var mem = get_current_member() if mem: var did_update = HLog._check_diff_and_set_dict(mem.rtc_state, "is_in_rtc_room", new_rtc_room_connected, "member:" + mem.product_user_id) if not new_rtc_room_connected: did_update = HLog._check_diff_and_set_dict(mem.rtc_state, "is_talking", false, "member:" + mem.product_user_id) or did_update if did_update: mem.rtc_state_updated.emit() lobby_updated.emit() func _on_lobby_interface_leave_lobby_requested_callback(data: Dictionary): if not is_valid() or data.lobby_id != lobby_id: return _log.debug("Lobby leave requested from UI: lobby_id=%s" % lobby_id) leave_async() func _subscribe_rtc_events(): if not is_valid() or not rtc_room_enabled: return var mem = get_member_by_product_user_id(HAuth.product_user_id) if mem == null: return _log.verbose("Subscribing to rtc events") # _log.verbose("Subscribing to lobby rtc events") var did_update = false # RTC room name var opts = EOS.Lobby.GetRtcRoomNameOptions.new() opts.lobby_id = lobby_id var ret = EOS.Lobby.LobbyInterface.get_rtc_room_name(opts) if not EOS.is_success(ret): _log.error("Failed to get rtc room name: result_code=%s" % EOS.result_str(ret)) else: did_update = HLog._check_diff_and_set(self, "rtc_room_name", ret.rtc_room_name) or did_update # RTC connection status var is_conn_opts = EOS.Lobby.IsRtcRoomConnectedOptions.new() is_conn_opts.lobby_id = lobby_id var is_conn_ret = EOS.Lobby.LobbyInterface.is_rtc_room_connected(is_conn_opts) if not EOS.is_success(is_conn_ret): _log.error("Failed to get rtc room connection status: result_code=%s" % EOS.result_str(is_conn_ret)) else: did_update = HLog._check_diff_and_set(self, "rtc_room_connected", is_conn_ret.is_connected) or did_update if rtc_room_name and not _connected_to_rtc_events: _connected_to_rtc_events = true # RTC room participant status changes var notify_parti_status_opts = EOS.RTC.AddNotifyParticipantStatusChangedOptions.new() notify_parti_status_opts.room_name = rtc_room_name _notif_rtc_parti_status_changed = EOS.RTC.RTCInterface.add_notify_participant_status_changed(notify_parti_status_opts) # RTC audio updates var notify_audio_parti_updated = EOS.RTCAudio.AddNotifyParticipantUpdatedOptions.new() notify_audio_parti_updated.room_name = rtc_room_name _notif_rtc_audio_parti_updated = EOS.RTCAudio.RTCAudioInterface.add_notify_participant_updated(notify_audio_parti_updated) # RTC Data participant updated var notify_rtc_data_parti_updated = EOS.RTCData.AddNotifyParticipantUpdatedOptions.new() notify_rtc_data_parti_updated.room_name = rtc_room_name _notif_rtc_data_parti_updated = EOS.RTCData.RTCDataInterface.add_notify_participant_updated(notify_rtc_data_parti_updated) # RTC Data data received var notify_data_received_opts = EOS.RTCData.AddNotifyDataReceivedOptions.new() notify_data_received_opts.room_name = rtc_room_name _notif_rtc_data_data_received = EOS.RTCData.RTCDataInterface.add_notify_data_received(notify_data_received_opts) if did_update: # _log.verbose("Lobby rtc values updated: lobby_id=%s, rtc_room_name=%s, rtc_room_connected=%s" % [lobby_id, rtc_room_name, rtc_room_connected]) lobby_updated.emit() func _on_rtc_interface_participant_status_changed(data: Dictionary): if not is_valid() or data.room_name != rtc_room_name: return var participant_id = data.participant_id var participant_status = data.participant_status var mem = get_member_by_product_user_id(participant_id) if not mem: _log.error("Got rtc participant status changed for unknown participant: participant_id=%s" % participant_id) return if participant_status == EOS.RTC.ParticipantStatus.Joined: mem.rtc_state.is_in_rtc_room = true mem.rtc_state_updated.emit() elif participant_status == EOS.RTC.ParticipantStatus.Left: mem.rtc_state.is_in_rtc_room = false mem.rtc_state_updated.emit() lobby_updated.emit() func _on_rtc_audio_participant_updated(data: Dictionary): if not is_valid() or data.room_name != rtc_room_name: return var participant_puid = data.participant_id var is_talking = data.speaking var is_audio_disabled = data.audio_status != EOS.RTCAudio.AudioStatus.Enabled var is_hard_muted = data.audio_status == EOS.RTCAudio.AudioStatus.AdminDisabled var mem = get_member_by_product_user_id(data.participant_id) if mem == null: _log.error("Got rtc audio participant update for unknown participant: participant_id=%s" % participant_puid) return # Update talking status var did_change = HLog._check_diff_and_set_dict(mem.rtc_state, "is_talking", is_talking, "member:" + mem.product_user_id) # Update audio output status for other players if mem.product_user_id != HAuth.product_user_id: did_change = HLog._check_diff_and_set_dict(mem.rtc_state, "is_audio_output_disabled", is_audio_disabled, "member:" + mem.product_user_id) or did_change else: # I could have been hard-muted by the lobby owner did_change = HLog._check_diff_and_set_dict(mem.rtc_state, "is_hard_muted", is_hard_muted, "member:" + mem.product_user_id) or did_change if did_change: mem.rtc_state_updated.emit() lobby_updated.emit() func _on_rtc_data_participant_updated(data: Dictionary): if not is_valid() or data.room_name != rtc_room_name: return var participant_puid = data.participant_id var data_status = data.data_status _log.debug("Got rtc data participant update: participant_id=%s, data_status=%s" % [participant_puid, EOS.RTCData.DataStatus.find_key(data_status)]) func _on_rtc_data_data_received(p_data: Dictionary): if not is_valid() or p_data.room_name != rtc_room_name: return rtc_data_received.emit(p_data.data) func _disconnect_from_signals() -> void: _disconnect_signal_if_connected(IEOS, "lobby_interface_lobby_update_received_callback", _on_lobby_interface_lobby_member_update_received_callback) _disconnect_signal_if_connected(IEOS, "lobby_interface_lobby_member_update_received_callback", _on_lobby_interface_lobby_member_update_received_callback) _disconnect_signal_if_connected(IEOS, "lobby_interface_lobby_member_status_received_callback", _on_lobby_member_status_received_callback) _disconnect_signal_if_connected(IEOS, "lobby_interface_rtc_room_connection_changed_callback", _on_lobby_interface_rtc_room_connection_changed_callback) _disconnect_signal_if_connected(IEOS, "lobby_interface_leave_lobby_requested_callback", _on_lobby_interface_leave_lobby_requested_callback) # RTC _disconnect_signal_if_connected(IEOS, "rtc_interface_participant_status_changed", _on_rtc_interface_participant_status_changed) _disconnect_signal_if_connected(IEOS, "rtc_audio_participant_updated", _on_rtc_audio_participant_updated) _disconnect_signal_if_connected(IEOS, "rtc_data_participant_updated", _on_rtc_data_participant_updated) _disconnect_signal_if_connected(IEOS, "rtc_data_data_received", _on_rtc_data_data_received) if _notif_rtc_parti_status_changed != EOS.NotificationIdInvalid: EOS.RTC.RTCInterface.remove_notify_participant_status_changed(_notif_rtc_parti_status_changed) if _notif_rtc_audio_parti_updated != EOS.NotificationIdInvalid: EOS.RTCAudio.RTCAudioInterface.remove_notify_participant_updated(_notif_rtc_audio_parti_updated) if _notif_rtc_data_parti_updated != EOS.NotificationIdInvalid: EOS.RTCData.RTCDataInterface.remove_notify_participant_updated(_notif_rtc_data_parti_updated) if _notif_rtc_data_data_received != EOS.NotificationIdInvalid: EOS.RTCData.RTCDataInterface.remove_notify_data_received(_notif_rtc_data_data_received) func _disconnect_signal_if_connected(p_obj, p_signal_name, p_callback): if p_obj.is_connected(p_signal_name, p_callback): p_obj.disconnect(p_signal_name, p_callback) #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobby.gd.uid ================================================ uid://dk65c5tvqlm1a ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobbymember.gd ================================================ ## A high-level lobby member wrapper for EOSG class_name HLobbyMember extends BaseClass #region Signals ## Emitted when the member's rtc state changes signal rtc_state_updated #endregion #region Public vars var product_user_id: String var epic_account_id: String var display_name: String ## [Array] of [Dictionary] having {"key": String, "value": Variant, "visibility": [enum EOS.Lobby.LobbyAttributeVisibility] } var attributes = [] var rtc_state = { is_in_rtc_room = false, is_talking = false, is_locally_muted = false, is_hard_muted = false, is_audio_output_disabled = false, } #endregion #region Private vars var _attributes_to_add = [] var _lobby: HLobby var _log = HLog.logger("HLobbyMember") #endregion #region Built-in methods func _init(p_lobby: HLobby) -> void: super._init("HLobbyMember") _lobby = p_lobby #endregion #region Public methods ## Returns the member's attribute as [Dictionary] or empty [Dictionary] if not found func get_attribute(key: String): for attr in attributes: if attr.key == key: return attr return {} ## Returns true if this member is muted func is_muted() -> bool: if HAuth.product_user_id == product_user_id: return rtc_state.is_audio_output_disabled else: return rtc_state.is_locally_muted ## Returns true if this member is hard muted func is_hard_muted() -> bool: return rtc_state.is_hard_muted ## Returns true if this member is the lobby owner func is_owner() -> bool: return _lobby.owner_product_user_id == product_user_id ## Mute/Unmute this member. ## If the member is the current user, it will mute/unmute the audio output. ## If the member is a remote user, it will locally mute/unmute the remote user's audio. func toggle_mute_member_async() -> bool: var _is_muted = is_muted() var action = "Unmuting" if _is_muted else "Muting" if not _lobby or not _lobby.rtc_room_name: return false if HAuth.product_user_id == product_user_id: # Toggle our mute/unmute status _log.debug("%s ourself" % action) var opts := EOS.RTCAudio.UpdateSendingOptions.new() opts.room_name = _lobby.rtc_room_name opts.audio_status = EOS.RTCAudio.AudioStatus.Enabled if _is_muted else EOS.RTCAudio.AudioStatus.Disabled EOS.RTCAudio.RTCAudioInterface.update_sending(opts) var ret = await IEOS.rtc_audio_interface_update_sending_callback if not EOS.is_success(ret): _log.error("Failed to update audio sending: result_code=%s" % EOS.result_str(ret)) return false var did_update = HLog._check_diff_and_set_dict(rtc_state, "is_audio_output_disabled", ret.audio_status == EOS.RTCAudio.AudioStatus.Disabled, "member:" + product_user_id) if did_update: rtc_state_updated.emit() _lobby.lobby_updated.emit() return true else: _log.debug("%s member: product_user_id=%s" % [action, product_user_id]) # Locally mute the remote user's audio var opts := EOS.RTCAudio.UpdateReceivingOptions.new() opts.room_name = _lobby.rtc_room_name opts.participant_id = product_user_id opts.audio_enabled = _is_muted EOS.RTCAudio.RTCAudioInterface.update_receiving(opts) var ret = await IEOS.rtc_audio_interface_update_receiving_callback if not EOS.is_success(ret): _log.error("Failed to update audio receiving: result_code=%s" % EOS.result_str(ret)) return false var did_update = HLog._check_diff_and_set_dict(rtc_state, "is_locally_muted", ret.audio_enabled == false, "member:" + product_user_id) if did_update: rtc_state_updated.emit() _lobby.lobby_updated.emit() return true ## Hard mute this member in the lobby, can't speak but can hear other members of the lobby func toggle_hard_mute_member_async() -> bool: if not _lobby.is_owner(): return false var _is_hard_muted = is_hard_muted() var action = "Un hard-muting" if _is_hard_muted else "Hard-muting" _log.debug("%s member: product_user_id=%s" % [action, product_user_id]) var opts := EOS.Lobby.HardMuteMemberOptions.new() opts.lobby_id = _lobby.lobby_id opts.target_user_id = product_user_id opts.hard_mute = not _is_hard_muted EOS.Lobby.LobbyInterface.hard_mute_member(opts) var ret = await IEOS.lobby_interface_hard_mute_member_callback if not EOS.is_success(ret): _log.error("Failed to %s member: result_code=%s" % [action, EOS.result_str(ret)]) return false rtc_state.is_hard_muted = not _is_hard_muted rtc_state_updated.emit() _lobby.lobby_updated.emit() return true ## Kick this member from the lobby. Only lobby owner can kick a member. func kick_member_async() -> bool: if not _lobby.is_owner(): return false _log.debug("Kicking member: product_user_id=%s" % product_user_id) var opts = EOS.Lobby.KickMemberOptions.new() opts.lobby_id = _lobby.lobby_id opts.target_user_id = product_user_id EOS.Lobby.LobbyInterface.kick_member(opts) var ret = await IEOS.lobby_interface_kick_member_callback if not EOS.is_success(ret): _log.error("Failed to kick member: result_code=%s" % EOS.result_str(ret)) return false _log.debug("Kick member was successful: product_user_id=%s" % product_user_id) return true ## Promote another member to the lobby owner. Only the lobby owner can promote a member. func promote_member_async() -> bool: if not _lobby.is_owner(): return false _log.debug("Promoting member: product_user_id=%s" % product_user_id) var opts = EOS.Lobby.PromoteMemberOptions.new() opts.lobby_id = _lobby.lobby_id opts.target_user_id = product_user_id EOS.Lobby.LobbyInterface.promote_member(opts) var ret = await IEOS.lobby_interface_promote_member_callback if not EOS.is_success(ret): _log.error("Failed to promote member: result_code=%s" % EOS.result_str(ret)) return false _log.debug("Promote member was successful: product_user_id=%s" % product_user_id) return true ## Returns true if the member is the current logged in user func is_self() -> bool: return product_user_id == HAuth.product_user_id #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlobbymember.gd.uid ================================================ uid://brdkf3u56ylbk ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlog.gd ================================================ ## Used for logging by HEOS classes ## ## See also [EOS.Logging] for EOS SDK logging class_name HLog extends RefCounted enum LogLevel { OFF, ERROR, WARN, INFO, DEBUG, VERBOSE, } ## Current log level static var log_level = LogLevel.INFO ## Returns a new logger for the given class name static func logger(clazz_name: String) -> HEOSLogger: return HEOSLogger.new(clazz_name) static func _log_level_str(level: LogLevel) -> String: match level: LogLevel.VERBOSE: return "VERBOSE" LogLevel.DEBUG: return "DEBUG" LogLevel.INFO: return "INFO" LogLevel.WARN: return "WARN" LogLevel.ERROR: return "ERROR" return "" static func _check_diff_and_set(p_obj: Object, p_key, p_value, p_name = "") -> bool: if p_key in p_obj: var orig = p_obj.get(p_key) p_obj.set(p_key, p_value) if orig != p_value: if log_level >= LogLevel.VERBOSE: print_rich("[color=yellow]CHANGED %s[/color]: %s = %s -> %s" % [p_name, p_key, orig, p_value]) return true return false static func _check_diff_and_set_dict(p_dict: Dictionary, p_key, p_value, p_name = "") -> bool: if p_dict.has(p_key): var orig = p_dict[p_key] p_dict[p_key] = p_value if orig != p_value: if log_level >= LogLevel.VERBOSE: print_rich("[color=yellow]CHANGED %s[/color]: %s = %s -> %s" % [p_name, p_key, orig, p_value]) return true return false static func _check_attr_diff(orig_attrs: Array, new_attrs: Array, p_name = "") -> void: if log_level < LogLevel.VERBOSE: return var orig_map: Dictionary = {} for attr in orig_attrs: orig_map[attr.key] = attr var new_map: Dictionary = {} for attr in new_attrs: new_map[attr.key] = attr for id in orig_map.keys(): if not new_map.has(id): print_rich("[color=yellow]CHANGED %s[/color]: attr removed: %s = (%s,%s)" % [p_name, id, orig_map[id].value, orig_map[id].visibility]) else: var old_attr = orig_map[id] var new_attr = new_map[id] if not (old_attr.value == new_attr.value and old_attr.visibility == new_attr.visibility): print_rich("[color=yellow]CHANGED %s[/color]: attr updated: %s = (%s,%s) -> (%s,%s)" % [p_name, id, old_attr.value, old_attr.visibility, new_attr.value, new_attr.visibility]) for id in new_map.keys(): if not orig_map.has(id): print_rich("[color=yellow]CHANGED %s[/color]: attr added: %s = (%s,%s)" % [p_name, id, new_map[id].value, new_map[id].visibility]) ## Used internally by HEOS classes for logging class HEOSLogger extends RefCounted: var clazz_name: String func _init(p_clazz_name) -> void: clazz_name = p_clazz_name func verbose(msg: String) -> void: _log(LogLevel.VERBOSE, msg) func debug(msg: String) -> void: _log(LogLevel.DEBUG, msg) func info(msg: String) -> void: _log(LogLevel.INFO, msg) func warn(msg: String) -> void: _log(LogLevel.WARN, msg) func error(msg: String) -> void: _log(LogLevel.ERROR, msg) func _log(level: LogLevel, msg: String) -> void: if level <= HLog.log_level: var timestamp = Time.get_datetime_string_from_system(true, true) print("%s [%s] [%s] %s" % [timestamp, HLog._log_level_str(level), clazz_name, msg]) ================================================ FILE: sample/addons/epic-online-services-godot/heos/hlog.gd.uid ================================================ uid://cke8udaaf4kka ================================================ FILE: sample/addons/epic-online-services-godot/heos/hp2p.gd ================================================ extends Node #region Public vars #endregion #region Private vars var _log = HLog.logger("HP2P") #endregion #region Public methods ## Returns the current user's NAT type func get_nat_type_async() -> EOS.P2P.NATType: _log.debug("Getting nat type...") EOS.P2P.P2PInterface.query_nat_type() var ret = await IEOS.p2p_interface_query_nat_type_callback if not EOS.is_success(ret): _log.error("Failed to get nat type: %s" % EOS.result_str(ret)) else: _log.debug("Got nat type: %s" % ret.nat_type) return ret.nat_type ## Get the current relay control setting func get_relay_control() -> EOS.P2P.RelayControl: var ret = EOS.P2P.P2PInterface.get_relay_control() if not EOS.is_success(ret): _log.error("Failed to get relay control: %s" % EOS.result_str(ret)) return ret.relay_control ## Set how relay servers are to be used func set_relay_control(relay_control: EOS.P2P.RelayControl) -> EOS.Result: return EOS.P2P.P2PInterface.set_relay_control(relay_control) ## Get the current chosen port and the amount of other ports to try above the chosen port if the chosen port is unavailable. ## Returns a [Dictionary] with the following keys: ## - result_code: EOS.Result ## - port: int ## - max_additional_ports_to_try: int func get_port_range() -> Dictionary: return EOS.P2P.P2PInterface.get_port_range() ## Set configuration options related to network ports. func set_port_range(port: int, max_additional_ports_to_try: int) -> EOS.Result: var opts = EOS.P2P.SetPortRangeOptions.new() opts.port = port opts.max_additional_ports_to_try = max_additional_ports_to_try return EOS.P2P.P2PInterface.set_port_range(opts) ## Gets the current cached information related to the incoming and outgoing packet queues ## Returns a [Dictionary] with the following keys: ## - result_code: EOS.Result ## - incoming_packet_queue_current_packet_count: int ## - incoming_packet_queue_current_size_bytes: int ## - incoming_packet_queue_max_size_bytes: int ## - outgoing_packet_queue_current_packet_count: int ## - outgoing_packet_queue_current_size_bytes: int ## - outgoing_packet_queue_max_size_bytes: int func get_packet_queue_info(): return EOS.P2P.P2PInterface.get_packet_queue_info() ## Sets the maximum packet queue sizes that packets waiting to be sent or received can use. func set_packet_queue_size(incoming_packet_queue_max_size_bytes: int, outgoing_packet_queue_max_size_bytes: int) -> EOS.Result: var opts = EOS.P2P.SetPacketQueueSizeOptions.new() opts.incoming_packet_queue_max_size_bytes = incoming_packet_queue_max_size_bytes opts.outgoing_packet_queue_max_size_bytes = outgoing_packet_queue_max_size_bytes return EOS.P2P.P2PInterface.set_packet_queue_size(opts) #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hp2p.gd.uid ================================================ uid://u6ae5fpm6l8q ================================================ FILE: sample/addons/epic-online-services-godot/heos/hplatform.gd ================================================ extends Node #region Signals ## Emitted when the EOS platform was initialized successfully signal platform_initialized ## Emitted when the EOS platform was created successfully signal platform_created ## Emitted when the EOS SDK produces a log message ## Use this signal to handle the log however you want signal log_msg(msg: EOS.Logging.LogMessage) #endregion #region Constants ## Number of times to retry initializing the EOS SDK const INITIALIZE_RETRY_COUNT = 10 ## Delay between retries initializing the EOS SDK in seconds const INITIALIZE_RETRY_DElAY_SEC = 0.2 ## Number of times to retry creating the EOS platform const CREATE_RETRY_COUNT = 10 ## Delay between retries creating the EOS platform in seconds const CREATE_RETRY_DElAY_SEC = 0.2 #endregion #region Public vars ## Absolute path to the folder that is going to be used for caching temporary data var cache_directory := ProjectSettings.globalize_path("user://eosg-cache") ## Platform creation flags. This is a bitwise (union) of [enum EOS.Platform.PlatformFlags] var flags: int = 0 ## Set to true if its a dedicated game server var is_server: bool ## Override country code for the logged-in user var override_country_code: String ## Override local code for the logged-in user var override_locale_code: String ## Budget, measured in milliseconds, for EOS_Platform_Tick to do its work. Set it according to your desired FPS. var tick_budget_in_milliseconds: int ## Number of seconds for a task to wait for the network to become available before timing out with an EOS_TimedOut error var task_network_timeout_seconds = null # float ## Configures RTC behavior upon entering to any background application statuses See [enum EOS.Platform.RTCBackgroundMode] var rtc_options_background_mode = null #endregion #region Private vars var _log = HLog.logger("HPlatform") #endregion #region Built-in methods func _ready() -> void: IEOS.logging_interface_callback.connect(_on_logging_callback) #endregion #region Public methods ## Easy setup for EOS. Returns true if EOS setup is success. func setup_eos_async(p_creds: HCredentials) -> bool: _log.debug("Setting up EOS") if not p_creds.product_name: _log.error("HCredentials.product_name cannot be empty") return false var init_opts = EOS.Platform.InitializeOptions.new() init_opts.product_name = p_creds.product_name init_opts.product_version = p_creds.product_version var init_ret := await initialize_async(init_opts) if not EOS.is_success(init_ret): return false var create_opts = EOS.Platform.CreateOptions.new() create_opts.product_id = p_creds.product_id create_opts.sandbox_id = p_creds.sandbox_id create_opts.deployment_id = p_creds.deployment_id create_opts.client_id = p_creds.client_id create_opts.client_secret = p_creds.client_secret create_opts.encryption_key = p_creds.encryption_key create_opts.cache_directory = cache_directory if not flags: _log.debug("Using default flags for creating platform") if OS.get_name() == "Windows": create_opts.flags = EOS.Platform.PlatformFlags.WindowsEnableOverlayOpengl else: create_opts.flags = flags create_opts.is_server = is_server create_opts.override_country_code = override_country_code create_opts.override_locale_code = override_locale_code if not tick_budget_in_milliseconds: var max_fps = max(Engine.get_max_fps()*1.0, 60.0) var budget_ms = floori((0.3 * 1000) / max_fps) budget_ms = max(budget_ms, 2) # at least 2ms _log.debug("Using default tick_budget_in_milliseconds as %s" % budget_ms) create_opts.tick_budget_in_milliseconds = budget_ms else: create_opts.tick_budget_in_milliseconds = tick_budget_in_milliseconds create_opts.task_network_timeout_seconds = task_network_timeout_seconds create_opts.rtc_options.background_mode = rtc_options_background_mode var is_success := await create_platform_async(create_opts) if not is_success: return false return true ## Initialize the EOS SDK func initialize_async(opts: EOS.Platform.InitializeOptions) -> EOS.Result: _log.debug("Initializing EOS SDK") var res := EOS.Platform.PlatformInterface.initialize(opts) var retry_count = INITIALIZE_RETRY_COUNT while not EOS.is_success(res) and retry_count > 0: if not EOS.is_success(res) and retry_count > 0: _log.debug("Failed to initialize EOS SDK: result_code=%s, retry_count=%s" % [EOS.result_str(res), INITIALIZE_RETRY_COUNT - retry_count + 1]) res = EOS.Platform.PlatformInterface.initialize(opts) retry_count -= 1 await get_tree().create_timer(INITIALIZE_RETRY_DElAY_SEC).timeout if not (EOS.is_success(res) or res == EOS.Result.AlreadyConfigured): _log.error("Failed to initialize EOS SDK: %s" % EOS.result_str(res)) return res _log.debug("EOS SDK initialized: result_code=%s" % EOS.result_str(res)) if EOS.is_success(res): platform_initialized.emit() return res ## Set the logging level for the EOS SDK func set_eos_log_level(log_category: EOS.Logging.LogCategory, log_level: EOS.Logging.LogLevel) -> EOS.Result: var log_cat_str = EOS.Logging.LogCategory.find_key(log_category) _log.verbose("Setting log level: log_category=%s, log_level=%s" % [log_cat_str, log_level]) var res := EOS.Logging.set_log_level(log_category, log_level) if not EOS.is_success(res): _log.error("Failed to set log level: %s" % EOS.result_str(res)) return res ## Create the EOS Platform func create_platform_async(opts: EOS.Platform.CreateOptions) -> bool: _log.debug("Creating EOS Platform") var res := EOS.Platform.PlatformInterface.create(opts) var retry_count = CREATE_RETRY_COUNT while not EOS.is_success(res) and retry_count > 0: if not EOS.is_success(res) and retry_count > 0: _log.debug("Failed to create EOS Platform: result_code=%s, retry_count=%s" % [EOS.result_str(res), CREATE_RETRY_COUNT - retry_count + 1]) res = EOS.Platform.PlatformInterface.create(opts) retry_count -= 1 await get_tree().create_timer(CREATE_RETRY_DElAY_SEC).timeout if not EOS.is_success(res): _log.error("Failed to create EOS Platform: %s" % EOS.result_str(res)) return res platform_created.emit() return res #endregion #region Private methods func _on_logging_callback(p_msg: Dictionary): var msg = EOS.Logging.LogMessage.from(p_msg) as EOS.Logging.LogMessage log_msg.emit(msg) #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hplatform.gd.uid ================================================ uid://yl2muvqrdiud ================================================ FILE: sample/addons/epic-online-services-godot/heos/hsessions.gd ================================================ extends Node #region Public vars var max_search_results = 25 #endregion #region Private vars var _log = HLog.logger("HSessions") #endregion #region Public methods func create_session(opts: EOS.Sessions.CreateSessionModificationOptions) -> EOSGSessionModification: _log.debug("Creating session...") var ret = EOS.Sessions.SessionsInterface.create_session_modification(opts) if not EOS.is_success(ret): _log.error("Failed to create session: %s" % EOS.result_str(ret)) return null var session_mod = ret.session_modification _log.debug("Session created session_name=%s, session_id=%s" % [opts.session_name, opts.session_id]) return session_mod func update_session_async(p_session: EOSGSessionModification) -> bool: _log.debug("Updating session...") var opts := EOS.Sessions.UpdateSessionOptions.new() opts.session_modification = p_session EOS.Sessions.SessionsInterface.update_session(opts) var ret = await IEOS.sessions_interface_update_session_callback if not EOS.is_success(ret): _log.error("Failed to update session: %s" % EOS.result_str(ret)) return false _log.debug("Session updated session_name=%s, session_id=%s" % [ret.session_name, ret.session_id]) return true func get_active_session(p_session_name: String) -> EOSGActiveSession: _log.debug("Getting active session... session_name=%s" % p_session_name) var opts = EOS.Sessions.CopyActiveSessionDetailsOptions.new() opts.session_name = p_session_name var ret = EOS.Sessions.SessionsInterface.copy_active_session_details(opts) if not EOS.is_success(ret): _log.error("Failed to get active session: %s" % EOS.result_str(ret)) return null _log.debug("Got active session session_name=%s" % p_session_name) return ret.active_session func join_async(session: EOSGSessionDetails, session_name: String, presence_enabled = false) -> bool: if not session: return false var session_id = session.copy_info().info.session_id _log.debug("Joining session... session_name=%s, session_id=%s" % [session_name, session_id]) var opts = EOS.Sessions.JoinSessionOptions.new() opts.session_details = session opts.presence_enabled = presence_enabled opts.session_name = session_name EOS.Sessions.SessionsInterface.join_session(opts) var ret = await IEOS.sessions_interface_join_session_callback if not EOS.is_success(ret): _log.error("Failed to join session session_id=%s: result_code=%s" % [session_id, EOS.result_str(ret)]) return false _log.debug("Session joined: session_id=%s" % session_id) return true func search_by_attribute_async(attributes): if typeof(attributes) == TYPE_DICTIONARY: attributes = [attributes] _log.debug("Searching for session by attributes: attributes=%s" % str(attributes)) var opts = EOS.Sessions.CreateSessionSearchOptions.new() opts.max_search_results = max_search_results var search := create_search(opts) if not search: return null for attr in attributes: if attr.has("comparison"): search.set_parameter(attr.key, attr.value, attr.comparison) else: search.set_parameter(attr.key, attr.value, EOS.ComparisonOp.Equal) return await search_async(search) ## (Advanced) func create_search(opts: EOS.Sessions.CreateSessionSearchOptions) -> EOSGSessionSearch: _log.debug("Creating session search...") var create_ret = EOS.Sessions.SessionsInterface.create_session_search(opts) if not EOS.is_success(create_ret): _log.error("Failed to create session search: result_code=%s" % EOS.result_str(create_ret)) return null return create_ret.session_search ## (Advanced) func search_async(session_search: EOSGSessionSearch): _log.debug("Searching for sessions...") session_search.find(HAuth.product_user_id) var search_ret = await IEOS.session_search_find_callback if not EOS.is_success(search_ret): _log.error("Failed to search for sessions: result_code=%s" % EOS.result_str(search_ret)) return null var count = session_search.get_search_result_count() _log.debug("Found sessions: count=%s" % count) var sessions: Array = [] for search_idx in count: var copy_ret = session_search.copy_search_result_by_index(search_idx) if not EOS.is_success(copy_ret): _log.error("Failed to copy session search result by index: result_code=%s" % EOS.result_str(copy_ret)) continue var details = copy_ret.session_details sessions.append(details) return sessions #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hsessions.gd.uid ================================================ uid://bieh2rxxahuu2 ================================================ FILE: sample/addons/epic-online-services-godot/heos/hstats.gd ================================================ extends Node #region Signals #endregion #region Public vars #endregion #region Private vars var _log = HLog.logger("HStats") #endregion #region Built-in methods func _ready() -> void: pass #endregion #region Public methods ## Returns an [Array] of stats for the current user. ## Each stat has: ## name: String - name of the stat ## value: int - current value for the stat ## start_time: int - unix timestamp or [EOS.Stats.STATS_TIME_UNDEFINED] ## end_time: int - unix timestamp or [EOS.Stats.STATS_TIME_UNDEFINED] func get_stats_async() -> Array: _log.debug("Getting stats for user...") var query_opts = EOS.Stats.QueryStatsOptions.new() EOS.Stats.StatsInterface.query_stats(query_opts) var ret = await IEOS.stats_interface_query_stats_callback if not EOS.is_success(ret): _log.error("Failed to query stats: result_code=%s" % EOS.result_str(ret)) return [] var stats_count: int = EOS.Stats.StatsInterface.get_stats_count(EOS.Stats.GetStatsCountOptions.new()) _log.debug("Got stats: count=%s" % stats_count) var stats = [] for i in range(stats_count): var copy_opts = EOS.Stats.CopyStatByIndexOptions.new() copy_opts.stat_index = i var copy_ret = EOS.Stats.StatsInterface.copy_stat_by_index(copy_opts) if not EOS.is_success(copy_ret): _log.error("Failed to copy stat: result_code=%s" % EOS.result_str(copy_ret)) else: stats.append(copy_ret.stat) return stats ## Ingest a stat by the specified amount func ingest_stat_async(stat_name: String, _ingest_amount: int) -> EOS.Result: _log.debug("Ingesting stat: name=%s, amount=%s" % [stat_name, _ingest_amount]) var opts = EOS.Stats.IngestStatOptions.new() opts.local_user_id = HAuth.product_user_id opts.target_user_id = HAuth.product_user_id opts.stats = [ {stat_name = stat_name, ingest_amount = _ingest_amount}, ] EOS.Stats.StatsInterface.ingest_stat(opts) var ret = await IEOS.stats_interface_ingest_stat_callback if not EOS.is_success(ret): _log.error("Failed to ingest stat: result_code=%s" % EOS.result_str(ret)) return ret.result_code #endregion #region Private methods #endregion ================================================ FILE: sample/addons/epic-online-services-godot/heos/hstats.gd.uid ================================================ uid://dbckdip83oone ================================================ FILE: sample/addons/epic-online-services-godot/plugin.cfg ================================================ [plugin] name = "Epic Online Services Godot 4.2+ (EOSG)" description = "Unofficial Epic Online Services for Godot 4.2+ (includes demo project)" author = "3ddelano" version = "2.2.9" script = "plugin.gd" ================================================ FILE: sample/addons/epic-online-services-godot/plugin.gd ================================================ # Copyright (c) 2023-present Delano Lourenco # https://github.com/3ddelano/epic-online-services-godot/ # MIT License @tool extends EditorPlugin var _export_plugin = preload("res://addons/epic-online-services-godot/export_plugin.gd").new() func _enable_plugin(): add_autoload_singleton("EOSGRuntime", "res://addons/epic-online-services-godot/runtime.gd") add_autoload_singleton("HPlatform", "res://addons/epic-online-services-godot/heos/hplatform.gd") add_autoload_singleton("HAuth", "res://addons/epic-online-services-godot/heos/hauth.gd") add_autoload_singleton("HAchievements", "res://addons/epic-online-services-godot/heos/hachievements.gd") add_autoload_singleton("HFriends", "res://addons/epic-online-services-godot/heos/hfriends.gd") add_autoload_singleton("HStats", "res://addons/epic-online-services-godot/heos/hstats.gd") add_autoload_singleton("HLeaderboards", "res://addons/epic-online-services-godot/heos/hleaderboards.gd") add_autoload_singleton("HLobbies", "res://addons/epic-online-services-godot/heos/hlobbies.gd") add_autoload_singleton("HP2P", "res://addons/epic-online-services-godot/heos/hp2p.gd") add_autoload_singleton("HSessions", "res://addons/epic-online-services-godot/heos/hsessions.gd") add_export_plugin(_export_plugin) func _disable_plugin(): remove_autoload_singleton("EOSGRuntime") remove_export_plugin(_export_plugin) remove_autoload_singleton("HPlatform") remove_autoload_singleton("HAuth") remove_autoload_singleton("HAchievements") remove_autoload_singleton("HFriends") remove_autoload_singleton("HStats") remove_autoload_singleton("HLeaderboards") remove_autoload_singleton("HLobbies") remove_autoload_singleton("HP2P") ================================================ FILE: sample/addons/epic-online-services-godot/plugin.gd.uid ================================================ uid://c2bh2munh5110 ================================================ FILE: sample/addons/epic-online-services-godot/runtime.gd ================================================ # Copyright (c) 2023-present Delano Lourenco # https://github.com/3ddelano/epic-online-services-godot/ # MIT License ## Runtime controller for Epic Online Services. This is available as the EOSGRuntime autoload. class_name EOSGRuntime_ extends Node ## The Product User ID of the most recent logged in user. var local_product_user_id: String ## The Epic Account ID of the most recent logged in user. var local_epic_account_id: String func _ready() -> void: IEOS.auth_interface_login_callback.connect(func (data: Dictionary): if data.local_user_id != "": local_epic_account_id = data.local_user_id ) IEOS.connect_interface_login_callback.connect(func (data: Dictionary): if data.local_user_id != "": local_product_user_id = data.local_user_id ) IEOS.auth_interface_logout_callback.connect(_on_logout) func _on_logout(data: Dictionary): local_epic_account_id = "" local_product_user_id = "" func _process(_delta: float): IEOS.tick() ================================================ FILE: sample/addons/epic-online-services-godot/runtime.gd.uid ================================================ uid://7saywdx0ipep ================================================ FILE: sample/components/StyledPopupWindow/StyledPopupWindow.gd ================================================ class_name StyledPopupWindow extends PopupPanel @onready var _close_btn = %CloseBtn @onready var _title = %Title func _ready() -> void: visible = false _close_btn.pressed.connect(hide) _title.text = title ================================================ FILE: sample/components/StyledPopupWindow/StyledPopupWindow.gd.uid ================================================ uid://467nklflb1n8 ================================================ FILE: sample/components/StyledPopupWindow/StyledPopupWindow.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://dkprjbptror6n"] [ext_resource type="Theme" uid="uid://dwns7wwoyi1hy" path="res://components/StyledPopupWindow/StyledPopupWindowTheme.tres" id="1_72hut"] [ext_resource type="Script" uid="uid://467nklflb1n8" path="res://components/StyledPopupWindow/StyledPopupWindow.gd" id="1_xtvca"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pe58l"] content_margin_left = 7.0 content_margin_right = 6.0 bg_color = Color(0.196078, 0.196078, 0.196078, 1) border_width_left = 1 border_width_top = 1 border_width_right = 1 border_width_bottom = 1 border_color = Color(0.192157, 0.192157, 0.192157, 1) [node name="StyledPopupWindow" type="PopupPanel"] disable_3d = true size = Vector2i(250, 100) visible = true theme = ExtResource("1_72hut") script = ExtResource("1_xtvca") [node name="VB" type="VBoxContainer" parent="."] offset_left = 4.0 offset_top = 4.0 offset_right = 246.0 offset_bottom = 96.0 theme_type_variation = &"Panel_TitleBar" [node name="PanelContainer" type="PanelContainer" parent="VB"] layout_mode = 2 theme_type_variation = &"Panel_TitleBar" [node name="TitleBar" type="HBoxContainer" parent="VB/PanelContainer"] layout_mode = 2 [node name="Title" type="Label" parent="VB/PanelContainer/TitleBar"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 text = "POPUP WINDOW TITLE" horizontal_alignment = 1 [node name="CloseBtn" type="Button" parent="VB/PanelContainer/TitleBar"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 8 mouse_default_cursor_shape = 2 theme_override_colors/font_color = Color(1, 0, 0, 1) theme_override_colors/font_pressed_color = Color(1, 0, 0, 1) theme_override_colors/font_hover_color = Color(1, 0.345098, 0.345098, 1) theme_override_colors/font_hover_pressed_color = Color(1, 0, 0, 1) theme_override_styles/normal = SubResource("StyleBoxFlat_pe58l") text = "x" [node name="MC" type="MarginContainer" parent="VB"] layout_mode = 2 theme_override_constants/margin_left = 20 theme_override_constants/margin_top = 20 theme_override_constants/margin_right = 20 theme_override_constants/margin_bottom = 20 [node name="VB" type="VBoxContainer" parent="VB/MC"] layout_mode = 2 ================================================ FILE: sample/components/StyledPopupWindow/StyledPopupWindowTheme.tres ================================================ [gd_resource type="Theme" load_steps=26 format=4 uid="uid://dwns7wwoyi1hy"] [ext_resource type="Texture2D" uid="uid://bca7dt4mbam7m" path="res://components/StyledPopupWindow/checkbox_checked.png" id="1_q2c85"] [ext_resource type="Texture2D" uid="uid://b81awaq6i80hc" path="res://components/StyledPopupWindow/checkbox_checked_disabled.png" id="2_s6mpo"] [ext_resource type="Texture2D" uid="uid://ckxml3rkfinlo" path="res://components/StyledPopupWindow/checkbox_unchecked.png" id="3_gg3vm"] [ext_resource type="Texture2D" uid="uid://bt7f80d71sprw" path="res://components/StyledPopupWindow/checkbox_unchecked_disabled.png" id="4_s07s3"] [ext_resource type="FontFile" uid="uid://ij5t8dy1m6e6" path="res://fonts/Roboto-Regular.ttf" id="5_kvsd8"] [sub_resource type="Image" id="Image_bboio"] data = { "data": PackedByteArray("/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP98//////9O/wD/AP8A/wD/AP+M//////9E/wD/AP8A/wD/AP8A/2D/1v/4/9z/bP8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/1z///+E/wD/AP8A/wD/AP8A/1P/z//4/+r/lv8L/wD/AP8A/wD/AP9V////vv8A/wD/C//h//j/Kv8A/wD/AP8A/wD/AP9T/8n/9v/x/8f/Lv8A/wD/AP8A/wD/AP9Q/8n/9//x/7P/K/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP+w/9L/Ev+q//X/iP8A/wD/AP8A/7D/3P89/8T/9//l/5X/C/8A/wD/AP8A/wD/HP+R/9f/9v/l/6D/Ff8A/wD/AP8A/wD/sP/c/0L/zP/5/+f/iv8V/6P/7//v/7X/Hv8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP9U/7z/7v/5/+D/m/8g/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/2v///+y/wH/AP8A/wD/AP98///////////////0/wD/AP8A/wD/lf/s/x//AP8A/wD/AP8A/wb/8P9U/wD/AP8A/wD/AP8A/wD/AP8A/zz/rf/l//j/4P+o/zb/AP8A/wD/AP8A/zr/vf/u//T/1P+A/wD/AP8A/wD/AP8A/wD/AP8A/3z//////6P/AP8A/wD/AP8B/+L//////0T/AP8A/wD/AP8A/1b////m/53/2////2j/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/XP///4T/AP8A/wD/AP8A/1f////e/3z/pP///6j/AP8A/wD/AP8A/wD/rP///1//AP+N////ev8A/wD/AP8A/wD/AP9d////8v+l/6T/3v8S/wD/AP8A/wD/AP9Y////7P+b/6j/+//w/yT/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/7D/9v/B//P/x/9i/wD/AP8A/wD/sP////X/lP9r/8P///+I/wD/AP8A/wD/AP8U/8v/lv94/6D///+s/wD/AP8A/wD/AP+w////8v+K/3H/5v///+z/wP9t/6v///+0/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP+P////+v++/6r/1v///+//NP8A/wD/AP8A/wD/fP///2D/AP8A/z3/+v/X/w7/AP8A/wD/AP8A/3z///+//5z/nP+c/5T/AP8A/wD/AP+l//j/Jf8A/wD/AP8A/wD/Tf///1T/AP8A/wD/AP8A/wD/AP8A/23//f/9/8T/rv/V////T/8A/wD/AP8A/w7/8//r/4T/hP+8/5n/AP8A/wD/AP8A/wD/AP8A/wD/fP///8r/8f8H/wD/AP8A/zv////f////RP8A/wD/AP8A/wD/0//7/yP/AP8S/+3/5/8C/wD/AP8A/wD/fP///2D/AP8A/wD/AP9c////hP8A/wD/AP8A/wD/3P/7/yL/AP8A/6r///8r/wD/AP8A/wD/AP8X/+3/6/9F//z/zf8E/wD/AP8A/wD/AP8A/+D//v80/wD/AP8A/wD/AP8A/wD/AP8A/9///f8p/wD/AP9i////ov8A/wD/AP8A/wD/AP8A/wD/AP8A/y////+o/wD/AP8A/wD/sP///9D/Fv8A/wD/AP8A/wD/AP+w////hf8A/wD/Ff///9H/AP8A/wD/AP8A/wD/AP8A/wD/AP/T//H/AP8A/wD/AP8A/7D///9//wD/AP9k////5f8F/wD/BP/q//T/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP9I////5/8p/wD/AP8A/3P////a/wP/AP8A/wD/AP98////YP8A/xz/6P/v/yX/AP8A/wD/AP8A/wD/fP///1z/AP8A/wD/AP8A/wD/AP8A/wH/CP8A/wD/AP8A/wD/aP/1/////////8z/AP8A/wD/AP8A/zP//P/z/z3/AP8A/wD/Kv8C/wD/AP8A/wD/MP///6//AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP98////gf///03/AP8A/wD/k//9/5L///9E/wD/AP8A/wD/F////8P/AP8A/wD/ov///zT/AP8A/wD/AP98////YP8A/wD/AP8A/1z///+E/wD/AP8A/wD/Hf///9L/FP8U/xT/dv///1r/AP8A/wD/AP8A/wD/Xf////z/+v8u/wD/AP8A/wD/AP8A/x7////O/wD/AP8A/wD/AP8A/wD/AP8A/x7////L/wD/AP8A/wf//v/k/wD/AP8A/wD/AP8B/33/5f/3/8L/Uv///6j/AP8A/wD/AP+w////UP8A/wD/AP8A/wD/AP8A/7D///8//wD/AP8A/+7/6v8A/wD/AP8A/wD/Hf+h/+L/+P/8//////8H/wD/AP8A/wD/sP///z3/AP8A/0L///+q/wD/AP8A/8r///8L/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/7L///9g/wD/AP8A/wD/AP/F////Sv8A/wD/AP8A/3z///9g/wf/yf/9/0j/AP8A/wD/AP8A/wD/AP98////XP8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP9K/7j///+c/2z/Vv8A/wD/AP8A/wD/oP///4D/AP8A/wD/AP8A/wD/AP8A/wD/AP8H/93///+6/0v/Av8A/wD/AP8A/wD/AP8A/wD/AP8A/3z///87//j/ov8A/wD/Av/n/7z/gP///0T/AP8A/wD/AP87////nf8A/wD/AP96////XP8A/wD/AP8A/3z///9g/wD/AP8A/wD/XP///4T/AP8A/wD/AP8w////////////////////c/8A/wD/AP8A/wD/AP8F/+r///+7/wD/AP8A/wD/AP8A/wD/MP///7b/AP8A/wD/AP8A/wD/AP8A/wD/Lv///7b/AP8A/wD/AP/v//f/AP8A/wD/AP8A/2z////p/5j/sv/x////qP8A/wD/AP8A/7D///8q/wD/AP8A/wD/AP8A/wD/sP///yr/AP8A/wD/7P/s/wD/AP8A/wD/Bf/T////iP89/yz/1P///wj/AP8A/wD/AP+w////Kv8A/wD/QP///5j/AP8A/wD/yP///wz/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/7P///xT/AP8A/wD/AP8A/3j///+F/wD/AP8A/wD/fP///2D/n////3T/AP8A/wD/AP8A/wD/AP8A/3z///9c/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/hP///1T/AP8A/wD/AP8A/wD/AP/l////Hv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/F/+f//r////Z/zT/AP8A/wD/AP8A/wD/AP8A/wD/fP///zH/s//w/wb/AP9C////Yv+G////RP8A/wD/AP8A/0v///+P/wD/AP8A/2z///9t/wD/AP8A/wD/fP//////////////////////hP8A/wD/AP8A/x7////T/0T/RP9E/0T/RP8e/wD/AP8A/wD/AP8A/33////p////Sf8A/wD/AP8A/wD/AP8h////zf8A/wD/AP8A/wD/AP8A/wD/AP8Z////zP8A/wD/AP8H//7/5P8A/wD/AP8A/wD/4//+/yr/AP8A/4b///+o/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP/s/+z/AP8A/wD/AP8y////u/8A/wD/AP/c////CP8A/wD/AP8A/7D///8o/wD/AP9A////mP8A/wD/AP/I////DP8A/wD/AP8A/7D///8o/wD/AP8A/wD/BP////b/AP8A/wD/AP8A/wD/W////5//AP8A/wD/AP98////yP////X/Ev8A/wD/AP8A/wD/AP8A/wD/fP///8L/oP+g/6D/a/8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP+E////VP8A/wD/AP8A/wD/AP8C//z/+v8C/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Gf+Q//7/5v8B/wD/AP8A/wD/AP8A/wD/AP98////M/9e////TP8A/5n/+P8P/4j///9E/wD/AP8A/wD/S////47/AP8A/wD/a////27/AP8A/wD/AP98////xv+k/6T/pP+k/8X///+E/wD/AP8A/wD/AP/c//X/IP8A/wD/AP8A/wD/AP8A/wD/AP8A/yz/+f/V/yD/8f/j/w//AP8A/wD/AP8A/wH/5//+/zL/AP8A/wL/Bv8A/wD/AP8A/wD/0//9/y3/AP8A/2L///+k/wD/AP8A/wD/Ff///83/AP8A/wD/Mv///6j/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/+z/7P8A/wD/AP8A/zv///+u/wD/AP8v//3///8I/wD/AP8A/wD/sP///yj/AP8A/0D///+Y/wD/AP8A/8j///8M/wD/AP8A/wD/sP///yj/AP8A/wD/AP8E////9f8A/wD/AP8A/wD/AP9b////nv8A/wD/AP8A/3z//////9P///+i/wD/AP8A/wD/AP8A/wD/AP98//////////////+s/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/4T///9U/wD/AP8A/wD/AP8A/wX////y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8F/wH/AP8A/wD/w////xH/AP8A/wD/AP8A/wD/AP8A/3z///80/w//+f+h/wT/7P+u/wD/iP///0T/AP8A/wD/AP87////nP8A/wD/AP96////Xv8A/wD/AP8A/3z///9g/wD/AP8A/wD/XP///4T/AP8A/wD/AP8A/1D//f/s/5X/g/+d/97/BP8A/wD/AP8A/wP/y//+/zv/AP9q////nf8A/wD/AP8A/wD/AP9t////8f+j/6P/4P80/wD/AP8A/wD/AP9J//3/7v+b/6b/+//z/yj/AP8A/wD/AP8s////tv8A/wD/AP8c////qP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/7P/s/wD/AP8A/wD/Df/t//v/jP+I/+z/4////wj/AP8A/wD/AP+w////KP8A/wD/QP///5j/AP8A/wD/yP///wz/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/6////xP/AP8A/wD/AP8A/3j///+F/wD/AP8A/wD/fP///4f/Cv/c////Sv8A/wD/AP8A/wD/AP8A/3z///9c/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/hP///1T/AP8A/wD/AP8A/wD/AP/t////Ef8A/wD/AP8A/wD/AP8A/wD/AP8A/zj/4P+W/3f/kv/7/9H/AP8A/wD/AP8A/wD/AP8A/wD/fP///zT/AP+1//D/T////1P/AP+I////RP8A/wD/AP8A/xP////B/wD/AP8A/5////85/wD/AP8A/wD/fP///2D/AP8A/wD/AP9c////hP8A/wD/AP8A/wD/AP9F/8D/9P/3/9//lv8B/wD/AP8A/wD/eP///5j/AP8A/wL/xv///0f/AP8A/wD/AP8A/wD/Zf/T//n/8P+7/xn/AP8A/wD/AP8A/wD/Rf/C//T/8v+1/y//AP8A/wD/AP8A/xb////J/wD/AP8A/y3///+o/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP/s/+z/AP8A/wD/AP8A/0P/1P/5/93/Yv9p////CP8A/wD/AP8A/7D///8o/wD/AP9A////mP8A/wD/AP/I////DP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP+x////X/8A/wD/AP8A/wD/xP///0r/AP8A/wD/AP98////YP8A/z///v/j/w7/AP8A/wD/AP8A/wD/fP///1z/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP95////Y/8A/wD/AP8A/wD/AP8A/7T///9e/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP+1/+n/+v/m/6T/Hv8A/wD/AP8A/wD/AP8A/wD/AP98////NP8A/2D////f//H/CP8A/4j///9E/wD/AP8A/wD/AP/K//r/IP8A/w//6v/u/wX/AP8A/wD/AP98////YP8A/wD/AP8A/1z///+E/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/m//v/If8A/wD/d////6j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/0f////l/yb/AP8A/wD/b////9r/A/8A/wD/AP8A/3z///9g/wD/AP+V////mf8A/wD/AP8A/wD/AP98////XP8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/0X////l/47/lP8A/wD/AP8A/wD/Tv///+X/KP8A/wD/AP8E/wb/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/3z///80/wD/Ef/6////n/8A/wD/iP///0T/AP8A/wD/AP8A/0n//v/k/5j/2P///3b/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/XP///4T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/3L////l/5P/qf/o////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP+P////+P+5/6b/0////+//NP8A/wD/AP8A/wD/fP///2D/AP8A/wz/4P/+/0H/AP8A/wD/AP8A/3z///9c/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP+F/+n/9v+1/wD/AP8A/wD/AP8A/5z////6/7v/pv++//L/MP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/fP///zT/AP8A/7b///9F/wD/AP+I////RP8A/wD/AP8A/wD/AP9W/9T/+f/g/3f/Af8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP9c////hP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Av+D/+j/9/+9/yb/5v+o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8A/wD/Vf+9/+//+v/h/5v/IP8A/wD/AP8A/wD/AP98////YP8A/wD/AP9F////3f8K/wD/AP8A/wD/fP///1z/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Af9h/8b/8//4/+P/rP8Y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/7L/7P8o/wD/AP8A/wD/xP///xj/AP8A/wD//P/c/wD/AP8A/wD/0f/+/xz/AP8A/wD/j////2D/AP8A/wD/AP9C/5j/mP+Y/1f/AP8A/wD/AP80/1v/Av8A/wD/AP8A/7b///8n/wD/AP+Y////sf8A/wD/Ev/+/8T/AP8A/wD/AP80///////////////4/wD/AP8A/wD/AP/r/9H/AP/X/+b/AP8A/wD/AP8H//T/9P/0//T/9P/0/93/AP8A/wD/AP8A/wL/BP8A/wD/AP8A/wD/FP8U/wD/AP8A/wj/IP8A/wD/AP8A/wD/AP8A/wD/e////zL/AP8A/wD/AP8A/wD/AP/r/9H/AP8A/wD/AP8G/yD/IP8g/yD/IP8g/yD/Cv8A/wD/AP8A/wD/AP8O/wb/AP8A/wD/AP8C/wD/AP8A/wD/C/99/3//EP8A/wD/AP8A/wD/AP8A/wD/If/w/z//AP8A/wD/AP8A/wD/AP8A/xT/nf/q//j/y/9F/wD/AP8A/wD/AP8E//H/5v8A/wD/AP/Y////cf8A/wD/Uf///4T/AP8A/wD/AP8A/wb/ef/L//D/7v+7/zD/AP8A/wD/AP8A/zD///9j/43/5v/1/8v/Q/8A/wD/AP8A/wD/AP8D/3v/2v/5/+v/s/8J/wD/AP8A/wD/AP8A/wD/4////0v/AP8A/wD/AP/E////GP8A/wD/AP/8/9z/AP8A/wD/AP90////cf8A/wD/Af/j//X/Df8A/wD/AP8A/3D/////////lP8A/wD/AP8A/+T///9L/wD/AP8A/wD/cf///2f/AP8A/97/9v/x/wT/AP9R////f/8A/wD/AP8A/xb/cP9w/3D/mf///87/AP8A/wD/AP8A/9j/vv8A/8P/0/8A/wD/AP8A/wH/IP8g/yD/IP8g/yD/Hf8A/wD/AP8A/wD/y////xv/AP8A/wD/AP+3/9D/Ev8A/wj/uf/Y/wP/AP8A/wD/AP8A/wD/AP9f////GP8A/wD/AP8A/wD/AP8A/9j/vv8A/wD/AP8A/zT///////////////////9U/wD/AP8A/wD/FP+/////+/+1/1v/MP97/17/AP8A/wD/AP8A/1z/+/+e/wD/AP8A/wD/AP8A/wD/AP8k////RP8A/wD/AP8A/wD/AP8A/wb/0P///6L/fP/h//v/LP8A/wD/AP8A/wD/sf///yf/AP8e////9v+2/wD/AP+R////P/8A/wD/AP8A/wD/AP+x/6P/fP+K//T/4/8I/wD/AP8A/wD/MP////j/0f9y/4P/9//y/xb/AP8A/wD/AP8A/53////g/53/sP++/wD/AP8A/wD/AP8A/wD/AP8z/1v/Av8A/wD/AP8A/8T///8Y/wD/AP8A//z/3P8A/wD/AP8A/xr//f/H/wD/AP86////pf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/rf/r/yf/AP8A/wD/AP8s////p/8A/yX///+X////Ov8A/5L///86/wD/AP8A/wD/AP8A/wD/BP/N//n/Lv8A/wD/AP8A/wD/xf+q/wD/r//A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Af/0/83/AP8A/wD/AP8A/y3/7P/N/xj/tv/2/0D/AP8A/wD/AP8A/yr/n/9U/1L/+/8f/2b/q/8B/wD/AP8A/wD/xf+q/wD/AP8A/wD/EP9Q/1D/UP9Q/1D/UP9Q/xr/AP8A/wD/AP9A/7r/V/95/9T//////+//NP8A/wD/AP8A/wD/AP9I//D/Wv8A/wD/AP8A/wf/HP8c/zz///9Y/xz/HP8K/wD/AP8A/wD/Xf///57/AP8A/yv///+q/wD/AP8A/wD/AP9s////Z/8A/2X//v+Z//T/Bv8A/9L/9P8G/wD/AP8A/wD/AP8A/wD/AP8A/wD/k////zL/AP8A/wD/AP8w////9P8Q/wD/AP+U////Uf8A/wD/AP8A/yL//v/j/w7/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/xP///xj/AP8A/wD//P/c/wD/AP8A/wD/AP+6////Hv8A/5D///9H/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/5v/m/wD/bP///yf//f9//wD/0//w/wP/AP8A/wD/AP8A/wD/AP+B////eP8A/wD/AP8A/wD/AP+y/5b/AP+b/63/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8q////b/8A/wD/AP8A/wD/AP8x/+7/9f/4/0b/AP8A/wD/AP8A/wD/VP/9///////////////5/xf/AP8A/wD/AP+y/5b/AP8A/wD/AP8G/yD/IP8g/yD/IP8g/yD/Cv8A/wD/AP8A/w3/Av8A/wD/AP8s/0T/Ef8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/QP///////////////////2T/AP8A/wD/AP+d////XP8U/xT/FP/s/9r/AP8A/wD/AP8A/yf///+n/wD/rP/X/0z///8//xT//v+0/wD/AP8A/wD/AP8A/wj/hf/X//X//P/+////R/8A/wD/AP8A/zD///+//wD/AP8A/27///9q/wD/AP8A/wD/Xv///47/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/E////GP8A/wD/Av/+/9z/AP8A/wD/AP8A/1z///90/wH/4//m/wP/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+i////Jv+z/9j/AP/N/8P/E//+/6//AP8A/wD/AP8A/wD/AP8z//v/xf8D/wD/AP8A/wD/AP8A/yT/Hv8A/x//I/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/2P/+P8S/wD/AP8A/wD/AP8A/wT/uf///9H/C/8A/wD/AP8A/wD/AP8A/wL/H//l//v/sf8Q/wD/AP8A/wD/AP8A/yT/Hv8A/wD/AP8A/zT///////////////////9U/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8T/0z/TP9l////e/9M/0z/Hf8A/wD/AP8A/7D/////////////////8/8A/wD/AP8A/wD/AP/i/+b/A//v/5j/EP/8/4P/Uv///2//AP8A/wD/AP8A/wD/mf///7L/Rf8t/5////9I/wD/AP8A/wD/MP///6r/AP8A/wD/bP///2z/AP8A/wD/AP9w////dv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8P///8c/wD/AP8Y////3P8A/wD/AP8A/wD/C//z/8j/N////4z/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/13///9f//L/k/8A/4j/+/9W////av8A/wD/AP8A/wD/AP8I/9X/9P8k/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wL/pf/6/37/9P++/wn/AP8A/wD/AP8A/wD/AP+Z//b/Zf///1f/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/D/9M/0z/TP9M/0z/TP9M/xj/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/yT///9E/wD/AP8A/wD/AP8A/wD/nv///3X/RP9E/0T/RP9A/wD/AP8A/wD/AP8A/53///9S////U/8A/8j/xf+M////Kv8A/wD/AP8A/wD/Af/w//T/Bv8A/wD/nP///0j/AP8A/wD/AP8w////qP8A/wD/AP9s////bP8A/wD/AP8A/2H///+N/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zT/W/8C/wD/AP8A/wD/q////0r/AP8A/27////c/wD/AP8A/wD/AP8A/6L//v+U////Lv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Gf///7P///9M/wD/RP///7b///8k/wD/AP8A/wD/AP8A/43///9p/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/mf/8/1X/AP8///X/tv8B/wD/AP8A/wD/AP8r//v/mv8B/9X/4v8I/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/JP///0T/AP8A/wD/AP8A/wD/AP9c////lf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/WP///7P//P8Q/wD/hP/2/7//5P8A/wD/AP8A/wD/AP8C//j/7P8C/wD/Cv/i////SP8A/wD/AP8A/zD///+o/wD/AP8A/2z///9s/wD/AP8A/wD/Kf///+L/Dv8A/wD/Cf8A/wD/AP8A/wD/AP8A/wD/5P///0v/AP8A/wD/AP9k////6v+R/6j/9v/x/9z/AP8A/wD/AP8A/wD/RP////P/0P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/9P/+v/5/wz/AP8I//b/+v/f/wD/AP8A/wD/AP8A/zv//f/t/3D/cP9w/3D/Cv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9V/1v/AP8A/wD/Qf9v/wD/AP8A/wD/AP8A/wD/J/8h/wD/NP8U/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8k////RP8A/wD/AP8A/wD/AP8A/wX/yP///7z/hf+O/7n/fP8A/wD/AP8A/wD/AP8V//7/+v/F/wD/AP8/////+v+f/wD/AP8A/wD/AP8A/wD/u////6r/fv/U/+L///9I/wD/AP8A/wD/MP///6j/AP8A/wD/bP///2z/AP8A/wD/AP8A/63////f/5v/rP/l/wD/AP8A/wD/AP8A/wD/AP+t/+v/J/8A/wD/AP8A/wP/gv/g//j/z/9I/7D/3P8A/wD/AP8A/wD/AP8C/+T///9y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/jv///7//AP8A/wD/uv///5r/AP8A/wD/AP8A/wD/eP////////////////8Y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8N/47/4f/7/+7/wf9B/wD/AP8A/wD/AP8A/wD/zv///3//AP8A/wb/9P///1r/AP8A/wD/AP8A/wD/AP8h/7z/9f/q/47/Lv///0j/AP8A/wD/AP8w////qP8A/wD/AP9s////bP8A/wD/AP8A/wD/CP+K/+L/+//p/53/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/K/+r/BP8A/wD/AP8A/3z/////////9v/Z/5H/Hf8A/wD/AP8A/wD/AP+w////JP8A/wD/AP8A/wD/AP8A/wD/AP8A/3z/////////9P/J/2H/AP8A/wD/AP8A/wD/AP8E/3r/2v/3/+D/jv8O/wD/AP8A/wD/AP8A/wD/LP/e////GP8A/wD/AP8A/3z///////////////j/AP8A/wD/AP8A/xr/lP/h//j/2f92/wT/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9a/9f/+P/o/0z/AP8A/wD/AP8A/0r/tf/t//f/2v+B/wj/AP8A/wD/AP8A/2z///////////////////98/wD/AP8A/wD/AP8A/wL/Y//E//D/+/+Y/wD/AP8A/wD/AP8A/wX/dP/W//b/5/+8/1//AP8A/wD/AP8A/wD/AP8A/wD/Av/I////UP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/yf/mf/c//f/8P/S/4z/Ff8A/wD/AP8A/8T////////////////////Q/wD/AP8A/wD/fP////X/H/8A/wD/AP8A/7D///8I/wD/AP8A/wD/1v///0X/AP8A/wD/AP8A/wD/AP8l////k/8A/wD/AP8A/wD/fP///7//mP+i/9X////v/z3/AP8A/wD/AP8A/7D///8k/wD/AP8A/wD/AP8A/wD/AP8A/wD/fP///7//mf+1//r///9z/wD/AP8A/wD/AP8A/43////B/33/tP///6z/AP8A/wD/AP8A/wD/TP/y//////8Y/wD/AP8A/wD/fP///8H/nP+c/5z/l/8A/wD/AP8A/w7/6P///83/p//j////hf8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/AP8b//v/8v+U/6D/Gv8A/wD/AP8A/xf/8v/v/7H/nv/f////nP8A/wD/AP8A/wD/Rv+o/6j/qP+o/6j/2f///2L/AP8A/wD/AP8A/wH/qP///+P/nP+S/3P/AP8A/wD/AP8A/wD/mv///+L/rP/B//b/of8A/wD/AP8A/wD/AP8A/wD/AP9v//////9Q/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/1f/+P///9z/rP+3/+L/7f8J/wD/AP8A/wD/ff+k/6T/yf///83/pP+k/4X/AP8A/wD/AP98//////+v/wD/AP8A/wD/sP///wj/AP8A/wD/AP/J////Of8A/wD/AP8A/wD/AP8A/4D///84/wD/AP8A/wD/AP98////YP8A/wD/AP9h//7/6f8N/wD/AP8A/wD/sP///yT/AP8A/wD/AP8A/wD/AP8A/wD/AP98////YP8A/wD/O////+n/AP8A/wD/AP8A/wD/4v/n/wX/AP8A/83/+v8G/wD/AP8A/wD/av/9/8f/w////xj/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP9k/z//AP8A/xT/8//q/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/0b///+U/wD/AP8A/wD/AP8A/wD/AP83/wz/AP8A/xD/9P/x/wD/AP8A/wD/AP8A/wD/AP8A/wD/Af/Y//D/C/8A/wD/AP8A/wD/YP///6z/B/8A/wD/AP8A/wD/AP8A/wD/Bf/5/+n/DP8A/wD/DP8Y/wD/AP8A/wD/AP8A/wD/AP8f//T/z////1D/AP8A/wD/AP8A/7D///8n/wD/AP8A/wD/AP8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/yv/+P/7/2P/AP8A/wD/AP8d/wD/AP8A/wD/AP8A/wD/AP9o////dP8A/wD/AP8A/wD/AP8A/3z////h////Sv8A/wD/AP+w////CP8A/wD/AP8A/7z///8t/wD/AP8A/wD/AP8A/wD/2v/d/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/pP///2z/AP8A/wD/AP+w////JP8A/wT/s////2j/AP8A/wD/AP8A/3z///9g/wD/AP8A/+T///8N/wD/AP8A/wD/AP/X/+//Cv8A/wH/1P/x/wH/AP8A/wD/AP9D/6n/Cf+5////GP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/Z////BP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/23/6f////////+o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/s/+b/AP8A/wD/AP8A/wD/AP8A/wD/AP9K////kP8A/wD/AP8A/wD/AP/L//b/Ef8A/wD/AP8A/wD/AP8A/wD/AP8H//7/3P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/7j/2/93////UP8A/wD/AP8A/wD/sP///0r/wP/3/+X/ev8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/oP///4X/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/2j///90/wD/AP8A/wD/AP8A/wD/fP///2D////c/wj/AP8A/7D///8I/wD/AP8A/wD/r////yD/AP8A/wD/AP8A/wD/Nv///4P/AP8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP9S////qv8A/wD/AP8A/7D///8k/wD/nv///4D/AP8A/wD/AP8A/wD/fP///2D/AP8A/wb/9v/5/wT/AP8A/wD/AP8A/2P///++/zr/pP///2//AP8A/wD/AP8A/wD/AP8A/7z///8Y/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8X//v/0/8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/Tf+X////vP9s/0b/AP8A/wD/AP8A/wD/AP8A/wT/IP+V////bf8A/wD/AP8A/wD/AP8A/wD/AP8A/7r//v8l/wD/AP8A/wD/AP8M//7/uP8A/w7/Ff8A/wD/AP8A/wD/AP8A/wD/w////5b/D/8A/wD/AP8A/wD/AP8A/wD/AP8A/13///9D/3b///9Q/wD/AP8A/wD/AP+w////7/+w/5j/6v///2b/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP/l////HP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/aP///3T/AP8A/wD/AP8A/wD/AP98////LP+r////f/8A/wD/sP///wj/AP8A/wD/AP+j////FP8A/wD/AP8A/wD/AP+R////Kf8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/zP////E/wD/AP8A/wD/sP///yL/hf///5n/AP8A/wD/AP8A/wD/AP98////YP8F/yb/pv///7D/AP8A/wD/AP8A/wD/AP9w/////////2r/AP8A/wD/AP8A/wD/AP8A/wD/vP///xj/AP8A/wD/AP98//////////////+w/wD/AP8A/wD/AP8A/wD/AP8A/6L///9h/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/wD/AP8A/wD/AP8A/5T//////+3/W/8A/wD/AP8A/wD/AP8A/wD/AP8A/yr///+4/wD/AP8A/wD/AP8A/zH///+a/6j//f///+H/R/8A/wD/AP8A/wD/AP8m/9/////z/47/F/8A/wD/AP8A/wD/AP8A/xX/7P+e/wD/eP///1D/AP8A/wD/AP8A/7D///+K/wD/AP8s//7/2/8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8D//7/8f8A/wD/AP+F/6T/pP+k/0D/AP8A/wD/AP8A/wD/AP9o////dP8A/wD/AP8A/wD/AP8A/3z///8y/x7/9v/3/yL/AP+w////CP8A/wD/AP8A/5b///8I/wD/AP8A/wD/AP8D/+j/zv8A/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/Nf///8D/AP8A/wD/AP+w////bf///7P/A/8A/wD/AP8A/wD/AP8A/3z//////////////9b/HP8A/wD/AP8A/wD/AP85/+f/7/+n//j/7P9F/wD/AP8A/wD/AP8A/wD/AP+8////GP8A/wD/AP8A/3z///+//5j/mP+Y/2j/AP8A/wD/AP8A/wD/AP8A/2////+v/wH/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/AP8A/wD/AP8A/wD/Tv+L/6H/5//9/33/AP8A/wD/AP8A/wD/AP8A/wD/mv///0z/AP8A/wD/AP8A/wD/P/////f/uf9l/3v/8//z/xr/AP8A/wD/AP8A/wD/Cv94/+j////u/zv/AP8A/wD/AP8A/wD/p//o/xH/AP94////UP8A/wD/AP8A/wD/sP///z7/AP8A/wD/1v///wr/AP8A/wD/AP98////YP8A/wD/AP8A/wT////u/wD/AP8A/9D/////////ZP8A/wD/AP8A/wD/AP8A/2j///90/wD/AP8A/wD/AP8A/wD/fP///zT/AP9+////tP8A/67///8I/wD/AP8A/wD/if/7/wD/AP8A/wD/AP8A/0f///9z/wD/AP8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP9Z////o/8A/wD/AP8A/7D////3////zv8G/wD/AP8A/wD/AP8A/wD/fP///8H/mv+K/1P/Bv8A/wD/AP8A/wD/AP8K/+r/5P8e/wD/Jv/g//b/HP8A/wD/AP8A/wD/AP8A/7z///8Y/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1v//v/C/wr/AP8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/AP9M////jP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8L/9f///8r/wD/AP8A/wD/AP8A/wD/Ev/2/97/Av8A/wD/AP8A/wD/AP83////wf8B/wD/AP96////Yf8A/wD/AP8A/wD/AP8A/wD/CP+F////2/8A/wD/AP8A/wD/S////1b/AP8A/3j///9Q/wD/AP8A/wD/AP+w////LP8A/wD/AP/C////IP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP/r//7/Ef8A/wD/AP8A/3T///9k/wD/AP8A/wD/AP8A/wD/aP///3T/AP8A/wD/AP8A/wD/AP98////NP8A/wj/3////07/qP///wj/AP8A/wD/AP98/+//AP8A/wD/AP8A/wD/ov/9/xv/AP8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/7T///9g/wD/AP8A/wD/sP///5z/jv///47/AP8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/0H///+D/wD/AP8A/2T///9l/wD/AP8A/wD/AP8A/wD/vP///xj/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/1H/+//G/w3/AP8A/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/of///1T/AP8A/wD/AP8A/wD/AP95////df8A/wD/AP8A/wD/AP8A/xj///+r/wD/AP8A/1r///94/wD/AP8A/wD/AP8A/wD/AP8A/wD/0////xD/AP8A/wD/AP+v////////////////////3P8A/wD/AP8A/7D///9A/wD/AP8A/9j///8J/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/67///9k/wD/AP8A/wD/dP///2T/AP8A/wD/AP8A/wD/AP9o////dP8A/wD/AP8A/wD/AP8A/3z///80/wD/AP9S////3/+n////CP8A/wD/AP8A/xD/IP8A/wD/AP8A/wD/Cf/z/77/AP8A/wD/AP8A/wD/AP8A/3z///9g/wD/AP8E/33////f/wf/AP8A/wD/AP+w////JP8G/9D//v9H/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/wD/Pf///6P/AP8A/wD/gv///1v/AP8A/wD/AP8A/wD/AP+8////GP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/0n/+f/I/w7/AP8A/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/AP8A/wD/AP8V/wz/AP8A/wD/C//d////M/8A/wD/AP8A/wD/AP8E/+P/9v8S/wD/AP8A/wD/AP8A/wD/AP/N//L/Hv8A/wD/n////1L/AP8A/wD/AP8V/yn/AP8A/wD/D//q//X/BP8A/wD/AP8A/2P/kP+Q/5D/kP/E////s/97/wD/AP8A/wD/sP///5L/AP8A/y/////X/wD/AP8A/wD/AP98////YP8A/wD/AP8A/wD/Q////+z/NP8A/wD/AP90////ZP8A/wD/AP8A/wD/AP8A/2j///90/wD/AP8A/wD/AP8A/wD/fP///zT/AP8A/wD/u/////f///8I/wD/AP8A/wD/M/9b/wL/AP8A/wD/AP9Y////Y/8A/wD/AP8A/wD/AP8A/wD/fP///8H/nP+s/+X////n/zD/AP8A/wD/AP8A/7D///8k/wD/K//3/+j/Fv8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP8A/wD/AP8F/9f///+q/3j/nf/7/+L/Dv8A/wD/AP8A/wD/AP8A/7z///8Y/wD/AP8A/wD/fP///8T/oP+g/6D/m/8A/wD/AP8A/yv/9////77/sP+w/7D/sP9P/wD/AP8A/wD/fP///8b/pP+k/6T/pP8j/wD/AP8A/wD/AP9M////jP8A/wD/AP8A/wD/AP8A/1T/9P+z/5L/m//j////s/8A/wD/AP8A/wD/AP8A/1n///+d/wD/AP8A/wD/AP8A/wD/AP8A/0P/+//p/5v/uv///9X/Bv8A/wD/AP8A/zj////X/6//p//m////hv8A/wD/AP8A/wD/AP8A/wD/AP8A/3j///9Q/wD/AP8A/wD/AP+w////8/+0/5j/6v///2D/AP8A/wD/AP8A/3z///9g/wD/AP8A/wD/AP8A/4f////9/8H/ov+4/+f///9k/wD/AP8A/wD/AP8A/wD/aP///3T/AP8A/wD/AP8A/wD/AP98////NP8A/wD/AP8q//v//////wj/AP8A/wD/AP/k////S/8A/wD/AP8A/7P/+P8Q/wD/AP8A/wD/AP8A/wD/AP98//////////L/zf+C/xT/AP8A/wD/AP8A/wD/sP///yT/AP8A/27///+2/wH/AP8A/wD/AP98////YP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/GP+U/97/9//h/53/HP8A/wD/AP8A/wD/AP8A/wD/vP///xj/AP8A/wD/AP98///////////////4/wD/AP8A/wD/TP///////////////////3T/AP8A/wD/AP98/////////////////zj/AP8A/wD/AP8A/0z///+M/wD/AP8A/wD/AP8A/wD/JP+l/+D/9//t/8v/a/8G/wD/AP8A/wD/AP8A/wD/yP///zH/AP8A/wD/AP8A/wD/AP8A/wD/AP9G/8f/9f/h/5r/Ev8A/wD/AP8A/wD/F/+g/97/9f/v/8n/Xv8B/wD/AP8A/wD/AP8A/wD/AP8A/wD/eP///1D/AP8A/wD/AP8A/7D/1P8z/8b/+P/k/3b/AP8A/wD/AP8A/wD/fP///2D/AP8A/wD/AP8A/wD/AP9P/7r/7v/7/+z/0v+Y/yr/AP8A/wD/AP8A/wD/AP9o////dP8A/wD/AP8A/wD/AP8A/3z///80/wD/AP8A/wD/j///////CP8A/wD/AP8A/67/7P8n/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xT/nP/k//7/////////nP8A/wD/AP8A/8////8s/wD/AP8A/5P///9l/wD/AP8A/wD/sP/o/zb/x//4/+X/ev8A/wD/AP8A/wD/AP8A/wD/AP8C/+P///98/wD/AP8A/wD/AP8A/wD/AP/W//3/Fv8A/wD/AP8A/yX////I/wD/AP8A/wD/fP/////////z/8n/Zf8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8A/wD/AP8A/wH/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1r/1//5//r/W//F/9b/A/8A/wD/AP8A/5j///9I/wD/AP8A/wD/aP///3j/AP8A/wD/AP8A/wD/Wv/X//n/+v9b/wD/f//i//v/+f89/+T/q/8A/wD/AP8A/77///9I/wD/AP8A/wH/0P///zb/AP8A/wD/AP8A/0v/3P/r/4P/AP8A/wD/Df/q/4v/AP8A/wD/AP8A/wD/AP98//////////n/4f+j/yX/AP8A/wD/AP8A/8n///8d/wD/AP8A/4b///+a/wD/AP8A/wT/9f/n/wD/AP8A/wD/AP8A/wD/AP81////M/8A/9X/h/8A/wD/AP8A/wD/AP9y//////////////+I/wD/AP8A/wD/AP9w////Ff+D/+z/x/8A/wD/AP8A/wD/uv/7/2n/Q//J////i/8n/wD/AP8A/wD/bf///4T/AP8A/wH/5f/4/xD/AP8A/wD/AP+w////7f+I/3H/1////2b/AP8A/wD/AP8A/wD/AP8A/0H////5/9f/AP8A/wD/AP8A/wD/AP8A/4H///9m/wD/AP8A/wD/d////3P/AP8A/wD/AP98////wf+e/7n/+////3n/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP8A/wD/P/+x/y//AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xv/+//y/5T/nv8W/9b/5v8F/wD/AP8A/wD/mP///0j/AP8A/wD/AP9o////eP8A/wD/AP8A/wD/G//7//L/lP+e/xb/S////+H/j/+X/xT/8v+8/wD/AP8A/wD/O////8n/AP8A/wD/U////7H/AP8A/wD/AP8A/wn/7v+6/4P///9H/wD/AP9///H/E/8A/wD/AP8A/wD/AP8A/3z///+//5j/o//Z////6f8O/wD/AP8A/wD/if///1n/AP8A/wD/yv///9//AP8A/wD/Nv///6j/AP8A/wD/AP8A/wD/AP8A/13//v8L/wP/+f9e/wD/AP8A/wD/AP8A/4X///+7/6z/rP+s/1v/AP8A/wD/AP8A/3D///+3//v/zv+T/wD/AP8A/wD/EP///7T/AP8A/0T///+B/wD/AP8A/wD/AP8S//j/3P8A/wD/O////6z/AP8A/wD/AP8A/7D///9z/wD/AP8f//z/2/8A/wD/AP8A/wD/AP8A/wD/nf/8/5D///8y/wD/AP8A/wD/AP8A/wD/LP///7j/AP8A/wD/AP/J////H/8A/wD/AP8A/3z///9g/wD/AP85////6v8A/wD/AP8A/wD/AP+w////J/8A/wD/AP8A/wD/AP8A/wD/AP8i/9b//v+r/yv/AP8A/wD/AP8A/wD/AP8A/wD/Rv///5T/AP8A/wD/BP8F/wD/AP8A/wD/AP+Y////SP8A/wD/AP8A/2j///94/wD/AP8A/wD/AP9G////lP8A/wD/AP96////YP8A/wD/AP8H/wL/AP8A/wD/AP8A/7j///9M/wD/Af/S//7/Lv8A/wD/AP8A/wD/PP///0f/AP/r/5T/AP8T//H/ff8A/wD/AP8A/wD/AP8A/wD/fP///2D/AP8A/wP/yf///03/AP8A/wD/AP9J////lf8A/wD/EP/8/+n///8k/wD/AP9x////aP8A/wD/AP8A/wD/AP8A/wD/hv/h/wD/Jv///zX/AP8A/wD/AP8A/wD/mf///yH/AP8A/wD/AP8A/wD/AP8A/wD/cP////P/M/8A/wD/AP8A/wD/AP8g////qP8A/wD/OP///4n/AP8A/wD/AP8A/wD/qP///zX/AP+P////T/8A/wD/AP8A/wD/sP///zf/AP8A/wD/0v///wr/AP8A/wD/AP8A/wD/B//w/8f/MP///43/AP8A/wD/AP8A/wD/AP8A/9f/+v8Q/wD/AP8b//7/yP8A/wD/AP8A/wD/fP///2D/AP8A/wD/5f///w3/AP8A/wD/AP8A/7D///9E/7z/9v/k/5P/Cv8A/wD/AP8A/wD/Af9T/83//f+l/yb/AP8A/wD/AP8A/wD/bf/p/////////6j/AP/g//T/AP8A/wD/AP8A/5j///9I/wD/AP8A/wD/aP///3j/AP8A/wD/AP9t/+n/////////qP+V//X/////////dP8Q////xP8A/wD/AP8A/wD/Nf///83/AP9X////qP8A/wD/AP8A/wD/AP9R////M/8A/9T/rP8A/43/6v8M/wD/AP8A/wD/AP8A/wD/AP98////YP8A/wD/AP+i////S/8A/wD/AP8A/wz/+//R/wD/AP9S////iv///2r/AP8A/63///8o/wD/AP8A/wD/AP8A/xT/FP+z/7//FP9b////If8T/wD/AP8A/wD/AP+s////DP8A/wD/AP8A/wD/AP8A/wD/AP9w////kP8A/wD/AP8A/wD/AP8A/wH/2v/w/zP/Dv+i////Q/8A/wD/AP8A/wD/AP9F////jf8B/+L/7P8F/wD/AP8A/wD/AP+w////LP8A/wD/AP/C////IP8A/wD/AP8A/wD/AP9T////d/8A/9z/5v8C/wD/AP8A/wD/AP8A/wD/gv///1z/AP8A/2z///9z/wD/AP8A/wD/AP98////YP8A/wD/Bf/0//b/Af8A/wD/AP8A/wD/sP///+v/vP+T/97///+H/wD/AP8A/wD/AP8A/wD/AP9I/8P//P+g/xv/AP8A/wD/AP9N/5f///+8/2z/Rv8A/+D/9P8A/wD/AP8A/wD/mP///0j/AP8A/wD/AP9o////eP8A/wD/AP8A/03/l////7z/bP9G/2P/tv///57/bP8w/xD////E/wD/AP8A/wD/AP8A/7L///9S/9b//P8n/wD/AP8A/wD/AP8A/0D///8+/wD/4v+c/xz/9/9v/wD/BP8A/wD/AP8A/wD/AP8A/3z///9g/wD/CP9A/+7/6v8N/wD/AP8A/wD/AP/J//3/D/8A/5b///8g//j/r/8A/wD/5//o/wD/AP8A/wD/AP8A/wj///////////////////////T/AP8A/wD/AP8A/8D//P94/4n/a/8h/wD/AP8A/wD/AP8A/3D///9q/wD/AP8A/wD/AP8A/wD/AP8u/+z///////L/cP8A/wD/AP8A/wD/AP8A/wH/4P/d/zb///+W/wD/AP8A/wD/AP8A/7D///9C/wD/AP8A/9v///8J/wD/AP8A/wD/AP8A/6////8j/wD/h////0P/AP8A/wD/AP8A/wD/AP8t////rv8A/wD/vv///x//AP8A/wD/AP8A/3z///9g/wH/HP+b////nP8A/wD/AP8A/wD/AP+w////m/8A/wD/If///9H/AP8A/wD/AP8A/wD/AP8A/wX/Wf/k////WP8A/wD/AP8A/wD/TP///4z/AP8A/wD/4P/0/wD/AP8A/wD/AP+Y////SP8A/wD/AP8A/2j///94/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/EP///8T/AP8A/wD/AP8A/wD/L//+//L///+f/wD/AP8A/wD/AP8A/wD/Ef/2/5D/RP///2f/m//h/x7/wv/6/9T/Kv8A/wD/AP8A/wD/fP//////////////zv8n/wD/AP8A/wD/AP8A/4j///9J/wD/2v/V/wD/v//w/wP/JP///6n/AP8A/wD/AP8A/wD/Av9E/1H///99/0T/z/+7/0T/QP8A/wD/AP8A/wD/xf////////////f/YP8A/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/AP8A/3v/7P8z/yL/BP8A/wD/AP8A/wD/AP8A/wD/AP+A////nP///zn/AP8A/wD/AP8A/wD/sP///5X/AP8A/zf////X/wD/AP8A/wD/AP8A/xD/+f/O/wD/AP8y////nv8A/wD/AP8A/wD/AP8A/wD/2P/1/wr/E//8/8j/AP8A/wD/AP8A/wD/fP//////////////r/8K/wD/AP8A/wD/AP8A/7D///9D/wD/AP8A//D/6v8A/wD/AP8A/wD/AP8a/4H/5//7/6b/M/8A/wD/AP8A/wD/AP9M////jP8A/wD/AP/g//T/AP8A/wD/AP8A/5j///9I/wD/AP8A/wD/aP///3j/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8Q////xP8A/wD/AP8A/wD/AP8A/6v////6/yH/AP8A/wD/AP8A/wD/AP8A/3L//////8f/MP/7/2H/kf/i/wz/tf/C/wD/AP8A/wD/AP98////t/+M/5P/wP///8r/E/8A/wD/AP8A/wD/Sf///4X/Hf///5H/AP95////Of9f////af8A/wD/AP8A/wD/AP8M/xT/Wv/+/yf/GP/0/3b/FP8I/wD/AP8A/wD/AP8P/yf/B/8V/2r//f/2/xL/AP8A/wD/AP9w////aP8A/wD/AP8A/wD/AP8A/wD/of/5/6X/mP+V/3r/IP8A/wD/AP8A/wD/AP8A/x///v/7/9z/AP8A/wD/AP8A/wD/AP+w////9f+0/5n/7f///2D/AP8A/wD/AP8A/wD/Zf/////////////////x/wj/AP8A/wD/AP8A/wD/AP+D////Uv9h////c/8A/wD/AP8A/wD/AP98////vP+U/9r/+v8p/wD/AP8A/wD/AP8A/wD/sP///yz/AP8A/wD/7P/s/wD/AP8A/wD/Gf+p//v/8f+M/x3/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/+D/9P8A/wD/AP8A/wD/lv///0z/AP8A/wD/AP9s////dv8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/xD////E/wD/AP8A/wD/AP8A/wD/Pv///7P/AP8A/wD/AP8A/wD/AP8A/wD/AP8a/yz/A/+p/9f/A//P/6z/AP9u////CP8A/wD/AP8A/3z///9g/wD/AP8A/4r///+C/wD/AP8A/wD/AP8N//z/vf9d////Tf8A/zT///96/5n///8q/wD/AP8A/wD/AP8A/6D//////////////////////2j/AP8A/wD/AP8A/wD/AP8A/wD/AP+w////SP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP8A/wD/Af9z//7////////////0/yT/AP8A/wD/AP8A/wD/AP+7////gP8A/wD/AP8A/wD/AP8A/7D///9O/8b/+P/j/3X/AP8A/wD/AP8A/wD/AP/B////p/+k/6T/pP/Q////VP8A/wD/AP8A/wD/AP8A/y7///+d/6z///8f/wD/AP8A/wD/AP8A/3z///9g/wD/OP/+/8f/Av8A/wD/AP8A/wD/AP+w////KP8A/wD/AP/s/+z/AP8A/wD/AP9A/+H/c/8O/wD/AP8A/wD/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/4P/0/wD/AP8A/wD/AP+E////av8A/wD/AP8A/47///9f/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/EP///8T/AP8A/wD/AP8A/wD/AP80////rP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8x//7/U/8A/+H/o/8A/2T///8c/wD/AP8A/wD/fP///2D/AP8A/wD/T////6v/AP8A/wD/AP8A/wD/yv/w/5P/+/8N/wD/Av/s/7D/0f/p/wD/AP8A/wD/AP8A/wD/Kv9E/83/v/9E/5D/+/9F/0T/G/8A/wD/AP8A/wD/AP8A/wD/AP8A/6D///9I/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/AP+C//z/T/8F/wD/Cv91////c/8A/wD/AP8A/wD/AP8A/7b///8k/wD/AP8A/wD/AP8A/wD/sP///yf/AP8A/wD/AP8A/wD/AP8A/wD/AP8d//7/zv8A/wD/AP8A/0H///+v/wD/AP8A/wD/AP8A/wD/AP/Z/9z/5f/I/wD/AP8A/wD/AP8A/wD/fP///2D/AP8A/5j///9x/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/+z/7P8A/wD/AP8A/wv/BP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9M////jP8A/wD/AP/g//T/AP8A/wD/AP8A/0H////T/w//AP8A/x7/7P/8/xv/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8Q////xP8A/wD/AP8A/wD/AP8A/zT///+s/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/7f/zP8B/wD/y/+4/wD/fv///wj/AP8A/wD/AP98////YP8A/wD/AP+Y////kf8A/wD/AP8A/wD/AP+L////2f/F/wD/AP8A/6n/4P/4/6r/AP8A/wD/AP8A/wD/AP8A/wD/3v+C/wD/jf/W/wD/AP8A/wD/AP8A/wD/Bv8e/wD/AP8A/xX/6P/7/xb/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8T/4v8A/wD/AP8A/1D///9q/wD/AP8A/wD/AP8A/y7//f+8/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/AP8A/wD/AP8A/3f///94/wD/AP8A/wD/Av/n//n/Ef8A/wD/AP8A/wD/AP8A/4T//////3P/AP8A/wD/AP8A/wD/AP98////YP8A/wD/EP/p//X/I/8A/wD/AP8A/wD/sP///yj/AP8A/wD/7P/s/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/+D/9P8A/wD/AP8A/wD/AP+v////6v+w/7X/8////33/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/xD////E/wD/AP8A/wD/AP8A/wD/NP///6z/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/z3///9F/wD/AP+D//b/Zf/e/8L/AP8A/wD/AP8A/3z////B/5z/of/O////9P8o/wD/AP8A/wD/AP8A/0v//////4H/AP8A/wD/ZP//////a/8A/wD/AP8A/wD/AP8A/wD/Cf/9/1f/AP+4/6v/AP8A/wD/AP8A/wD/AP8U//3/w/+d/6T/7P///4f/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/if///5z/Vf9Z/4b/7//i/xP/AP8A/wD/AP9j/5T/6//5/zf/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8A/wD/AP8A/wD/0v///yP/AP8A/wD/AP8A/5P///9m/wD/AP8A/wD/AP8A/wD/L///////H/8A/wD/AP8A/wD/AP8A/3z///9g/wD/AP8A/1v///++/wH/AP8A/wD/AP+w////KP8A/wD/AP/s/+z/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/4P/0/wD/AP8A/wD/AP8A/wX/eP/V//n/8f/D/1b/AP8A/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/EP///8T/AP8A/wD/AP8A/wD/AP80////rP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/xP+//wD/AP8A/w3/rP/y/8r/Kf8A/wD/AP8A/wD/fP/////////9/+P/qv8q/wD/AP8A/wD/AP8A/wD/D//8////Pf8A/wD/AP8f//////8r/wD/AP8A/wD/AP8A/wD/AP8w////Lf8A/+P/gf8A/wD/AP8A/wD/AP8A/wf/lv/d//b/9v/L/2P/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8G/4X/2//3//f/2P+P/xf/AP8A/wD/AP8A/5b/9//Q/0j/AP8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8//6X/3P/3/+f/uP9T/wD/AP8A/wD/AP8A/wD/AP8G/4b/4//4/9L/Xf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Af8A/wD/AP8A/wD/AP9a/9f/+P/o/0z/AP9//+L/+f/f/yf/AP8A/wD/AP8A/wD/e//k//f/v/8r//D/qP8A/wD/AP8A/wD/AP9a/9f/+P/o/0z/4P/0/wD/AP8A/wD/AP8A/1r/1//4/+j/TP8A/3//4v/5/9//N////8T/AP8A/wD/AP9q////pv8A/wD/AP8A/6f///9Y/wD/AP8A/wD/VP///////////////////4j/AP8A/wD/AP+z//j/EP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/0/xj/AP8A/wD/AP8A/wD/AP/w/+T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xb/lf/f//f/4P+p/z3/AP8A/wD/AP8A/xL/mP/m//r/2/93/wL/AP8A/wD/AP8A/wD/L/+4//P/8/+y/yT/AP8A/wD/AP8A/xj////I/wD/AP8A/wD/AP/o//j/AP8A/wD/AP8b/6X/5//4/9//nv8R/wD/AP8A/wD/AP8A/3f/1P8A/wD/AP8A/wD/AP8A/wD/A/99/97/+v/d/3D/AP8A/wD/AP8A/wD/AP8A/wD/B/+a//3/tP9o/0z/Xf+n//z/p/8D/wD/AP8A/wD/AP8A/5X////S/5v/3////2b/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/yH/nv9g/wD/AP8A/wD/AP8b//v/8v+U/6D/Gv9L////4f+Q/5z/Av8A/wD/AP8A/wD/av///+n/mP+z/+3///+o/wD/AP8A/wD/AP8b//v/8v+U/6D/Gv/g//T/AP8A/wD/AP8A/xv/+//y/5T/oP8a/0v////h/5D/nP8S////xP8A/wD/AP8A/wL/yv///zz/AP8A/zz///+6/wD/AP8A/wD/AP81/6T/pP+k/6T/pP/w////ZP8A/wD/AP8A/1j///9j/wD/AP8A/wD/AP8A/wD/AP8A/wD/Pv+Q//z/rf+I/0P/Af8A/wD/AP8A//D/5P8A/wD/AP8A/wD/AP8A/wD/AP8A/wX/1P///8//rf/N//7/Yf8A/wD/AP8A/wf/0P///7//lf/X////lf8A/wD/AP8A/wD/JP/y//X/if+K//b/4P8I/wD/AP8A/wD/GP///8j/AP8A/wD/AP8A/+j/+P8A/wD/AP8A/8H//f+V/33/qf/P/wH/AP8A/wD/AP8A/wD/zP/U/wD/AP8A/wD/AP8A/wD/AP+W////wv95/8T///9o/wD/AP8A/wD/AP8A/wD/AP+c/+r/Pf8A/wD/AP8A/wD/Q//4/3//AP8A/wD/AP8A/xf//f/S/wX/AP8P/+D/7f8G/wD/AP8A/wD/AP8A/wD/AP8d/5n/+v/j/zn/AP8A/wD/AP8A/0b///+U/wD/AP8A/3r///9g/wD/AP8A/wD/AP8A/wD/AP/i//7/Kv8A/wD/iP///6j/AP8A/wD/AP8A/0b///+U/wD/AP8A/+D/9P8A/wD/AP8A/wD/Rv///5T/AP8A/wD/ev///2D/AP8A/xD////E/wD/AP8A/wD/AP8y//z/zv8C/wL/zv/4/yX/AP8A/wD/AP8A/wD/AP8A/wD/AP9F////x/8C/wD/AP8A/wD/Cf/z/77/AP8A/wD/AP8A/wD/AP8A/wD/f////+z//f/W//j/2/8B/wD/AP8A/wD/8P/k/wD/AP8A/wD/AP8A/wD/AP8A/wD/P////7X/AP8A/wD/G/8J/wD/AP8A/wD/X////6b/AP8A/wn/2f/+/yP/AP8A/wD/AP+d////Xv8A/wD/av///2r/AP8A/wD/AP8Y////yP8A/wD/AP8A/wD/6P/4/wD/AP8A/wD/8P/o/wb/AP8A/wD/AP8A/wD/AP8A/xX/yP///////////0z/AP8A/wD/AP8f//3/2v8E/wD/Bv/k/+n/AP8A/wD/AP8A/wD/AP9D//7/Rv8A/2D/2f/5/+r/vP8R/3v/8/8L/wD/AP8A/wD/Qv///4n/AP8A/wD/hP///0H/AP8A/wD/AP8A/wD/Gf+T//n/2/9k/wb/AP8A/wD/AP8A/23/6f////////+o/5X/9f////////90/wD/AP8A/wD/AP8U////zf8A/wD/AP8z////qP8A/wD/AP8A/23/6f////////+o/wD/4P/0/wD/AP8A/wD/bf/p/////////6j/lf/1/////////3T/EP///8T/AP8A/wD/AP8A/wD/kv///2X/Zv///4D/AP8A/wD/AP8A/wD/AP8A/wD/AP8K/93/+v8s/wD/AP8A/wD/AP8A/6L//f8b/wD/AP8A/wD/AP8A/wD/A//2/+H/C//0/xj/Cv8m/wD/AP8A/wD/AP/w/+T/AP8A/xj/3v/z/zT/AP8A/wD/AP9G////nP8A/wD/AP8A/wD/AP8A/wD/AP+e////S/8A/wD/AP+F////ZP8A/wD/AP8A/93///8i/xT/FP87////mv8A/wD/AP8A/xj////I/wD/AP8A/wD/AP/o//j/AP8A/wD/AP+k////1/9l/wr/AP8A/wD/AP8A/wD/FP9u////5v9s/2z/IP8A/wD/AP8A/13///+X/xT/FP8U/7H///8a/wD/AP8A/wD/AP8A/6v/wv8A/1D///+U/0D/k////xP/Gf///07/AP8A/wD/AP8/////kf8A/wD/AP+C////Yf8A/wD/AP8A/w//jf/3/9P/Wv8D/wD/AP8A/wD/AP8A/wD/Tf+X////vP9s/0b/Y/+2////nv9s/zD/AP8A/wD/AP8A/yz///+2/wD/AP8A/xz///+o/wD/AP8A/wD/Tf+X////vP9s/0b/AP/g//T/AP8A/wD/AP9N/5f///+8/2z/Rv9j/7b///+e/2z/MP8Q////xP8A/wD/AP8A/wD/AP8O/+b/6v/r/9v/B/8A/wD/AP8A/wD/AP8A/wD/AP8A/4z///+B/wD/AP8A/wD/AP8A/wD/R////3P/AP8A/wD/AP8A/wD/AP8B//T/7f8g//T/GP8A/wD/AP8A/wD/AP8A//D/5P8A/w7/0P/6/0b/AP8A/wD/AP8A/xH/8v/6/2f/BP8A/wD/AP8A/wD/AP8A/67///82/wD/AP8A/2////93/wD/AP8A/wD/8P////////////////+z/wD/AP8A/wD/GP///8j/AP8A/wD/AP8A/+j/+P8A/wD/AP8A/wX/fv/t////7/9f/wD/AP8A/wD/AP8A/wT////U/wD/AP8A/wD/AP8A/wD/cP///////////////////zP/AP8A/wD/AP8A/wD/8f91/wD/zf/B/wD/AP9u////CP8A//D/bv8A/wD/AP8A/xL/9f/s/zj/Cv9N//H///9q/wD/AP8A/wD/Nv/9//L/af8L/wD/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8A/wD/AP8A/wD/Fv///8j/AP8A/wD/K////6j/AP8A/wD/AP8A/0z///+M/wD/AP8A/+D/9P8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/xD////E/wD/AP8A/wD/AP8A/wD/X///////Sv8A/wD/AP8A/wD/AP8A/wD/AP8A/zL//P/W/wb/AP8A/wD/AP8A/wD/AP8D/+j/zv8A/wD/AP8A/wD/AP8A/wD/eP////j//f9P/wD/AP8A/wD/AP8A/wD/8P/i/wb/vv/+/1v/AP8A/wD/AP8A/wD/AP9Q//b////i/2//B/8A/wD/AP8A/wD/mf///0z/AP8A/wD/hf///2T/AP8A/wD/AP/e//3/SP9E/0T/RP9E/y//AP8A/wD/AP8Y////yP8A/wD/AP8A/wD/6P/4/wD/AP8A/wD/AP8A/wr/a//y//3/Kf8A/wD/AP8A/wD/BP///9T/AP8A/wD/AP8A/wD/AP9e////pP9E/0T/RP9E/0T/Df8A/wD/AP8A/wD/D////1b/Av/4/4L/AP8A/33//f8A/wD/7v9x/wD/AP8A/wD/AP9q//z///////j/qf///1z/AP8A/wD/AP8A/yT/lv/1//D/kP8l/wD/AP8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/wD/AP8A/wD/AP8A/+b/+/8g/wD/AP92////qP8A/wD/AP8A/wD/TP///4z/AP8A/wD/4P/0/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/EP///8T/AP8A/wD/AP8A/wD/AP+d//////+A/wD/AP8A/wD/AP8A/wD/AP8A/wP/zv/+/zv/AP8A/wD/AP8A/wD/AP8A/wD/kf///yj/AP8A/wD/AP8A/wD/AP8A/0v/w///////z/80/wD/AP8A/wD/AP/w/9b/l////3b/AP8A/wD/AP8A/wD/AP8A/wD/HP+X//f////S/xf/AP8A/wD/AP9T////qv8A/wD/CP/Z//7/Jv8A/wD/AP8A/5z///9V/wD/AP8A/wD/AP8A/wD/AP8A/xj////I/wD/AP8A/wD/AP/o//j/AP8A/wD/AP8H/wD/AP8A/4P///9R/wD/AP8A/wD/AP8E////1P8A/wD/AP8A/wD/AP8A/x///f/P/wX/AP8A/wD/AP8A/wD/AP8A/wD/AP8Z////Tv8B//b/g/8A/wD/pf/2/wD/FP/+/0r/AP8A/wD/AP8A/wD/Lv9t/3D/JP+B////N/8A/wD/AP8A/wD/AP8A/xP/fP/o//7/uP8q/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/AP8A/wD/AP8A/wD/cv///+P/j/+l/+v///+o/wD/AP8A/wD/AP9M////jP8A/wD/AP/g//T/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8Q////xP8A/wD/AP8A/wD/AP86//7/sf/T//j/J/8A/wD/AP8A/wD/AP8A/wD/dv///5X/AP8A/wD/AP8A/wD/AP8A/wD/AP82////g/8A/wD/AP8A/wD/AP8A/wD/AP8A//T/lP/5//H/FP8A/wD/AP8A//D/9///////lf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/GP+0////nP8A/wD/AP8A/wL/w////8H/lP/V////m/8A/wD/AP8A/wD/IP/t//r/pf+C/5T/y/9C/wD/AP8A/wD/Fv///8z/AP8A/wD/AP8A/+z/9v8A/wD/AP8A/+j/pP98/4L/6P/0/xz/AP8A/wD/AP8A/wT////U/wD/AP8A/wD/AP8A/wD/AP+N////1v+L/4j/qv+x/wD/AP8A/wD/AP8A/wb/+/9u/wD/uv/Y/x7/O//z////L/+I/+n/Cf8A/wD/AP8A/wD/AP8A/wD/AP8A/8z/8P8H/wD/AP8A/wD/AP8A/wD/AP8A/wf/Yv/U/2T/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8A/wD/AP8A/wD/AP8C/4P/6P/4/7//UP///6j/AP8A/wD/AP8A/0z///+M/wD/AP8A/+D/9P8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/xD////E/wD/AP8A/wD/AP8E/9D/9/8g/0L///++/wD/AP8A/wD/AP8A/wD/Iv/2/+P/Df8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/2v/d/wD/AP8A/wD/AP8A/wD/AP8A/wD/9P8Y/5n///9D/wD/AP8A/wD/8P/8/2f/x////07/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xP////Q/wD/AP8A/wD/AP8L/4//4//6/9z/e/8D/wD/AP8A/wD/AP8A/yX/qf/s//r/6P+s/x7/AP8A/wD/AP8H//z/6f8A/wD/AP8A/w7////f/wD/AP8A/wD/m//h//j/7f+9/z3/AP8A/wD/AP8A/wD/AP/4/+P/AP8A/wD/AP8A/wD/AP8A/wH/bP/T//n/8//S/2n/AP8A/wD/AP8A/wD/AP/D/7v/AP8p/97////6/2v/rv////H/Sf8A/wD/AP8A/wD/AP8A/wD/AP8A/3L///+O/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wH/Dv8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/TP///4z/AP8A/wD/4P/0/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/EP///8T/AP8A/wD/AP8A/3L///+A/wD/AP+t////Xf8A/wD/AP8A/wD/AP+7////Tf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+A////OP8A/wD/AP8A/wD/HP+v/17/Nv/1/2X/4//3/xj/AP8A/wD/AP/w/+T/AP8j//P/7P8a/wD/AP8A/wD/AP8o/xb/AP8A/wD/Ov///7r/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/wf///2L/AP8A/wD/iv///5j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8X///+t/5f/Q/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1T///9a/wD/A/8m/xL/AP8B/yj/Df8A/wD/AP8A/wD/AP8A/1f/jv+M/8n////M/w3/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9M////jP8A/wD/AP+A////WP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP9M////jP8A/wD/AP/g//T/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8Q////xP8A/wD/AP8A/xr/8v/g/wn/AP8A/yD/9//q/xL/AP8A/wD/AP9X////8/+k/6T/pP+k/6T/cP8A/wD/AP8A/wD/AP8A/yX///+T/wD/AP8A/wD/AP8Y/+v////////////o/1D/AP8A/wD/AP8A//D/5P8A/wD/Y////73/Av8A/wD/AP8A/3j//P/K/6f/sP/2//3/Sf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP84//f////H/6n/0v///+X/GP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Lf/I//f/5v9H/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+V//3/lv85/xT/GP8z/23/XP8A/wD/AP8A/wD/AP8A/wD/eP/7//X/0f98/wr/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/0z///+M/wD/AP8A/4D///9Y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/0z///+M/wD/AP8A/+D/9P8A/wD/AP8A/wD/TP///4z/AP8A/wD/gP///1j/AP8A/xD////E/wD/AP8A/wD/qv///1P/AP8A/wD/AP+D////nP8A/wD/AP8A/3z///////////////////+w/wD/AP8A/wD/AP8A/wD/AP/K/+r/BP8A/wD/AP8A/wD/A/8w/0j/9/9F/wT/AP8A/wD/AP8A/wD/8P/k/wD/AP8A/67///93/wD/AP8A/wD/OP+0/+b/+f/m/7b/PP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Mf+v/+z/+v/h/5b/F/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Xv/V////////////3v9b/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/0/xj/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/F/8z/zD/GP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1v/Cf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/5X/7P8f/wD/AP8A/wD/AP8A/1X///9L/wD/AP8A/wD/Of/+/2n/AP8A/wD/AP8A/wD/zP//////5P8A/wD/AP8A/5j/////////EP8A/wD/AP8A/wD/AP8A/1T/vP/u//n/4P+b/yD/AP8A/wD/AP8A/wD/AP8A/wD/d//Y/8r/AP8A/wD/AP+e/+D/iP8C/wD/AP8A/wD/AP8A/wD/AP8A/4z///9U/wD/AP8A/wD/TP///xz/AP8A/wD/AP9w////aP8A/wD/AP8A/wD/Av94/9r/+f/o/5j/Ef8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/wD/AP9P////q/8A/wD/AP8V//z/4/8C/wD/AP8A/wD/AP8A/xT/qv/s//b/vP8A/wD/AP8A/wD/Bf+H/97/+P/n/7P/MP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP8A/wD/AP8A/wD/EP/h/9D/BP8A/wD/AP8A/wD/Ff+a/+b/+f/j/4j/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/yX/7f+O/wD/AP8A/wD/kv///1r/AP8A/wD/T////6D/AP8A/wD/AP8A/zf///8U/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/pf/4/yX/AP8A/wD/AP8A/w7/6/+2/wD/AP8A/wD/AP8A/6P/8/8Z/wD/AP8A/wD/AP/M/+//aP9c/wD/AP8A/wD/Pf9o/8L///8Q/wD/AP8A/wD/AP8A/4/////6/77/qv/W////7/80/wD/AP8A/wD/AP8A/wD/Vv///9r/cP8A/wD/AP8A/1X/z////2T/AP8A/wD/AP8A/wD/AP8A/wD/jP///1T/AP8A/wD/AP9M////HP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP+X////1/+V/7/////O/wf/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/wT/6P/3/w3/AP8A/2f///+I/wD/AP8A/wD/AP8A/wD/l////8D/kv9u/wD/AP8A/wD/AP+B////s/97/5r/4v8g/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/AP8A/wD/AP8j/////P8O/wD/AP8A/wD/CP/U////y/+a/8D/h/8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP8A/wD/LP/5/57/AP8A/wD/AP80////sf8A/wD/AP+l////Q/8A/wD/AP8A/wD/jf///xT/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8B/wj/AP8A/wD/AP8A/wD/cP///0f/AP8A/wD/AP8A/wD/Nf///4L/AP8A/wD/AP8A/8z/5P8A/wD/AP8A/wD/AP8A/wD/mP///xD/AP8A/wD/AP8A/0j////n/yn/AP8A/wD/c////9r/A/8A/wD/AP8A/wD/AP+L////M/8A/wD/AP8A/wD/AP8l////l/8A/wD/AP8A/wD/AP8A/wD/AP+M////VP8A/wD/AP8A/0z///8c/wD/AP8A/wD/cP///2j/AP8A/wD/AP8h//7/3P8K/wD/AP+i////Yv8A/wD/AP8A/zD///+n/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+K////XP8A/wD/u////yz/AP8A/wD/AP8A/wD/AP/G////FP8A/wD/AP8A/wD/AP8A/7D///8v/wD/AP8A/wD/AP8A/wD/AP9w////Z/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Tv9D/wD/AP8A/wD/AP9h////sf8B/wD/AP8A/wD/AP8A/wD/MP///6f/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wn/Af8A/wD/AP8A/wD/1//4/w//AP8H//L/4/8C/wD/AP8A/wD/n//+/////////4z/AP8A/wD/AP8A/wD/AP8A/wD/AP/v/+j/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP/P/+n/Av8A/wD/AP8A/wD/AP8A/9f/4f8A/wD/AP8A/wD/zP/k/wD/AP8A/wD/AP8A/wD/AP+Y////EP8A/wD/AP8A/wD/sv///2D/AP8A/wD/AP8A/8X///9K/wD/AP8A/wD/AP8A/5D///8g/wD/AP8A/wD/AP8A/xT///+g/wD/AP8A/wD/AP8A/wD/AP8A/4z///9U/wD/AP8A/wD/TP///xz/AP8A/wD/AP9w////aP8A/wD/AP8A/17///+L/wD/AP8A/0X///+k/wD/AP8A/wD/MP///6H/ff/m//f/vv8p/wD/AP8A/wD/AP8A/yj///+1/wD/E//7/8//AP8A/wD/AP8A/wD/G/+8//7/////////KP8A/wD/AP8A/wD/ZP///+3/gP8a/wD/AP8A/wD/AP8A/3D///9q/53/7//t/7L/If8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/57///9O/wD/AP8A/wD/AP8A/wD/AP8w////nf94/+P/9P/K/0H/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP96////Xv8A/1D///+H/wD/AP8A/wD/AP9l/93///93/2z/O/8A/wD/AP8A/wD/UP/W//n/1/9U/+T/6P8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8P//3/rf8A/wD/AP8A/wD/AP8A/wD/mv///yD/AP8A/wD/AP/M/+T/AP8A/wD/AP8A/wD/AP8A/5j///8Q/wD/AP8A/wD/AP/s////FP8A/wD/AP8A/wD/eP///4X/AP8A/wD/AP8A/wD/kP///yD/AP8A/wD/AP8A/wD/FP///6D/AP8A/wD/AP8A/wD/AP8A/wD/jP///1T/AP8A/wD/AP9M////HP8A/wD/AP8A/3D///9o/wD/AP8A/wD/bv///3b/AP8A/wD/L////7f/AP8A/wD/AP8w////8P/f/5b/u////+D/Bf8A/wD/AP8A/wD/AP/F//r/E/9j////cv8A/wD/AP8A/wD/AP8X/2z/4f///3L/bP8Q/wD/AP8A/wD/AP8A/1j/2f////v/kf8C/wD/AP8A/wD/cP///+v/1P+U/8b////H/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/sP///zb/AP8A/wD/AP8A/wD/AP8A/zD////r/+n/mv+r//7/8f8V/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/x7//v+0/wD/pf///yn/AP8A/wD/AP8A/wD/xP///xT/AP8A/wD/AP8A/wD/L//9//v/o/+h//L/9P/o/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/zv///99/wD/AP8A/wD/AP8A/wD/AP9p////Tv8A/wD/AP8A/8z/5P8A/wD/AP8A/wD/AP8A/wD/mP///xD/AP8A/wD/AP8E////9v8A/wD/AP8A/wD/AP9b////n/8A/wD/AP8A/wD/AP+0////FP8A/wD/AP8A/wD/AP8K//7/v/8A/wD/AP8A/wD/AP8A/wD/AP+M////VP8A/wD/AP8A/0z///8c/wD/AP8A/wD/cP///2j/AP8A/wD/AP9Z////jP8A/wD/AP9F////pP8A/wD/AP8A/zD////1/xT/AP8A/6v///9b/wD/AP8A/wD/AP8A/2P///9d/7b//P8Z/wD/AP8A/wD/AP8A/wD/AP/M////DP8A/wD/AP8A/wD/AP8A/wD/AP8B/0v/2v///2f/AP8A/wD/AP9w////1P8G/wD/Av/e////Ef8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+h////Tf8A/wD/AP8A/wD/AP8A/wD/MP////r/If8A/wD/of///1H/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP+///n/FP/x/8z/AP8A/wD/AP8A/wD/AP/E////FP8A/wD/AP8A/wD/AP+k////aP8A/wD/Rv///+j/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/T////2j/AP8A/wD/AP8A/wD/AP8A/1X///9i/wD/AP8A/wD/zP/k/wD/AP8A/wD/AP8A/wD/AP+Y////EP8A/wD/AP8A/wT////1/wD/AP8A/wD/AP8A/1v///+a/wD/AP8A/wD/XP+6////n/8A/wD/AP8A/wD/AP8A/wD/k////7r/Vf8A/wD/AP8A/wD/AP8A/4z///9U/wD/AP8A/wD/TP///xz/AP8A/wD/AP9w////aP8A/wD/AP8A/xf//P/g/wv/AP8A/6L///9k/wD/AP8A/wD/MP///77/AP8A/wD/Vv///4n/AP8A/wD/AP8A/wD/DP/z/6T/9v+5/wD/AP8A/wD/AP8A/wD/AP8A/8z///8M/wD/AP8A/wD/AP8A/wD/B/8A/wD/AP9D////kf8A/wD/AP8A/3D///+D/wD/AP8A/7D///8q/wD/AP8A/wD/AP9P/0P/AP8A/wD/AP8A/2n///+v/wH/AP8A/wn/AP8A/wD/AP8w////w/8A/wD/AP9w////av8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/2L///+S////bv8A/wD/AP8A/wD/AP8A/8T///8U/wD/AP8A/wD/AP8A/9X///8N/wD/AP8A//H/6P8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP9W////Yv8A/wD/AP8A/wD/AP8A/wD/Tv///2n/AP8A/wD/AP/M/+T/AP8A/wD/AP8A/wD/AP8A/5j///8Q/wD/AP8A/wD/AP/r////E/8A/wD/AP8A/wD/eP///4D/AP8A/wD/AP+k////z/87/wD/AP8A/wD/AP8A/wD/AP8v/8b///+Y/wD/AP8A/wD/AP8A/wD/jP///1T/AP8A/wD/AP9M////HP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP+G////2f+U/73////T/wj/AP8A/wD/AP8w////rP8A/wD/AP9C////oP8A/wD/AP8A/wD/AP8A/57/+////1z/AP8A/wD/AP8A/wD/AP8A/wD/zP///wz/AP8A/wD/AP8A/wD/AP+z/7b/hP95/87///9R/wD/AP8A/wD/cP///2z/AP8A/wD/rP///yz/AP8A/wD/AP8k/////P8P/wD/AP8A/wD/Df/f////yf+Z/7r/r/8A/wD/AP8A/zD///+s/wD/AP8A/2z///9s/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/Df/2//P/+/8V/wD/AP8A/wD/AP8A/wD/xP///xT/AP8A/wD/AP8A/wD/7P/2/wD/AP8A/wD/3P/o/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/0T///90/wD/AP8A/wD/AP8A/wD/AP9h////Vv8A/wD/AP8A/8z/5P8A/wD/AP8A/wD/AP8A/wD/mP///xD/AP8A/wD/AP8A/7H///9f/wD/AP8A/wD/AP/E////Qf8A/wD/AP8A/wX/NP/j//H/B/8A/wD/AP8A/wD/AP8A/+T/6P80/wT/AP8A/wD/AP8A/wD/AP+M////VP8A/wD/AP8A/0z///8c/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/a//V//j/6P+b/xT/AP8A/wD/AP8A/zD////A/wD/AP8A/1j///+I/wD/AP8A/wD/AP8A/wD/O/////T/C/8A/wD/AP8A/wD/AP8A/wD/AP/M////DP8A/wD/AP8A/wD/AP8A/2z/1v/0//P/z/9j/wD/AP8A/wD/AP9w////aP8A/wD/AP+s////LP8A/wD/AP8A/w7/3f/Q/wT/AP8A/wD/AP8A/x7/p//t//n/3/9r/wD/AP8A/wD/MP///6j/AP8A/wD/bP///2z/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/6f///+y/wD/AP8A/wD/AP8A/wD/AP+5////I/8A/wD/AP8A/wD/AP/W////Cf8A/wD/AP/t/+j/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/JP///5j/AP8A/wD/AP8A/wD/AP8A/4T///83/wD/AP8A/wD/zP/k/wD/AP8A/wD/AP8A/wD/AP+Y////EP8A/wD/AP8A/wD/R////+X/Jv8A/wD/AP9v////1/8B/wD/AP8A/wD/AP8A/5X///8f/wD/AP8A/wD/AP8A/xH///+l/wD/AP8A/wD/AP8A/wD/AP8A/4z///9U/wD/AP8A/wD/TP///xz/AP8A/wD/AP9w////aP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP////j/Gf8A/wD/rv///1f/AP8A/wD/AP8A/wD/AP82////o/8A/wD/AP8A/wD/AP8A/wD/AP8A/8z///8M/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/3D///9o/wD/AP8A/6z///8s/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP9s////bP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/4X////K/4//bf8A/wD/AP8A/6f///9d/wD/AP83////6P8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8A/+j/0/8A/wD/AP8A/wD/AP8A/wD/wP/1/wX/AP8A/wD/AP/M/+T/AP8A/wD/AP8A/wD/AP8A/5j///8Q/wD/AP8A/wD/AP8A/4/////4/7n/pv/T////7/8s/wD/AP8A/wD/AP8A/wD/kP///yD/AP8A/wD/AP8A/wD/FP///6D/AP8A/wD/AP8A/wD/AP8A/wD/jP///1T/AP8A/wD/AP9M////HP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////8//j/5f/u////9z/BP8A/wD/AP8A/wD/AP8A/6z///88/wD/AP8A/wD/AP8A/wD/AP8A/wD/zP///wz/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/cP///2j/AP8A/wD/rP///yz/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD///+o/wD/AP8A/2z///9s/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/EP+s//H/8P98/wD/AP8A/wD/NP/+//j/nf+Z/+n/8f/o/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/wD/l////yb/AP8A/wD/AP8A/wD/F//6/6n/AP8A/wD/AP8A/8z/5P8A/wD/AP8A/wD/AP8A/wD/mP///xD/AP8A/wD/AP8A/wD/AP9V/73/7///////1f8Y/wD/AP8A/wD/AP8A/wD/AP+O////JP8A/wD/AP8A/wD/AP8Y////n/8A/wD/AP8A/wD/AP8A/wD/AP+R////S/8A/wD/AP8A/0z///8c/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD///9X/4f/6f/3/7v/Jf8A/wD/AP8A/wD/GP+Q/7n///+x/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/M////DP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9w////aP8A/wD/AP+s////LP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///6j/AP8A/wD/bP///2z/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1f/2f/5/9T/Sv+m/+j/AP8A/wD/AP8A/wD/AP8A/7D///8o/wD/AP8A/wD/AP8o//3/j/8A/wD/AP8A/wD/AP96////OP8A/wD/AP8A/wD/zP/n/xz/GP8A/wD/AP8A/xD/HP+j////EP8A/wD/AP8A/wD/AP8A/wD/AP8A/xb/3////2T/AP8A/wD/AP8A/wD/AP8A/27///+U/yP/AP8A/wD/AP8a/4n///+E/wD/AP8A/wD/AP8A/wD/AP8B/8b///8q/wD/AP8A/wD/TP///xz/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8i//D/7P+a/w3/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/w////xv/AP8A/wD/AP8A/wD/i//4/yP/AP8A/wD/AP8X//D/nf8A/wD/AP8A/wD/AP/M///////k/wD/AP8A/wD/mP////////8Q/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8p/+///f9X/wD/AP8A/wD/AP8A/wD/D//T////1P8A/wD/AP8A/6T////c/xz/AP8A/wD/AP8A/wD/LP+0/8r////J/wD/AP8A/wD/AP9M////HP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/5j/pP/+/+D/Af8A/wD/AP8A/wD/AP8H/0j/I/8A/wD/AP8A/x3/SP8M/wD/AP8A/wD/AP8A/zz/TP9M/0P/AP8A/wD/AP8t/0z/TP9M/wT/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Of+A/3r/Cv8A/wD/AP8A/wD/AP8A/wL/Mf8+/wD/AP8A/wD/Lv8y/wL/AP8A/wD/AP8A/wD/AP8t/+n/7f+v/xz/AP8A/wD/AP8A/0z///8c/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/3P/4/8z/N/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/TP///xz/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/77/9P/0//T/9P/0//T/Jv8A/wD/AP8A/0T/9P/0//T/9P/0//T/oP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Gf8g/yD/IP8g/yD/IP8F/wD/AP8A/wD/Cf8g/yD/IP8g/yD/IP8V/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD///9S/1f/3P/3/w7/AP8A/wD/AP9w////Pf+z//X/8f+s/xb/gf/k//X/zP8//wD/AP8A/wD/AP/w/5z/Zf/X//j/2/9u/wD/AP8A/wD/AP92//X/W/8A/wD/AP8A/wD/Lf+z//D/9//K/03/AP8A/wD/AP8A/wD/AP8w/7T/7//3/9b/WP8A/wD/AP8A/z3/8v+X/wD/AP8A/wD/AP9g/9D/9f/t/8X/Vf8A/wD/AP8A/wD/Wf+7/+j/9P/P/1T/AP8A/wD/AP8A//D/nP9t/97/+f/a/1z/L/+///b/5v+U/wj/AP8A/wD/AP8w////Zf+V/+v/9//H/zP/Vv/U//j/3P9q/wD/AP8A/wD/AP/w/5L/MP/H//j/S/8A/wD/AP8A/wT////Y/wD/AP8A/zz///+c/wD/AP8A/wD/AP83/6f/4P/3/93/fv8C/wD/AP8A/wD/RP///5j/AP8A/wD/fP///1z/AP8A/wD/AP9w////Ov+r//D/7v+0/yL/AP8A/wD/AP8A/4T///9Y/wD/AP8A/7z///8c/wD/AP8A/wD/IP/0/+v/E/8A/wD/rf///2P/AP8A/wD/AP8b/2X/Ev8A/wD/AP8A/wj/Yf8p/wD/AP8A/wD/AP9P/0P/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///7j//v/a/8L/AP8A/wD/AP8A/3D////4/6j/bP/H////7P/e/3T/if/8/+n/Cv8A/wD/AP8A//D/+P/s/3//cf/i////SP8A/wD/AP8A/6P///+L/wD/AP8A/wD/Jf/y//z/qf+a/+v//v9W/wD/AP8A/wD/AP8o//T//v+1/5z/0v9L/wD/AP8A/wD/Y////8v/AP8A/wD/AP9B////0v98/43/0f9c/wD/AP8A/wD/AP9+/7L/hP98/97//P8v/wD/AP8A/wD/8P/4/+b/d/+E//r//P/v/5//bP/N////dP8A/wD/AP8A/zD////4/8j/bv+k////7//z/4T/c//r////NP8A/wD/AP8A//D/zP/q/+j/xf8v/wD/AP8A/wD/BP///9j/AP8A/wD/PP///5z/AP8A/wD/AP8A/0b/xP+L/3f/wf///2z/AP8A/wD/AP9E////mP8A/wD/AP98////XP8A/wD/AP8A/3D////4/7L/bP+g////yP8A/wD/AP8A/wD/hP///1j/AP8A/wD/vP///xz/AP8A/wD/AP8A/23///+f/wD/Tf///7n/AP8A/wD/AP8A/6T///+L/wD/AP8A/wD/ZP///8v/AP8A/wD/AP8k/////P8P/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w//////9m/wD/AP8A/wD/AP8A/wD/cP///7//AP8A/yT//////yv/AP8A/67///80/wD/AP8A/wD/8P///0X/AP8A/1T///+R/wD/AP8A/wD/Gv9l/xL/AP8A/wD/AP+f////Zv8A/wD/Jv/8/+H/Af8A/wD/AP8A/6H///9y/wD/AP8A/wD/AP8A/wD/AP8H/2H/Kf8A/wD/AP8A/3D///9v/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/U////3L/AP8A/wD/AP/w////P/8A/wD/pP///6r/AP8A/y7///+0/wD/AP8A/wD/MP////L/DP8A/wH/4////2r/AP8A/27///90/wD/AP8A/wD/8P///6H/Bf8A/wD/AP8A/wD/AP8E////2P8A/wD/AP88////nP8A/wD/AP8A/wD/AP8A/wD/AP8T////sv8A/wD/AP8A/0T///+Y/wD/AP8A/3z///9c/wD/AP8A/wD/cP///8X/AP8A/wD/1P///xH/AP8A/wD/AP+E////WP8A/wD/AP+8////HP8A/wD/AP8A/wD/Af/C////TP/h//L/H/8A/wD/AP8A/wD/cP/1/1r/AP8A/wD/AP85//H/lf8A/wD/AP8A/w7/3f/Q/wT/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD////Q/wD/AP8A/wD/AP8A/wD/AP9w////ff8A/wD/Av///+r/AP8A/wD/iv///0v/AP8A/wD/AP/w//z/Av8A/wD/Lv///6r/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/97///8L/wD/AP8A/8X///8k/wD/AP8A/wD/3v///w//AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/K//5//r/nP8w/wD/AP8A/wD/AP8A/wD/Yv/I//H/+//9////h/8A/wD/AP8A//D/+/8B/wD/AP+C////av8A/wD/Cv///8v/AP8A/wD/AP8w////vf8A/wD/AP/C////Kv8A/wD/Sv///4v/AP8A/wD/AP/w////Ef8A/wD/AP8A/wD/AP8A/wT////Y/wD/AP8A/zz///+c/wD/AP8A/wD/AP87/7f/6v/6//z////H/wD/AP8A/wD/RP///5j/AP8A/wD/fP///1z/AP8A/wD/AP9w////f/8A/wD/AP+u////Kv8A/wD/AP8A/4T///9Y/wD/AP8A/7z///8c/wD/AP8A/wD/AP8A/yX/9v/8////aP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///6r/AP8A/wD/AP8A/wD/AP8A/3D///9q/wD/AP8A////2P8A/wD/AP+I////TP8A/wD/AP8A//D/6v8A/wD/AP8s////rP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/7v/2/wD/AP8A/wD/r////zf/AP8A/wD/AP/w//b/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zT/vf//////u/8T/wD/AP8A/wD/Wf///9X/Vf8x/2r///+I/wD/AP8A/wD/8P/q/wD/AP8A/4D///9Y/wD/AP8I////zP8A/wD/AP8A/zD///+q/wD/AP8A/8D///8Y/wD/AP9I////jP8A/wD/AP8A//D/6v8A/wD/AP8A/wD/AP8A/wD/BP///9j/AP8A/wD/QP///5z/AP8A/wD/AP8l//T/8P9p/zb/Nf///8j/AP8A/wD/AP9E////mP8A/wD/AP+A////XP8A/wD/AP8A/3D///9q/wD/AP8A/6z///8s/wD/AP8A/wD/hP///1j/AP8A/wD/wP///xz/AP8A/wD/AP8A/wD/AP+w////8v8J/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/cP///2j/AP8A/wD////Y/wD/AP8A/4j///9M/wD/AP8A/wD/8P/o/wD/AP8A/yz///+s/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/Z////Df8A/wD/AP/F////JP8A/wD/AP8A/+H///8N/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/y//uP///6f/AP8A/wD/AP+y////O/8A/wD/XP///4j/AP8A/wD/AP/w/+j/AP8A/wD/gP///1j/AP8A/wj////M/wD/AP8A/wD/MP///6j/AP8A/wD/wP///xj/AP8A/0j///+M/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8D////3P8A/wD/AP9Y////nP8A/wD/AP8A/3L///97/wD/AP8c////yP8A/wD/AP8A/0P///+c/wD/AP8A/5j///9c/wD/AP8A/wD/cP///2j/AP8A/wD/rP///yz/AP8A/wD/AP+D////XP8A/wD/AP/Y////HP8A/wD/AP8A/wD/AP8+//7/6f///4n/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP9w////aP8A/wD/AP///9j/AP8A/wD/iP///0z/AP8A/wD/AP/w/+j/AP8A/wD/LP///6z/AP8A/wD/AP8b/2X/Ev8A/wD/AP8A/5P///9r/wD/AP8m//z/4/8B/wD/AP8A/wD/qf///3D/AP8A/wD/Cf8A/wD/AP8A/wj/Yf8p/wD/AP8A/wD/B/8A/wD/AP8H//z/0f8A/wD/AP8A/7v///8u/wD/AP+t////iP8A/wD/AP8A//D/6P8A/wD/AP+A////WP8A/wD/CP///8z/AP8A/wD/AP8w////qP8A/wD/AP/A////GP8A/wD/SP///4z/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/wD/AP8A/wD/6//4/xH/AP8A/63///+c/wD/AP8A/wD/e////27/AP8A/23////I/wD/AP8A/wD/K////8r/AP8A/wr/5P///1z/AP8A/wD/AP9w////aP8A/wD/AP+s////LP8A/wD/AP8A/2v///+K/wD/AP8v//////8c/wD/AP8A/wD/AP8K/9v/9/8m/8j//P82/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/3D///9o/wD/AP8A////2P8A/wD/AP+I////TP8A/wD/AP8A//D/6P8A/wD/AP8s////rP8A/wD/AP8A/6T///+L/wD/AP8A/wD/Gv/s//3/qv+Z/+r///9c/wD/AP8A/wD/AP8z//r//v+y/5z/zP9z/wD/AP8A/wD/ZP///8v/AP8A/wD/AP93/8v/jP92/6////+R/wD/AP8A/wD/e////8v/ev+3/+L///+I/wD/AP8A/wD/8P/o/wD/AP8A/4D///9Y/wD/AP8I////zP8A/wD/AP8A/zD///+o/wD/AP8A/8D///8Y/wD/AP9I////jP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP+k////0v+O/8D/6////5z/AP8A/wD/AP87////5/99/5z/7v/w/8j/AP8A/wD/AP8F/9////+3/5H/2P/r////XP8A/wD/AP8A/3D///9o/wD/AP8A/6z///8s/wD/AP8A/wD/KP/8//v/nv+Z/+7/6////xz/AP8A/wD/AP8A/4////95/wD/Lv/7/9X/B/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/cP///2j/AP8A/wD////Y/wD/AP8A/4j///9M/wD/AP8A/wD/8P/o/wD/AP8A/yz///+s/wD/AP8A/wD/cP/1/1r/AP8A/wD/AP8A/yP/q//t//f/y/9T/wD/AP8A/wD/AP8A/wD/Pf/A//X/9f/P/z//AP8A/wD/AP85//H/lf8A/wD/AP8A/0H/x//v//j/3f+I/wf/AP8A/wD/AP8J/5z/7v/z/6//Gf/o/4j/AP8A/wD/AP/w/+j/AP8A/wD/gP///1j/AP8A/wj////M/wD/AP8A/wD/MP///6j/AP8A/wD/wP///xj/AP8A/0j///+M/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/xX/pf/p//X/t/8l/+//nP8A/wD/AP8A/wD/cf/j//j/yv84/6n/yP8A/wD/AP8A/wD/MP/A//L/7f+Z/zz///9c/wD/AP8A/wD/cP///2j/AP8A/wD/rP///yz/AP8A/wD/AP8A/1b/1P/3/+D/c/9w////HP8A/wD/AP8A/zn//f/T/wX/AP8A/4n///+H/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wL/iP///5j/AP8A/wD/AP8A/zT/rf/s//T/y/9P/wD/AP8A/wD/AP8A/wX/a//H//P/8v/N/13/AP8A/wD/AP8A/wD/Mv//////////////yP8A/wD/AP8A/wD/AP8A/wD/QP+y/+r/+//X/wD/AP8A/wD/AP/s//////////////////z/AP8A/wD/AP8A/zL/tv/s//D/w/9E/wD/AP8A/wD/AP8A/xn/pv/u//P/vv83/wD/AP8A/wD/AP8A/wH/0v/N/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wT//////////////////////5D/AP8A/wD/AP8Y//3/1P8A/wD/AP8A/wD/Zf///4j/AP8A/wD/AP8P/+7/8/8U/wD/AP8A/xn/9//p/wv/AP8A/wD/AP/Y////CP8A/wD/AP8A/6j///84/wD/AP8A/wD/AP8A/wD/AP8A/wD/sP///yj/AP8A/wD/AP8w////pP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/3D///9o/wD/AP8A/wD/cP///2T/AP8A/wD/AP8A/wD/AP8A/wD/AP+8////0P8D/wD/AP8A/wD/8P/I/wD/AP8A/wD/PP/////////4/9n/hv8L/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/w3/sv//////mP8A/wD/AP8A/zr//P/4/8D/qv/1//7/R/8A/wD/AP8A/wD/Sf///9//qf+k//L///9c/wD/AP8A/wD/AP9F////0P+s/6z/rP+G/wD/AP8A/wD/AP8A/wD/av////P/qP+P/5r/AP8A/wD/AP8A/5r/qP+o/6j/qP+t////4v8A/wD/AP8A/x3/7//y/47/h//q//r/Mv8A/wD/AP8A/wP/0P///7r/n//y//b/L/8A/wD/AP8A/wD/Av/j/93/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/Av+k/6T/pP/g////tv+k/6T/XP8A/wD/AP8A/wD/wf///yb/AP8A/wD/AP+3////M/8A/wD/AP8A/wD/e////4r/AP8A/wD/k////3H/AP8A/wD/AP8A/9j///8I/wD/AP8A/wD/qP///zj/AP8A/wD/AP8A/wD/AP8A/wD/AP+w////KP8A/wD/AP8A/zD///+k/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/cP///2j/AP8A/wD/AP9w////ZP8A/wD/AP8A/wD/AP8A/wD/AP8A/7z//////2//AP8A/wD/AP/w/8j/AP8A/wD/AP88////2v+c/67/7v///7n/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xX/0//7/4////+Y/wD/AP8A/wD/Af+D/x//AP8A/0j///+q/wD/AP8A/wD/AP8A/0L/Av8A/wD/Rf///7H/AP8A/wD/AP8A/1n///9h/wD/AP8A/wD/AP8A/wD/AP8A/wD/Jv/5/9n/Gv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1v///97/wD/AP8A/wD/Yv///2z/AP8A/03///+B/wD/AP8A/wD/Vf///5f/AP8A/zP//P+0/wD/AP8A/wD/AP8A/wX/BP8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/6j///80/wD/AP8A/wD/AP8A/wD/AP9s////eP8A/wD/AP8P//n/3f8A/wD/AP8A/wD/AP8M/+v/9f8X/wD/HP/4/+T/CP8A/wD/AP8A/wD/2P///wj/AP8A/wD/AP+o////OP8A/wD/AP8A/wD/AP8A/wD/AP8A/6////8o/wD/AP8A/wD/MP///6T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9v////aP8A/wD/AP8A/3D///9k/wD/AP8A/wD/AP8A/wD/AP8A/wD/vP/q//b/8f8Y/wD/AP8A//D/yP8A/wD/AP8A/zz///+g/wD/AP8T/+X///8r/wD/AP8A/wD/AP8A/wD/AP8A/wD/Av+u/0T/Of///5j/AP8A/wD/AP8A/wD/AP8A/wD/Gf///8P/AP8A/wD/AP8A/wD/AP8A/wD/AP8t////pv8A/wD/AP8A/wD/bP///0z/AP8A/wD/AP8A/wD/AP8A/wD/AP+L////SP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/yv/5/xb/AP8A/wD/AP9X////ev8A/wD/Vf///3P/AP8A/wD/AP+C////Sf8A/wD/AP/E//j/CP8A/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/qP///zT/AP8A/wD/AP8A/wD/AP8A/xn//f/K/wD/AP8A/1r///+I/wD/AP8A/wD/AP8A/wD/df///47/AP+X////aP8A/wD/AP8A/wD/AP/Y////CP8A/wD/AP8A/6j///84/wD/AP8A/wD/AP8r/7//9//m/4H/pv///yj/AP8A/wD/AP8w////pP8A/wD/P//4/9X/Ev8A/wD/AP8A/wD/EP+i//H/8v+l/3b///9o/wD/AP8A/wD/cP///2T/AP8A/3j///+m/wH/AP8A/wD/AP+8/9//gf///6T/AP8A/wD/8P/I/wD/AP8A/wD/PP///6D/AP8A/wD/pf///03/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP88////mP8A/wD/AP8A/wD/AP8A/wD/AP9S////k/8A/wD/AP8A/wD/AP8A/wD/CP8x/8D/9v82/wD/AP8A/wD/AP+A////lP+J/3X/OP8A/wD/AP8A/wD/AP8A/8v/9f8D/wf/Gv8B/wD/AP8A/wD/AP8A/wD/AP8A/wD/Ov///6T/AP8A/wD/AP8A/wr/2f/3/2r/Uf/q/93/Ef8A/wD/AP8A/3////9R/wD/AP8A/8L///8h/wD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP+o////NP8A/wD/AP8A/wD/AP8A/wD/AP/C////Hf8A/wD/rP///zP/AP8A/wD/AP8A/wD/AP8K/+f/9v84//n/3v8F/wD/AP8A/wD/AP8A/9j///8I/wD/AP8A/wD/qP///zj/AP8A/wD/AP8I/+T///+7/5f/4f/x////KP8A/wD/AP8A/zD///+k/wD/Lv/w/+P/Hf8A/wD/AP8A/wD/AP+s////1P+U/8v/8f///2j/AP8A/wD/AP9w////ZP8A/1////+7/wX/AP8A/wD/AP8A/7z/7P8J/+H///8//wD/AP/w/8j/AP8A/wD/AP88////oP8A/wD/AP+6////OP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zz///+Y/wD/AP8A/wD/AP8A/wD/AP8K/9j/+v8n/wD/AP8A/wD/AP8A/wD/1P//////1f80/wD/AP8A/wD/AP8A/4X//////////////5f/AP8A/wD/AP8A/wD/8f/S/3n/9f////P/df8A/wD/AP8A/wD/AP8A/wD/AP+q////OP8A/wD/AP8A/wD/AP8Z/9f//////9P/F/8A/wD/AP8A/wD/R////8f/IP8P/3f//////yr/AP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/6j///80/wD/AP8A/wD/AP8A/wD/AP8A/23///9u/wD/Cf/0/93/AP8A/wD/AP8A/wD/AP8A/wD/bv////L///9f/wD/AP8A/wD/AP8A/wD/2P///wj/AP8A/wD/AP+o////OP8A/wD/AP8A/2T///+o/wD/AP8T//L///8o/wD/AP8A/wD/MP///6L/H//m/+7/K/8A/wD/AP8A/wD/AP8l//7/3v8K/wD/Av/D////aP8A/wD/AP8A/3D///9i/0r/+//N/w3/AP8A/wD/AP8A/wD/vP/y/wD/Vf///9T/BP8A//D/yP8A/wD/AP8A/zz///+g/wD/Ev9o//7/2f8C/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/PP///5j/AP8A/wD/AP8A/wD/AP8C/6z///9w/wD/AP8A/wD/AP8A/wD/AP9w/5D/rv/1/+//S/8A/wD/AP8A/wD/Av8u/w3/Df9I/+f///9J/wD/AP8A/wD/Af/+//f/2P9w/2j/3P///07/AP8A/wD/AP8A/wD/AP8d//v/zP8A/wD/AP8A/wD/AP8D/53///+9/9L///+q/wf/AP8A/wD/AP8A/6f/////////5P++////HP8A/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/qP///zT/AP8A/wD/AP8A/wD/AP8A/wD/Gv/9/8D/AP9Q////iP8A/wD/AP8A/wD/AP8A/wD/AP8H/+P////Y/wP/AP8A/wD/AP8A/wD/AP/Y////CP8A/wD/AP8A/6j///84/wD/AP8A/wD/lf///03/AP8A/wD/sv///yj/AP8A/wD/AP8w////nv/O//v/O/8A/wD/AP8A/wD/AP8A/1X///+N/wD/AP8A/3L///9o/wD/AP8A/wD/cP///3v/8f/g/xb/AP8A/wD/AP8A/wD/AP+8//T/AP8A/73///90/wD/7v/I/wD/AP8A/wD/PP//////////////0/8m/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP88////mP8A/wD/AP8A/wD/AP8A/5j///+N/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/yf/+//p/wD/AP8A/wD/AP8A/wD/AP8A/wD/cP///4j/AP8A/wD/AP8A//f/8v8Q/wD/AP86////of8A/wD/AP8A/wD/AP8A/4n///9g/wD/AP8A/wD/AP8A/3X///+D/wD/Af+E////kv8A/wD/AP8A/wD/AP9E/3T/ZP8R/8H/9f8C/wD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP+o////NP8A/wD/AP8A/wD/AP8A/wD/AP8A/8P//P8V/6H///8z/wD/AP8A/wD/AP8A/wD/AP8A/wD/fv///3P/AP8A/wD/AP8A/wD/AP8A/9b///8M/wD/AP8A/wD/rP///zb/AP8A/wD/AP+s////Nv8A/wD/AP+c////KP8A/wD/AP8A/zD////3//////9V/wD/AP8A/wD/AP8A/wD/bP///3b/AP8A/wD/XP///2j/AP8A/wD/AP9w////9/////L/I/8A/wD/AP8A/wD/AP8A/7z/9P8A/wD/LP/7//P/G//o/8j/AP8A/wD/AP88////1/+U/7////9k/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zz///+Y/wD/AP8A/wD/AP8A/43///+T/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/h////FP8A/wD/AP8A/wD/AP8A/wD/AP9g////iP8A/wD/AP8A/wD/1//r/wD/AP8A/xr///+4/wD/AP8A/wD/AP8A/wr/7v/s/wj/AP8A/wD/AP8A/wD/wf/8/wf/AP8A/wD/4//l/wD/AP8A/wD/AP8A/wD/AP8A/xT/+P+4/wD/AP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/6j///80/wD/AP8A/wD/AP8A/wD/AP8A/wD/bv///17/6//d/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP90////bP8A/wD/AP8A/wD/AP8A/wD/xP///yr/AP8A/wD/AP/O////H/8A/wD/AP8A/5b///9J/wD/AP8A/63///8o/wD/AP8A/wD/MP///+r/Uf/u/+//Hv8A/wD/AP8A/wD/AP9W////if8A/wD/AP9t////aP8A/wD/AP8A/3D////K/2D////J/wT/AP8A/wD/AP8A/wD/vP/0/wD/AP8A/5L///+p/97/yP8A/wD/AP8A/zz///+g/wD/D//m/+//Gf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/PP///5j/AP8A/wD/AP8A/4T///+V/wD/AP8A/wD/AP8A/wD/AP8A/x7/A/8A/wD/AP8s//3/8P8D/wD/AP8A/wD/Jf8A/wD/AP8D/7r///9R/wD/AP8A/wD/AP+N////Uf8A/wD/X////5L/AP8A/wD/AP8A/wD/af///4n/AP8A/wD/AP8A/wD/AP+9////I/8A/wD/EP/y/9v/AP8A/wD/AP8A/wD/AP8A/wX/rP///07/AP8A/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/qP///zT/AP8A/wD/AP8A/wD/AP8A/wD/AP8a//7/wf///4j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/3T///9s/wD/AP8A/wD/AP8A/wD/AP+B////n/8C/wD/AP9L////2P8A/wD/AP8A/wD/Z////53/AP8A/wz/6////yj/AP8A/wD/AP8w////pP8A/1b////D/wP/AP8A/wD/AP8A/yj////X/wb/AP8A/7b///9o/wD/AP8A/wD/cP///2T/AP+X////hv8A/wD/AP8A/wD/AP+8//T/AP8A/wD/EP/r////+P/I/wD/AP8A/wD/PP///6D/AP8A/1j///+w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP88////mP8A/wD/AP8A/2L////5/7H/sP+w/7D/sP8j/wD/AP8A/wD/lP/l/6n/j/+m//T///9z/wD/AP8A/wD/AP/U/9T/pf+c/9j////E/wP/AP8A/wD/AP8A/xb/6f/6/6b/pf/5//T/Jv8A/wD/AP8A/wD/Af/X//3/IP8A/wD/AP8A/wD/AP8A/13////h/4T/fv/V////cP8A/wD/AP8A/wD/fv+I/5X/3////5n/AP8A/wD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP+o////NP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/xP//////M/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/dP///2z/AP8A/wD/AP8A/wD/AP8A/xL/3f///9r/qv/A//7/+v9D/wD/AP8A/wD/AP8K/+j///+z/5H/2v/q////KP8A/wD/AP8A/zD///+k/wD/AP+j////f/8A/wD/AP8A/wD/AP+y////zv+P/8H/6v///2j/AP8A/wD/AP9w////ZP8A/wr/2P/9/0H/AP8A/wD/AP8A/7z/9P8A/wD/AP8A/2b//////8j/AP8A/wD/AP88////oP8A/wD/AP+5////WP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/zz///+Y/wD/AP8A/wD/jP///////////////////zT/AP8A/wD/AP9F/7j/5v/5/+X/u/9L/wD/AP8A/wD/AP8A/2//0f/w//n/2/+G/wr/AP8A/wD/AP8A/wD/AP8k/6//7//r/7b/K/8A/wD/AP8A/wD/AP9I////sf8A/wD/AP8A/wD/AP8A/wD/AP9S/8T/7v/x/8r/Wv8A/wD/AP8A/wD/AP+2//3/8P/B/1z/Af8A/wD/AP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/6j///80/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9v////3v8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP90////bP8A/wD/AP8A/wD/AP8A/wD/AP8W/5b/4v/6/+r/rv8z/wD/AP8A/wD/AP8A/wD/MP/D//j/5f93/2b///8o/wD/AP8A/wD/MP///6T/AP8A/w7/3//8/zv/AP8A/wD/AP8A/xP/p//z//H/nv8z////aP8A/wD/AP8A/3D///9k/wD/AP8z//r/5P8T/wD/AP8A/wD/vP/0/wD/AP8A/wD/Av/M////yP8A/wD/AP8A/zz///+g/wD/AP8A/yP/9//r/xX/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/2b////y/wn/AP8A/wD/AP8A/wD/AP8A/7z//////////////7j/AP8A/wD/AP9J////nf8A/wD/AP8L//r///8a/wD/AP8A/3r///9n/wD/AP8A/wD/AP8A/xn/kf/b//j/8f/E/2D/Af8A/wD/AP8A/wD/AP88//////////v/6f+4/0X/AP8A/wD/AP8A/7z/////////9f/V/4n/Dv8A/wD/AP8A/wD/f////4j/AP8A/wD/AP+R////df8A/wD/AP8A//z//////////////3j/AP8A/wD/AP8A/wD/AP8A/6b///+8/wD/AP8A/wD/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP+q////Zv8A/wD/AP8I/97/9v8h/wD/AP8A/wD/Cv/f//v/Kv8A/wD/AP8r//v/0/8E/wD/AP8A/wD/AP8A/7f/lP8A/wD/AP8A/wD/AP8A/wD/AP8I/3T/zf/z//b/1P9//wz/AP8A/wD/AP8A/wD//P///////P/k/6D/Hv8A/wD/AP8A/wD/WP///4j/AP8A/wD/AP8o////uP8A/wD/AP8A/zz/////////+P/Z/4P/Cf8A/wD/AP8A/wD/if///13/AP8A/wD/Rv///9r/AP8A/wD/AP+6////J/8A/wD/AP8A/wD/AP8A/wD/wf/5////V/8A/wD/AP8A/wD/AP8A/wD/vP///6j/nP+c/5z/cP8A/wD/AP8A/wz/+//Z/wD/AP8A/0r//////1//AP8A/wD/tv///yj/AP8A/wD/AP8A/yf/5////+D/rP+3//X///+j/wD/AP8A/wD/AP8A/zz////Z/5j/nv/F/////f86/wD/AP8A/wD/vP///6X/mP+q/+z///+4/wD/AP8A/wD/AP8O/+3/9P8V/wD/AP8b//f/5/8J/wD/AP8A/wD//P/z/5z/nP+c/5z/Sf8A/wD/AP8A/wD/AP8A/wv/9f/5//3/Gv8A/wD/AP8A/wD/AP8A/7z///8g/wD/AP8A/wD/AP8A/wD/AP8A/xr/8v/r/xD/AP8A/3z///97/wD/AP8A/wD/AP8A/0z///+7/wD/AP8A/7v//v88/wD/AP8A/wD/AP8A/xP/+v+U/wD/AP8A/wD/AP8A/wD/AP8J/8X////v/7P/rv/o////0v8R/wD/AP8A/wD/AP/8//P/mP+j/9r////j/w//AP8A/wD/AP9Y////iP8A/wD/AP8A/yj///+4/wD/AP8A/wD/PP///9n/mP+r/+z///+z/wD/AP8A/wD/AP9J////mf8A/wD/AP+K//////8f/wD/AP8D//L/5/8A/wD/AP8A/wD/AP8A/wD/Hv/+/5b/9v+y/wD/AP8A/wD/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP/J////Fv8A/wD/jv/7/+7/pP8A/wD/Af/v/+f/AP8A/wD/AP8A/wD/yP///43/A/8A/wD/G//Y////Xv8A/wD/AP8A/wD/PP///6D/AP8A/wD/jP///43/AP8A/wD/AP+8////IP8A/wD/Fv/1////Dv8A/wD/AP8A/wD/eP///4z/AP8A/5X///9s/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Xf///43///9y/wD/AP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP8A/wD/AP8A/wD/AP9v////kP8A/xz/9P/X/wb/AP8A/wD/AP8A/wD/AP+v////UP8A/1H///+d/wD/AP8A/wD/AP8A/zv/4////////////wz/AP8A/wD/AP8A/4j////B/w//AP8A/wj/q////57/AP8A/wD/AP8A//z/4P8A/wD/A/+3////av8A/wD/AP8A/1j///+I/wD/AP8A/wD/KP///7j/AP8A/wD/AP88////oP8A/wD/E//m////Kv8A/wD/AP8A/wz/+//V/wD/AP8A/87/6f///2T/AP8A/zH///+o/wD/AP8A/wD/AP8A/wD/AP94////R/+w//r/E/8A/wD/AP8A/wD/AP8A/7z///8g/wD/AP8A/wD/AP8A/wD/AP8A/4n///9R/wD/AP/S/8//u//o/wH/AP8t////qP8A/wD/AP8A/wD/Mv///93/A/8A/wD/AP8A/0X////K/wD/AP8A/wD/AP88////oP8A/wD/AP9i////i/8A/wD/AP8A/7z///8g/wD/AP8A/+L///8M/wD/AP8A/wD/AP8L/+n/9v8Y/x3/+f/h/wb/AP8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+4//r/Ef/s/83/AP8A/wD/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP8A/wP/z//7/yr/pv///0H/AP8A/wD/AP8A/wD/AP8A/x3/9P/d/w//3v/s/xP/AP8A/wD/AP8A/wD/L/+T////wf9s/2z/Bf8A/wD/AP8A/wP/7//+/yL/AP8A/wD/AP8Q//X/+v8Q/wD/AP8A/wD//P/g/wD/AP8A/2T///+N/wD/AP8A/wD/WP///4j/AP8A/wD/AP8o////uP8A/wD/AP8A/zz///+g/wD/AP8A/6T///9N/wD/AP8A/wD/AP/J//7/Ev8A/xP//v+T//b/qv8A/wD/bf///2j/AP8A/wD/AP8A/wD/AP8A/9P/8P8G/1z///9o/wD/AP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP8A/wD/AP8A/wD/Sf///43/AP8W//7/lv+B////L/8A/2j///9p/wD/AP8A/wD/AP9s////k/8A/wD/AP8A/wD/Av/1//z/Cf8A/wD/AP8A/zz///+g/wD/BP8p/8r//f85/wD/AP8A/wD/vP///yD/AP8P/2n///+3/wD/AP8A/wD/AP8A/wD/cv///5D/mf///2T/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8W//z/t/8A/5z///8o/wD/AP8A/wD/AP8A/7z///8g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP83//3/1v///6L/AP8A/wD/AP8A/wD/AP8A/wD/AP91////1f///2L/AP8A/wD/AP8A/wD/AP8A/0T///+U/wD/AP8A/wD/AP8A/wD/LP///9P/AP8A/wD/AP8A/wD/uP///0X/AP8A/wD/AP/8/+D/AP8A/wD/ff///33/AP8A/wD/AP9Y////iP8A/wD/AP8A/yj///+4/wD/AP8A/wD/PP///6D/AP8A/wD/vf///z3/AP8A/wD/AP8A/4n///9N/wD/Vv///1b/wf/t/wL/AP+o////Kf8A/wD/AP8A/wD/AP8A/y////+i/wD/Dv/4/8P/AP8A/wD/AP8A/wD/AP+8//////////////9w/wD/AP8A/wD/AP8M//v/yf8A/1r///9V/z////90/wD/pP///yn/AP8A/wD/AP8A/4T///92/wD/AP8A/wD/AP8A/9v///8f/wD/AP8A/wD/PP//////////////6v9L/wD/AP8A/wD/AP+8//////////////+o/w7/AP8A/wD/AP8A/wD/AP8I/+X/9//6/9v/BP8A/wD/AP8A/wD/AP/8//////////////8w/wD/AP8A/wD/AP8A/2////9i/wD/R////4P/AP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/n/////T/Fv8A/wD/AP8A/wD/AP8A/wD/AP8A/wT/2////8n/Af8A/wD/AP8A/wD/AP8A/wD/RP///5T/AP8A/wD/AP8A/wD/AP9E////tv8A/wD/AP8A/wD/AP+b////X/8A/wD/AP8A//z/4P8A/xH/Uf/u////Mf8A/wD/AP8A/1j///+I/wD/AP8A/wD/KP///7j/AP8A/wD/AP88////oP8B/xn/d////+j/B/8A/wD/AP8A/wD/SP///4n/AP+a//7/Fv9/////NP8A/+P/6P8A/wD/AP8A/wD/AP8A/wD/iv///07/AP8A/7L//v8g/wD/AP8A/wD/AP8A/7z///+l/5j/mP+Y/0L/AP8A/wD/AP8A/wD/yf/6/wr/nf/9/xP/Bf/z/7n/AP/f/+n/AP8A/wD/AP8A/wD/hP///3X/AP8A/wD/AP8A/wD/2////x7/AP8A/wD/AP88////1P+M/4//rv/5/+j/Nf8A/wD/AP8A/7z///+a/4z/mf/X////nv8A/wD/AP8A/wD/AP8A/wD/a///////W/8A/wD/AP8A/wD/AP8A//z/8/+Y/5j/mP+Y/xz/AP8A/wD/AP8A/wD/yv/7/xL/AP8E/+3/3v8A/wD/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Bf/W//////9B/wD/AP8A/wD/AP8A/wD/AP8A/wD/JP/4////7P8U/wD/AP8A/wD/AP8A/wD/AP9E////lP8A/wD/AP8A/wD/AP8A/0T///+1/wD/AP8A/wD/AP8A/5v///9e/wD/AP8A/wD//P////////////3/df8A/wD/AP8A/wD/WP///4j/AP8A/wD/AP8o////uP8A/wD/AP8A/zz//////////////+//Qv8A/wD/AP8A/wD/AP8M//v/xf8A/93/0f8A/zn///95/x////+p/wD/AP8A/wD/AP8A/wD/Af/j/////////////////3n/AP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP8A/wD/AP8A/wD/AP+J////Pf/d/83/AP8A/7T/9f8e////qv8A/wD/AP8A/wD/AP9r////k/8A/wD/AP8A/wD/Av/1//z/Cf8A/wD/AP8A/zz///+g/wD/AP8A/0r////C/wD/AP8A/wD/vP///yD/AP8A/wP/x////0L/AP8A/wD/AP8A/wD/AP8G//j/8f8C/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A/wD/Jf///////////////////zn/AP8A/wD/AP8A/7z///8g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP95////jf/3/9j/B/8A/wD/AP8A/wD/AP8A/wD/AP+4//3/h////5//AP8A/wD/AP8A/wD/AP8A/0T///+U/wD/AP8A/wD/AP8A/wD/K////9P/AP8A/wD/AP8A/wD/uP///0X/AP8A/wD/AP/8//P/nP+V/3X/Jv8A/wD/AP8A/wD/AP9W////jP8A/wD/AP8A/yz///+2/wD/AP8A/wD/PP///9r/m/+R/2b/E/8A/wD/AP8A/wD/AP8A/wD/yf/4/yL///+N/wD/A//w/7r/Wf///2r/AP8A/wD/AP8A/wD/AP9B////1f+k/6T/pP+k//3/1P8A/wD/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP8A/0r///+E////if8A/wD/b////4H///9q/wD/AP8A/wD/AP8A/zH////c/wP/AP8A/wD/AP9E////yv8A/wD/AP8A/wD/PP///6D/AP8A/wD/D////+v/AP8A/wD/AP+8////IP8A/wD/AP+P////a/8A/wD/AP8A/wD/AP8A/wD/9P/s/wD/AP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD/AP+B////vv+k/6T/pP+5////lP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP8A/wD/AP8A/wD/AP8f//X/0/8E/4L///9+/wD/AP8A/wD/AP8A/wD/AP9V////l/8A/8H///8//wD/AP8A/wD/AP8A/wD/Of///6P/AP8A/wD/AP8A/wD/AP8C/+7//v8h/wD/AP8A/wD/D//0//n/EP8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP8A/0T///+q/wD/AP8A/wD/Tv///5//AP8A/wD/AP88////oP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+K////g////0n/AP8A/6//8P+R////Kv8A/wD/AP8A/wD/AP8A/5z///9O/wD/AP8A/wD/wf///y//AP8A/wD/AP8A/7z///8g/wD/AP8A/wD/AP8A/wD/AP8A/wD/Dv/8/9n///9F/wD/AP8p////2f///yr/AP8A/wD/AP8A/wD/AP/H////if8C/wD/AP8Z/9b///9d/wD/AP8A/wD/AP88////oP8A/wD/AP9Y////0f8A/wD/AP8A/7z///8g/wD/AP8G/9H///9R/wD/AP8A/wD/AP8A/wD/AP/0/+z/AP8A/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP8A/9v/+/8T/wD/AP8A/wv/9f/r/wT/AP8A/wD/AP+8////IP8A/wD/AP8A/wD/AP8A/wD/AP8A/7L///9B/wD/Cv/i//j/Jf8A/wD/AP8A/wD/AP8M/+T/7f8T/wD/MP/9/9f/Bv8A/wD/AP8A/wD/AP8Q//X/+P+W/6H/F/8A/wD/AP8A/wD/h////77/Df8A/wD/B/+o////nf8A/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A/wD/Df/0//f/K/8A/wD/Cv/A////WP8A/wD/AP8A/zz///+g/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/0v////Z//n/C/8A/wD/af///9n/6v8A/wD/AP8A/wD/AP8A/wf/7//x/wf/AP8A/wD/AP9q////iv8A/wD/AP8A/wD/vP///6z/oP+g/6D/c/8A/wD/AP8A/wD/AP8A/8v////4/wn/AP8A/wD/5P///+r/AP8A/wD/AP8A/wD/AP8A/yf/6P///9z/p/+z//T///+i/wD/AP8A/wD/AP8A/zz////a/5z/nv+7//3///9d/wD/AP8A/wD/vP///6j/nP+n/+L////W/wf/AP8A/wD/AP8A/wD/AP8A//T/7P8A/wD/AP8A/wD/AP8A/wD//P/0/6D/oP+g/6D/S/8A/wD/AP8A/zf///+4/wD/AP8A/wD/AP+q////Sv8A/wD/AP8A/7z///+v/6T/pP+k/57/AP8A/wD/AP8A/03///+p/wD/AP8A/1j///+8/wD/AP8A/wD/AP8A/47///9p/wD/AP8A/5j///98/wD/AP8A/wD/AP8A/wD/VP/c//j/2v8W/wD/AP8A/wD/AP8J/8b////t/67/qv/l////0v8R/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/2/////4/7n/rf/k////u/8D/wD/AP8A/wD/PP///6D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Dv/8////wf8A/wD/AP8k//////+r/wD/AP8A/wD/AP8A/wD/Uv///6L/AP8A/wD/AP8A/xb//P/j/wL/AP8A/wD/AP+8//////////////+4/wD/AP8A/wD/AP8A/wD/jP///73/AP8A/wD/AP+f////q/8A/wD/AP8A/wD/AP8A/wD/AP8Z/5H/3P/5//L/xP9g/wH/AP8A/wD/AP8A/wD/PP///////////+z/wP9K/wD/AP8A/wD/AP+8//////////n/2/+O/xP/AP8A/wD/AP8A/wD/AP8A/wD/9P/s/wD/AP8A/wD/AP8A/wD/AP/8//////////////94/wD/AP8A/wD/kv///2L/AP8A/wD/AP8A/1P///+m/wD/AP8A/wD/vP//////////////+P8A/wD/AP8A/wn/3//1/x3/AP8A/wD/AP/C////XP8A/wD/AP8A/y3/+//Q/wP/AP8A/wD/FP/u//f/JP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/CP91/87/9f/3/9T/gP8M/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9T/8T/9P/2/9T/ef8F/wD/AP8A/wD/AP88////oP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8z///99/wD/AP8A/wD/3v///2v/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xX/+v/o/wP/AP8A/wD/0////yX/AP8A/wD/AP88////oP8A/wD/AP8A/zz//////1T/AP8A/wD/AP9w////SP8A/wD/AP8A/7z/////////8v/K/3f/C/8A/wD/AP8A/wD/AP+E//////////////////////8Q/wD/AP8A/wD/Pv///8f/AP8A/wD/AP9R////tf8A/wD/AP8A//z/4P8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP/8///////9//D/yP9r/wH/AP8A/wD/AP8A/0T//////////////////////1D/AP8A/wD/AP8A/wT/ev/b//v/////////3P8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP8A/wD/AP8A/wD//P///87/AP8A/wD/AP8A/xH/+v///8T/AP8A/wD/AP8w////bP+L/+n/9/++/yn/AP8A/wD/AP8A/1n/9f9S/wD/AP8A/wD/AP8A/zj/xP/2/+n/kv8K/wD/AP8A/wD/AP8A/yT/x//x/6z/Df8A/wD/AP+4/8v/AP8A/wD/AP8A/wD/AP8A/1b/wv/z/+3/tv8t/wD/AP8A/wD/AP8U/4j/1//4/+v/uf85/wD/AP8A/wD/AP+8////IP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP+t////RP8A/wD/J////8j/AP8A/wD/AP8A/zz///+g/wD/AP8A/wD/PP//////4/8M/wD/AP8A/3D///9I/wD/AP8A/wD/vP///6X/mP+q/+f////U/xj/AP8A/wD/AP8A/1T/pP+k/7L////k/6T/pP+k/wr/AP8A/wD/AP8A/7v///9K/wD/AP8B/9H//v8y/wD/AP8A/wD//P/g/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A//z/8/+Y/5v/tf/4////eP8A/wD/AP8A/wD/K/+k/6T/pP/3//v/pP+k/6T/M/8A/wD/AP8A/wD/ev///5P/P/+d////tf88/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/wD/AP8A/wD/AP/8//////8j/wD/AP8A/wD/ZP//////xP8A/wD/AP8A/zD////2/8T/bv+X//7/4P8F/wD/AP8A/wD/Zf///17/AP8A/wD/AP8A/yP/8//3/6T/w////6j/AP8A/wD/AP8A/wD/uP/g/2j/9f+H/wD/AP8+////RP8A/wD/AP8A/wD/AP8A/3b////s/7L/uP///+z/Gf8A/wD/AP8A/4n////N/6D/s//9//X/J/8A/wD/AP8A/7z///8g/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/0r///+c/wD/AP97////bP8A/wD/AP8A/wD/PP///6D/AP8A/wD/AP88////4f///4r/AP8A/wD/cP///0j/AP8A/wD/AP+8////IP8A/wD/Bv+Z////t/8A/wD/AP8A/wD/AP8A/wD/KP///7T/AP8A/wD/AP8A/wD/AP8A/wD/OP///8v/AP8A/1X///+s/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/0z////N/wD/AP8A/wD/AP8A/wD/AP8A/+j/9P8A/wD/AP8A/wD/AP8A/wD/AP/R//H/A/8A/wj//P/B/wD/AP8A/wD/AP9w////Z/8A/wD/AP8A/wD/AP8A/wD/AP8A//z/yv///3j/AP8A/wD/AP+7/+z/8//E/wD/AP8A/wD/MP///+v/CP8A/wD/nP///1v/AP8A/wD/AP8A/wr/AP8A/wD/AP8A/wD/k////1//AP8B/77///8q/wD/AP8A/wD/Af/6/4f/AP+s/9T/AP8A/8X/vv8A/wD/AP8A/wD/AP8A/wD/E/+F/wv/AP8A/4j///9q/wD/AP8A/wD/Cf87/wD/AP8A/4X///9x/wD/AP8A/wD/vP///yD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/wD/AP8A/wD/A//k/+7/Bv8A/8//+v8U/wD/AP8A/wD/AP88////oP8A/wD/AP8A/zz///9y/+3/+v8q/wD/AP9w////SP8A/wD/AP8A/7z///8g/wD/AP8A/wX/3////yz/AP8A/wD/AP8A/wD/AP8o////tP8A/wD/AP8A/wD/AP8A/wD/AP8A/7X///9O/wL/1P/9/yr/AP8A/wD/AP8A//z/4P8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/Iv///8v/AP8A/wD/AP8A/wD/AP8A/wD/6P/0/wD/AP8A/wD/AP8A/wD/AP8A/+D/6P8A/wD/Af/3/8n/AP8A/wD/AP8A/3D///9v/6L/8v/w/6D/Dv8A/wD/AP8A/wD//P+k/9v/zf8A/wD/AP8W//z/l//4/8T/AP8A/wD/AP8w////t/8A/wD/AP9S////if8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP/X//v/CP8A/wD/Yv///3T/AP8A/wD/AP8R////c/8A/5T/7P8A/03///83/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Wf///4P/AP8A/wD/AP8A/wD/AP8A/wD/bf///2b/AP8A/wD/AP+8////IP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP8A/4X///9N/yP///+y/wD/AP8A/wD/AP8A/zz///+g/wD/AP8A/wD/PP///2z/a////77/AP8A/3D///9I/wD/AP8A/wD/vP///yD/AP8A/wD/AP+S////av8A/wD/AP8A/wD/AP8A/yj///+0/wD/AP8A/wD/AP8A/wD/AP8A/wD/Mv///8//Wv///6T/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A//z/4P8A/wH/Gf+d////d/8A/wD/AP8A/wD/AP8A/wD/AP/o//T/AP8A/wD/AP8A/wD/AP8A/wD/m////2D/Cv9q////g/8A/wD/AP8A/wD/cP////D/yP+U/9T///+m/wD/AP8A/wD/AP/8/63/h////yL/AP8A/2r///89////xP8A/wD/AP8A/zD///+s/wD/AP8A/0L///+g/wD/AP8A/wD/cP///2j/AP8A/wD/AP8B//r/3f8A/wD/AP86////nP8A/wD/AP8A/wP//P9+/wD/ov/c/wL/0f+v/wD/BP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+S////U/8A/wD/AP8A/wD/AP8A/w7/R//k/97/Dv8A/wD/AP8A/7z///8g/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/wD/I//+/53/dv///1b/AP8A/wD/AP8A/wD/PP///6D/AP8A/wD/AP88////cv8D/9H///9Z/wD/cP///0j/AP8A/wD/AP+8////IP8A/wD/AP8A/3P///+E/wD/AP8A/wD/AP8A/wD/KP///7T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/67////y//v/JP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD//P////////////v/ef8B/wD/AP8A/wD/AP8A/wD/AP8A/+j/9P8A/wD/AP8A/wD/AP8A/wD/AP8Q/8r///////3/nv8H/wD/AP8A/wD/AP9w////x/8C/wD/Cf/h//3/Hv8A/wD/AP8A//z/sf8z////d/8A/wD/wv/h/wf////E/wD/AP8A/wD/MP///8L/AP8A/wD/W////4j/AP8A/wD/AP9w////aP8A/wD/AP8A/wv////P/wD/AP8A/yz///+t/wD/AP8A/wD/AP/I/8f/J//l/6f/W//9/y7/m//1/+r/U/8A/wD/AP8A/wD/AP8A/wD/AP8p//j/3P8F/wD/AP8A/wD/AP8U/////////7L/Fv8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8D/3/+8//D/CP8A/wD/AP8A/wD/AP88////oP8A/wD/AP8A/zz///90/wD/P////+b/Df9u////SP8A/wD/AP8A/7z///8g/wD/AP8A/wD/df///4D/AP8A/wD/AP8A/wD/AP8o////tP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Lf/+////m/8A/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP/8//H/jP+M/6H/6//5/2T/AP8A/wD/AP8A/wD/AP8A/wD/6P/0/wD/AP8A/wD/AP8A/wD/AP8A/0D/+v9V/yj/Cf8A/wD/AP8A/wD/AP8A/3D///9+/wD/AP8A/5b///9J/wD/AP8A/wD//P+z/wD/3f/M/wD/G//+/4j/CP///8T/AP8A/wD/AP8w////+f8b/wD/Af+2////V/8A/wD/AP8A/3D///9o/wD/AP8A/wD/C////87/AP8A/wD/K////67/AP8A/wD/AP8A/z7/8////+f/L//c/6H/Uf///yz/d//y/w//AP8A/wD/AP8A/wD/AP8V/9r/+P84/wD/AP8A/wD/AP8A/wr/iP+V/77//f/X/yP/AP8A/wD/AP+8////IP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Xv////v/nP8A/wD/AP8A/wD/AP8A/zz///+g/wD/AP8A/wD/PP///3T/AP8A/6j///+O/2j///9I/wD/AP8A/wD/vP///yD/AP8A/wD/AP+Z////Y/8A/wD/AP8A/wD/AP8A/yj///+0/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/77///8z/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD//P/g/wD/AP8A/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8Y//L/9v8M/wD/AP8A/wD/AP8A/wD/AP/o//T/AP8A/wD/AP8A/wD/AP8A/wD/Yf///7j/mP+X/4P/O/8A/wD/AP8A/wD/cP///2z/AP8A/wD/gv///2D/AP8A/wD/AP/8/7T/AP+J////Iv9x////Lv8I////xP8A/wD/AP8A/zD////1/+T/l/+/////3P8E/wD/AP8A/wD/cP///2j/AP8A/wD/AP8B//n/3P8A/wD/AP86////nv8A/wD/AP8A/wD/AP8O/zL/Cf9p//n/If+P/+z/AP8u////SP8A/wD/AP8A/wD/AP8N/8v/+/9S/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9j////qv8A/wD/AP8A/7z///8g/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8K//H///9A/wD/AP8A/wD/AP8A/wD/PP///6D/AP8A/wD/AP88////dP8A/wD/HP/1//v/i////0j/AP8A/wD/AP+8////IP8A/wD/AP8L/+n//v8i/wD/AP8A/wD/AP8A/wD/KP///7T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/tP///yz/AP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD/z////yv/AP8A/wD/AP8A/wD/AP8A/+j/9P8A/wD/AP8A/wD/AP8A/wD/AP9F/+7//////////////1n/AP8A/wD/AP9w////gP8A/wD/AP+Y////SP8A/wD/AP8A//z/tP8A/zX///92/8n/0/8A/wj////E/wD/AP8A/wD/MP///57/h//p//f/u/8l/wD/AP8A/wD/AP9w////aP8A/wD/AP8A/wD/0v/6/wf/AP8A/1////95/wD/AP8A/wD/AP8A/wD/AP8J/+b/k/8A/6H/4/8A/yT///9c/wD/AP8A/wD/AP8K/8L//P9X/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/yH////U/wD/AP8A/wD/vP///yD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wf/7v/h/wH/AP8A/wD/AP8A/wD/AP88////oP8A/wD/AP8A/zz///90/wD/AP8A/3z////3////SP8A/wD/AP8A/7z///8g/wD/AP8Q/7H///+n/wD/AP8A/wD/AP8A/wD/AP8o////tP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+0////LP8A/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8A//z/4P8A/wD/AP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/If/2//7/E/8A/wD/AP8A/wD/AP8A/wD/6P/0/wD/AP8A/wD/AP8A/wD/AP9D//7/hv8L/wD/Bv88//3/s/8A/wD/AP8A/3D////O/wP/AP8L/+T//P8b/wD/AP8A/wD//P+0/wD/AP/f/+D///95/wD/CP///8T/AP8A/wD/AP8w////p/8A/wD/AP8A/wD/AP8A/wD/AP8A/3D///9o/wD/AP8A/wD/AP+K////W/8A/wD/uf///zP/AP8A/wD/AP8A/wD/AP8A/3f/9f8Y/wD/i//1/wL/Pv///0f/AP8A/wD/AP8H/7v//P9Z/wD/AP8A/wD/AP8A/wD/AP8h/wD/AP8A/wD/af///7P/AP8A/wD/AP+8////IP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/bP///3z/AP8A/wD/AP8A/wD/AP8A/zz///+g/wD/AP8A/wD/PP///3T/AP8A/wD/B//d//////9I/wD/AP8A/wD/vP///6j/nv+2//L////H/w//AP8A/wD/AP8A/wD/AP8A/yj///+0/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/7T///8s/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD//P/0/6T/pP+k/6T/df8A/wD/AP8A//z/8/+c/5z/r//y////nf8A/wD/AP8A/wD/AP8A/wD/AP/o//T/AP8A/wD/AP8A/wD/AP8A/4T///8i/wD/AP8A/xX/+/+q/wD/AP8A/wD/cP////P/zP+U/9T///+g/wD/AP8A/wD/AP/8/7T/AP8A/4v////+/yD/AP8I////xP8A/wD/AP8A/zD///+o/wD/AP8A/wD/AP8A/wD/AP8A/wD/cP///2j/AP8A/wD/AP8A/xr/7f/2/5//vv///7b/AP8A/wD/AP8A/wD/AP8A/w//7v+F/wD/AP9D////gv+3//P/D/8A/wD/AP8A/6P////m/7D/sP+w/7D/p/8A/wD/AP8A/9T/0/+h/5D/t//9//n/Ov8A/wD/AP8A/7z///8g/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/4b/of/6/+H/EP8A/wD/AP8A/wD/AP8A/wD/PP///6D/AP8A/wD/AP88////dP8A/wD/AP8A/0///////0j/AP8A/wD/AP+8///////9/+z/v/9m/wX/AP8A/wD/AP8A/wD/AP8A/wD/KP///7T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/tP///yz/AP8A/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP/8//////////////+4/wD/AP8A/wD//P/////////z/9D/bf8F/wD/AP8A/wD/AP8A/wD/AP8A/+j/9P8A/wD/AP8A/wD/AP8A/wD/Sf///8D/XP9U/3X/2f/5/zz/AP8A/wD/AP9w//7/LP+q//T/8P+d/wz/AP8A/wD/AP8A//z/tP8A/wD/Nv///8X/AP8A/wj////E/wD/AP8A/wD/MP///6j/AP8A/wD/AP8A/wD/AP8A/wD/AP9w////aP8A/wD/AP8A/wD/AP8w/8D/9//s/5r/D/8A/wD/AP8A/wD/AP8A/wD/hf/v/xD/AP8A/wD/gv/s/97/Uf8A/wD/AP8A/wD/zP/////////////////0/wD/AP8A/wD/bP/J/+z/+P/d/6X/LP8A/wD/AP8A/wD/vP///yD/AP8A/wD/AP/w/+j/AP8A/wD/AP8A/wD/AP8A/wD/1P/0/7j/Jv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1//zP/y//r/4/+n/zH/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8w////qP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Lv+4/+3///////////9c/wD/AP8A/wD//P///////f/s/7r/WP8B/wD/AP8A/wD/AP8A//D/6P8A/wD/AP8A/wD/AP8A/wD/AP8A/zz/////////+//k/6j/N/8A/wD/AP8A/wD/AP8A/zz/p//d//f/3P96/wL/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Ef/o/+P/T/9T/+r//P9h/xT/AP8A/wD/AP/8//P/mP+a/7X/9P///6n/Av8A/wD/AP8A/wD/8P/o/wD/AP8A/wD/AP8A/wD/AP8A/wD/PP///9n/mP+e/8L//f/9/27/AP8A/wD/AP8A/wD/mf/0/7j/nf/a////eP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9R////dP8A/wD/hP///0H/AP8A/wD/AP8A//z/4P8A/wD/AP8W/8n///93/wD/AP8A/wD/AP/w/+f/AP8A/wD/AP8A/wD/AP8A/wD/AP88////oP8A/wD/AP80/+v//f85/wD/AP8A/wD/AP8U/w//AP8A/w7/+P/J/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/2D///9o/wD/AP94////Sf8A/wD/AP8A/wD//P/g/wD/AP8A/wD/Jv/+/+r/Av8A/wD/AP8A//D/4P9Q/9f/+f/V/03/AP8A/wD/AP8A/zz///+g/wD/AP8A/wD/ZP///6z/AP8A/wD/AP8A/wD/AP8A/wD/Df/5/8H/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/IP/7/8r/Gv8c/9L/7/8T/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/9L///8q/wD/AP8A/wD/8P/0/+//nv+j//v//P8p/wD/AP8A/wD/PP///6D/AP8A/wD/AP8T////6v8A/wD/AP8A/wD/AP8A/wD/CP+x////VP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z//v//////3v9E/wD/AP8A/wD/AP8A//z/4P8A/wD/AP8A/wD/s////0T/AP8A/wD/AP/w////Sv8A/wD/a////5v/AP8A/wD/AP88////oP8A/wD/AP8A/wD/8////wT/AP8A/wD/AP8A/wD/DP/E//3/bv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/u//C/yn/G/8B/wD/AP8A/wD/AP8A/wD//P/g/wD/AP8A/wD/AP+1////QP8A/wD/AP8A//D/+/8C/wD/AP8W////yf8A/wD/AP8A/zz///+g/wD/AP8A/wD/AP/1//3/Av8A/wD/AP8A/wD/AP+I//7/U/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/h/+f/nf+Y/5H/a/8N/wD/AP8A/wD/AP/8/+D/AP8A/wD/AP8A/9n///8j/wD/AP8A/wD/8P/s/wD/AP8A/wP////g/wD/AP8A/wD/PP///6D/AP8A/wD/AP8Y////4/8A/wD/AP8A/wD/AP8A/8H/zf8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8P/6X//////////////9L/Bv8A/wD/AP8A//z/4P8A/wD/AP8A/zT////g/wD/AP8A/wD/AP/w//3/A/8A/wD/GP///8j/AP8A/wD/AP88////oP8A/wD/AP8A/3T///+g/wD/AP8A/wD/AP8A/wD/Lv8t/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8L/4v8t/wH/AP8T/6v///8z/wD/AP8A/wD//P/g/wD/AP8A/yf/2f///2f/AP8A/wD/AP8A//D///9S/wD/AP9v////l/8A/wD/AP8A/zz///+g/wD/AP8A/0z/9f/6/y3/AP8A/wD/AP8A/wD/AP9G/0//AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wX//v+i/wD/AP8A/wD/kP///yr/AP8A/wD/AP/8//P/nP+h/8L//P///5b/AP8A/wD/AP8A/wD/8P/0//X/oP+j//v/+v8l/wD/AP8A/wD/PP///9r/nP+l/9T////5/13/AP8A/wD/AP8A/wD/AP8P//3///8k/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/J//b/fP9R/1//nv/+/7X/AP8A/wD/AP8A//z///////r/5f+u/0f/AP8A/wD/AP8A/wD/AP/w/5T/W//b//n/0/9J/wD/AP8A/wD/AP88//////////f/2v+a/yr/AP8A/wD/AP8A/wD/AP8A/wT/0P/c/w3/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz/of/k//r/8v/K/3P/Bv8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wA="), "format": "LumAlpha8", "height": 256, "mipmaps": false, "width": 256 } [sub_resource type="Image" id="Image_vwwu5"] data = { "data": PackedByteArray("/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP////////////v/6f++/3X/D/8A/wD/AP8A/wD/AP8A/1z///+B/wb/h//m//f/Mv8A/wD/AP8A/wD/Av9q/83/9v/z/8T/U/8A/wD/AP8A/wD/AP8A/wL/Zv/G//L/9//d/5z/FP8A/wD/AP8A/wD/AP8q/4D/FP8A/wD/AP8A/wD/AP8A/wD/Af9l/8f/8v/3/9T/ev8I/wD/AP8A/wD/AP8A/wD/Bf9u/8z/7v/1/9v/ov89/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9H/77/6//y/87/Yf8A/wD/AP8A/wD/AP8A/wD/AP8A/xz////////////////////w/wD/AP8A/wD/XP/z/8f/B/8A/wD/AP8A/xz//////+//F/8A/wD/AP8A/wD/XP///8j/AP8A/wD/AP8A/wD/Pv+a/9P/8//1/8z/Zf8A/wD/AP8A/wD/AP9c////jf84/7n/8f/2/8//X/8A/17/y//2//P/u/8y/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c///////2//T/+f/////////r/0X/AP8A/wD/AP8A/wD/XP///6b/sf////////8c/wD/AP8A/wD/AP+m/////P/P/9X//////3f/AP8A/wD/AP8A/wD/qP///////v/3////6f8F/wD/AP8A/wD/AP8A/5b///8o/wD/AP8A/wD/AP8A/wD/AP+l///////4//L//////8b/CP8A/wD/AP8A/wD/AP+i/////v/V/9f/+P///2f/AP8A/wD/AP8A/wD/AP8A/wD/AP9U///////r/+b//////2T/AP8A/wD/AP8A/wD/AP8A/wD/HP//////+f/4//j/+P/4/+j/AP8A/wD/AP+q//////8q/wD/AP8A/wD/HP////////+l/wD/AP8A/wD/AP9c////yP8A/wD/AP8A/wD/AP+9////6//O/97//////3T/AP8A/wD/AP8A/1z////k//f/4v+4/+j//////7X////V/7n/8v///+3/Ev8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz//////zj/AP8A/xP/XP/e////9/81/wD/AP8A/wD/AP9c////+//9/43/OP8t/wL/AP8A/wD/AP9W////+P86/wD/AP9d////+/8f/wD/AP8A/wD/W///////fP8M/wD/Jf9B/wD/AP8A/wD/AP8A/wf/6P///yj/AP8A/wD/AP8A/wD/AP9Z/////v9o/wT/Af9H//D///+I/wD/AP8A/wD/AP8E//z///9b/wD/AP8K/1P/Cv8A/wD/AP8A/wD/AP8A/wD/AP8A/7f///+p/wP/Af+a////vv8A/wD/AP8A/wD/AP8A/wD/AP8c//////80/wD/AP8A/wD/AP8A/wD/AP8A/1H/6v+7/wX/AP8A/wD/AP8c////+///////Q/8A/wD/AP8A/1z////I/wD/AP8A/wD/AP8A/yT/Kv8A/wD/AP+I////5P8A/wD/AP8A/wD/XP//////qv8F/wD/Ef/p//////99/wD/AP8s//3///9k/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////OP8A/wD/AP8A/xP/4P///8j/AP8A/wD/AP8A/1z//////37/AP8A/wD/AP8A/wD/AP8A/7n///+b/wD/AP8A/wD/1////2v/AP8A/wD/AP+7////vf8A/wD/AP8A/wD/AP8A/wD/AP8A/0j/3v//////////////PP8A/wD/AP8A/7v///+1/wD/AP8A/wD/f////+3/Av8A/wD/AP8A/wD/8P///5r/B/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/uP///3r/AP8A/3b///+4/wD/AP8A/wD/AP8A/wD/AP8A/xz//////zT/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz////e/7L////Z/wf/AP8A/wD/XP///8j/AP8A/wD/AP8A/wD/AP8A/wj/Gv8g/1j//////wf/AP8A/wD/AP9c//////8p/wD/AP8A/6P////x/wf/AP8A/wD/0P///4j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c//////84/wD/AP8A/wD/AP9k//////8p/wD/AP8A/wD/XP//////E/8A/wD/AP8A/wD/AP8A/wD/6////8//qP+o/6j/qP/o////iv8A/wD/AP8A/+z///94/wD/AP8A/wD/AP8A/wD/AP8A/wD/df/F///////G/7z/vP8s/wD/AP8A/wD/7f///3X/AP8A/wD/AP9A//////8k/wD/AP8A/wD/AP96///////o/4X/H/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9l////5P8Z/0L/7////03/AP8A/wD/AP8A/wD/AP8A/wD/HP//////NP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP///+7/Hf/z////f/8A/wD/AP9c////yP8A/wD/AP8A/wD/Af9u/9T//v//////////////DP8A/wD/AP8A/1z////7/wL/AP8A/wD/kP///9D/AP8A/wD/AP+8////kP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz//////zj/AP8A/wD/AP8A/xz//////2H/AP8A/wD/AP9c////8v8A/wD/AP8A/wD/AP8A/wD/AP/7//////////////////////+Q/wD/AP8A/wD/+////2L/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/yT//////yj/AP8A/wD/AP8A/wD/AP/6////Yv8A/wD/AP8A/yv//////zP/AP8A/wD/AP8A/wD/Vf/W///////9/5n/CP8A/wD/AP8A/wD/AP8A/wD/AP8A/wL/uP///+n////4/2f/AP8A/wD/AP8A/wD/AP8A/wD/AP8c//////80/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c////+f8A/3D////4/yX/AP8A/1z////I/wD/AP8A/wD/AP+K/////v+l/2X/Uf98//////8M/wD/AP8A/wD/XP////H/AP8A/wD/AP+Q////wP8A/wD/AP8A/7z///+Q/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////OP8A/wD/AP8A/wD/Av/9////eP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/+3///9x/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/v////d/8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/JP//////KP8A/wD/AP8A/wD/AP8A/+b///94/wD/AP8A/wD/Qf//////I/8A/wD/AP8A/wD/AP8A/wD/Qf+2//////+P/wD/AP8A/wD/AP8A/wD/AP8A/wD/Ev+q/////////0n/AP8A/wD/Jf9c/1n/AP8A/wD/AP8A/xz///////z//P/8//z//P+R/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz////+/wD/Av/L////u/8A/wD/XP///8j/AP8A/wD/AP8H//b///+M/wD/AP8A/0n//////wz/AP8A/wD/AP9c////8P8A/wD/AP8A/5D////A/wD/AP8A/wD/vP///5D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c//////84/wD/AP8A/wD/AP8D//7///9z/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/uf///7X/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/8P///+6/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8k//////8o/wD/AP8A/wD/AP8A/wD/r////7r/AP8A/wD/AP+C////7v8C/wD/AP8A/wD/AP8A/wD/AP8A/wD/kf///9r/AP8A/wD/AP8A/wD/AP8A/wD/G//d////5//M////4/8n/wD/AP+h////xf8A/wD/AP8A/wD/HP///////////////////5T/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////AP8A/zD//P///1r/AP9b////yP8A/wD/AP8A/xj//////07/AP8A/wD/ev//////DP8A/wD/AP8A/1z////w/wD/AP8A/wD/kP///8D/AP8A/wD/AP+8////kP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz//////zj/AP8A/wD/AP8A/yP//////1r/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP9Q//////95/wT/AP8A/xj/a/8C/wD/AP8A/wD/av//////d/8L/wH/Hf91/wD/AP8A/wD/AP8A/yT//////yj/AP8A/wD/AP8A/wD/AP9I//////9y/wb/Av9K//H///+L/wD/AP8A/wD/AP8C/3H/Gf8A/wD/AP+S////yv8A/wD/AP8A/wD/AP8A/wD/AP+t////4/8b/wz/wv///+j/Lf8P//D///9p/wD/AP8A/wD/AP8c//////80/wD/AP8A/wD/AP8A/wD/AP8A/1X/7v+//wX/AP8A/wD/AP8c//////8A/wD/AP+M////6P8Q/1f////I/wD/AP8A/wD/Av/2////jP8A/wD/N//v//////8M/wD/AP8A/wD/XP////D/AP8A/wD/AP+Q////wP8A/wD/AP8A/7z///+Q/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////OP8A/wD/AP8A/wD/cv//////Hv8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/kv//////9//b/+3//////wT/AP8A/wD/AP8E/7////////3/9///////AP8A/wD/AP8A/wD/JP//////KP8A/wD/AP8A/wD/AP8A/wD/jv//////+v/0///////L/wv/AP8A/wD/AP8A/wT////+/+T/yv/j//////9b/wD/AP8A/wD/AP8A/wD/AP8B//j///91/wD/AP8L/77////t/63////m/wv/AP8A/wD/AP8A/xz//////zT/AP8A/wD/AP8A/wD/AP8A/wD/qv//////Kv8A/wD/AP8A/xz//////wD/AP8A/wr/3////5b/Tf///8j/AP8A/wD/AP8A/5X//////9n/2f/9/6z/+P///wz/AP8A/wD/AP9c////8P8A/wD/AP8A/5D////A/wD/AP8A/wD/vP///5D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c//////84/wD/AP8A/wD/JP/t////uP8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1P/vP/v//r/5//C/3D/Af8A/wD/AP8A/wD/B/95/9L/9v/6/9z/h/8A/wD/AP8A/wD/AP8g//////8s/wD/AP8A/wD/AP8A/wD/AP8A/1X/wP/v//f/1P99/wr/AP8A/wD/AP8A/wD/Af+D/87/7P/8/+3/uv9L/wD/AP8A/wD/AP8A/wD/AP8A/wf//////3L/AP8A/wD/Cf+6/////////1T/AP8A/wD/AP8A/wD/HP//////NP8A/wD/AP8A/wD/AP8A/wD/AP9W//L/w/8G/wD/AP8A/wD/HP//////AP8A/wD/AP9J/////f95////yP8A/wD/AP8A/wD/Cf+L/+D/+f/c/3z/BP+7////DP8A/wD/AP8A/1z////w/wD/AP8A/wD/kP///8D/AP8A/wD/AP+8////kP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz//////zj/AP8D/yT/dP/u////8P8o/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wb//f///3X/AP8B/wf/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP/P////4P8j/wD/AP8T/5L///////j/P/8A/wD/AP8A/wD/AP8c//////80/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c//////8A/wD/AP8A/wD/qP////P////I/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////+f/4//7/////////3/81/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP+v///////m//D/WP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/z//9/////v/1f/V//z////2/8z////1/0X/AP8A/wD/AP8A/xz//////zT/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xz//////wD/AP8A/wD/AP8X/+7//////8j/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8c////////////9v/i/7D/Yv8I/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/xX/o//o//f/1v86/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8s/6T/5P/7//P/zv+H/x7/Bf+t////+P9L/wD/AP8A/wD/HP//////NP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/HP//////AP8A/wD/AP8A/wD/Zf//////yP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/Pv/v/8H/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9x/////P8I/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/w7/eP9N/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/XP////D/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/1z////w/wD/AP8A/wD/AP9c////8P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wA="), "format": "LumAlpha8", "height": 256, "mipmaps": false, "width": 256 } [sub_resource type="FontFile" id="FontFile_5froe"] data = PackedByteArray("d09GMgABAAAAALU4ABEAAAABjRQAALTXAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGi4bEByFbgZgAIcICIEmCY80EQwKhYN0hLMRC45WAAE2AiQDnRoEIAWHdge+AwyCGFsvXHEH67bdn6iE9GZV9QMc1odOBdvVg57b1HE2VeYCBcd1twMk7aQnBv/////ZyUIOvftw/0kEUNrVtBtkucwI5CiKKmQEnH0oCI7cFJBkSMrsGHNU8TQv69y2hp2DJxa5s9MybuujjBgMZ9k7HyQj43QedpzBD0FPOAo/vINq5SyJq9JeSt8Ex8+bAkGjtePF9Wmrg0X98IsPFv+yICXeLFZ327DfaiLHW6YgJzkLLksyBdm5xuZLaTgwssPJSdxOLB94Dqd14J0VxpgknNbXXqxnD69jIGH8jhfy1FBMyfyTqXSlTQnX3EX04Da1zZwbSg8HTDLKvAbVmj4nq8sNxbr/rXC3Pj30BO9d2G5KfVkGxi6DEm3EinNe+Jf6+/hfSXZS50HAl6d/KI8kzAR038oBr5t/byR5pGka4SlGCDsNNmCYow0pPkII8IiYH4FCmsb81B2GEREXIo1UcVDMp7gojXTlIz+OGTemFBAXXdQizlRxfOrnI2z5mvVAA2g02r9+huSQw3GSdj2/89J34myJa4PkonAVZpdueHF2/6dWVj/wIwIIIIBQiAioBBKpKkt1dld2k6xVlSuuKjldPeScp/qRhscdw+IaHsfwuIY3QpmremixTyjzzjBv+tzsXxosWBJ86RIewRM0acBD8CAttewU6IqyJna47enLrd3vN7+tnb743HXuOgB6mz/zxsy7c+68m/vn5yTnJrfkNYy8kM0ZIe/OH3knLy9UbuQ3L1EqaWKdsczNOcMvaeB1pbgACAEF5kYrVAgt0+LFfLspKGP7MqhRAvQsuX4vchEGMghorizQ8QaADzfPLNyIvPAnhP/W2ApZITswz/7aJvP0c8MRs6UUW+V8kQIQGIC51cybSMaIiBG5SBbF6LFIcmzAiB4RrXQJmIAFBtp/p14Y+WA2KHOWUsbkhPEX6i4kBv5XNQVAtUK5NLqA/qZcSoMKaOnxWgFsJwGvUpluSqnccg/nTPtnqi7TrWnD3scbk9WtuDHF3h/meCeshPBILdb6d9kuxy2OwUSx1Cca/0kr4Imjd/P/ZzYJkJldlpSn/v3+PiLYKRUKtBzxksxPwn0K7VPXyDX39622PPYEdg0hWLUEnfTFEwYPc2aS3SPJLjJKmmy8yweUAjqZ1/fge/wneC1gJUu2S+HyoPncP+Wqle8S2MyWWUIM+/t/OpvWvL0+3k2px7SyUeCACAT5tXG7BAgYeGizMyBC2r5Mg+isy865/1+pltdqAC224gIbod21zXk2n+EsjZ/vFCU5xYj+XYUCupGYJADc0AQli6I2gE1KJgEFVDXgR4DkBIVN6eQQAgjSGgiTSIDSUmkCOFGaTSFPzEfno9fnm2ac8tWH22Wf7/no/7OclVYhtaeFYR72zB1BtHiPM0iOO8g3Crr+r1JXVXcL9SEstQQGyXiEGLxCwjNCc3Uj5j2EHch4D8Z73dkEoWBu9sR7HtHshBtEu0G4Qbb+l2raYv/7ewFLXIQk51Cp6A4dy3V3VckD/w5O2P1KAJ0oK4XKrcujKtAVzpGtm9K94Km/VP/c/pe+/IEjGkvQrpU9O5GMh1sdj1rAgyEAzqXr2/ZFD+RRZRLJ9ATF1irPbZJLDokxdWsE1KSnfymGaihCaQ1l1uGgVXhcNq7jnNC/2yq1ErmOXWOGEqJiGatUVRi6k2+fbI+HraoQgSSWXQaRXOgcr3nu/oztc276oy4NSkEe8Ui527Gf1p+3uK2piRg0YokNlBlmJP6/coyphTRqOvbbKY5MByrzuAP9+p4f/zkN+/WGpWJEVERFRVXFuP0xADaGu0IODACPBUHjd9kcyb7IXhyVTmxjHFayfBseVm6A8NFYZhqQJXecPAwI4IHmXNaHEHrlYBaCFOhpMMCAgLcA2BRmQOLNnPjeRcCBCR7AAQTeAU9mMhuf2ygDWPaOfRtQ4OHFLPj4Inh432Q7sH/5mstOHQKOD1u+8yA4pT6GOefB8vgy16ATfErQ/7mbUpGfWxS48zmiwe/qk0lz7oEik+YBVpZAbHBAAQtacQjgNUAAacbeMoENs0hhLbv5kT94aigyz/JyNEKctz8Da4qdsSt2xx4QYICcYe4ywmjy02YeJGF8znZ20MRj3DxlDF7AK0Rji63KDthB7/E78WHMBwUEa47DAE0PAkxsRfyLgxe+laMwBza2KepiS9TH1miIz8IS22JzjfKK4NWxDb7qeeSNeNA6loEMJWOPlLK+I1GafBEQcIo2g1ex9Exm4c1s+DEffBl+SWxTbHvyuYBj22OHBVggCOBIiAkty+TghbQ4vzoGcIDElyBCvQQMssj1Yr9eEY2gzR47HLkeoym47ykLDP/T1TAA0prRp9/fucQAeht/CvtbLCZG4CiK/wH67HQIuAkA+wNwxBlLdfA+Af2v/CfJ5qXR1bsDsg8A0ekA2F0dAcyYDhxbe2S/yHqYkAlFFutw0Wcii7Iy+9hqbacdMLuN2bT3Hu+n/Ir3+G/+MhAMwzywd7Dg9sjjzrQl9saBOByd8VNcj1t6G89Vnor0kbZou5p0Vuf0k/rSa1fE7zTqFjVBXaOe+/3D73/+l8C3AraBFKRhCKYgDwuwBBtwBTTDWtgOa2CjjyxwK5/DB320L+RL+LbQaFAFbRa0Je2mKZqlZbqU/iR47W98qqff9DQAu5OFnP38SL+FWrRpTWc7bb/9YGM24bPrES4Ld+rAuln43GGNQ45vYsABOHesfBWrVFu1Qzs35JyC+ncFv1Opq1QpNUm5qBd+/9w+KgRsBZ0Q3oODeaFcONRUgzhuvIWgEwBN6tMjkTM7e8RdEiB5+Jjdwlm6KGsjoJpZ86ybm2dl+7ZHu7cLi1t31Y/3qm/1Ex44V739e8pz46wFVtY5Z7UXmJypjd7cPz2mfr2h7xa4d/292SPn79UAbKVHuPe8/Q9v94/PePPizvPiBiuWjGhR548ylr+4PQu2/sMUGoKLN1bo0MJCAYMx/Jvh/uHeX/8JELle8G9j4FP9KJ9pn2TBKxjx2xOzh8+Ep+K/EiqMf/EkWvkrvuRZtPEc28dLbL/xoQNczG6wVvvS2myf7Y/2+MoOWbt9ZR32tf/TP7Vx+ztskRmHo8J9o9FjPNaLvaRIvFGmw1DFdrL3BLza7SRbnqjdAM6bpLz8NoO/vrtPZ3VfTttsw5uFf4HBn+YYdg6xO3ku6+USfZzAapF09Uhg49FwRVleWlyYH8zN9nvdjtxuNRv1WlWqiOVSsZDPZTNCOpVMOCyuXrlwvh2cg7P+mWOHWm6zUa9WyqXf+TFZpEaiE/BWGIOEeHD1CpfHkr7deWkJoUEec9yQa8NgMBWGmJDxtWMgfsMMgmhqwybqhIG6/Al5spYXJgb/3sDH9OWsstDX99Ec962Trbn2INSbfd+pKAdKezklhh8kVpr+dyDNqpUe9NYr0EmySvXOV8VUvABbGb3wjPY6uyAfXaKEKL8eNO6W8RrOHAmy7/m+7m0YYgP6S1pJj/eHYqGHxRY+2sOUyQ7Prux5M3bJirlcW4O1+LVCJ36jsHWktV9h6zJehL6MfPJP7elyHa/cxiReFr46mttuSLT2G4ViPnCB2/+QGtj/jBijgjvDd0nIekjnlP8u0QshNNaqCA9t6o7Hb+cr255dsFmtZrek4UhmFNL+6Vx2PAzfaHRNQh9pCBLDuQgPzr6a9LItypO4I5cf+Rz8B57fIh3RzDIDwWIPC4Zu8X041u+MBdHersF0Vl0RL+J9Q8T1yxqZIZizN+/w4pyU8okY8MuAaF5ZzAXDVFyOUOzEmK4gj9+3UBhcNd7/+WAPtPjD65rFwdHxjj7xfBsLzAxlDHPtcwv9rXyDT9H9DHS3DvCHUKvXxa2VIE2X+EFyDNMV3uvAwWUwwmSBm0X/pl3EjuuU2Y3rzubY7K2jvlW3weuwhYeg62SQr0uO5hUtod8YD/WQmNXeHTUZXzEuLeJAFhDpBSq7S26/RXaHe9/eJneI7lMlHOkpdNp8cGptA88Ybw252eDK81FopLEGta7xoqGki38YBO8cjZnXWFDRPESzL9UDieDM+d4tF0h+Frpe1475AEtBaWKVeY5eUG7D5+FNGbpPsjVDipZk3WwBPNg6LdfD1CNlvPhHvcblep8cxtPcdd6vSr2BUCDUjjirU/V8rQfEu2JjHpeAOXUJ3MJgCXEsZr2LLOhxk+pj3t/DFayDhsSKxYzSEWBCYwtQ0PTCZcMLowI03rWQ+NFCb4NawfByTJ4zL+yeDlIdGfKuk9sSRPN2FQ+GToIsGCLx5YZ40PJyAgT2DISxchN2dZsJEdRB8ohbGYZr0nLqJ0yQV+LPvU+2bD9AIhotdI3ejBFpBnR71l2m2/PxDLsuIXx7QX3DKOuZrs7OzVhdwgUgiNV9/S3xWuCNOaDSQ+3tihWklIt6/MnqmJK5rdIOAsySuyK1zyYMyrmbgsngXEKKCt8FGYHuNFHJi5IoixqrMy+jU+obHPsTJVBGcqRG67RN0szcypimmcf1CGkaCa34vkUZyosv1Tol+EXqaqLbefWKPJZAVF5iydc8puMznVijg26dHEEW1D0UKTwjyNIpGWWFGlZgvYtV6M5PPM8+nyiQooX+EfqMTLHYm8mAhJNfKR9c5Cd+jrvvsKK+fLlH6/4b2jgggAIB7OmGBgDROaSmNFT77D8DB38DwNlvgG0/DmDTq8DTLwBo5n5CJ58ffYw5P3ITRH2CASwPBwkFAbNnshKRjFKJRwjpbXYkAuRDOFnQNdMNjKZltemFxzcNmid+NgQMCrBJoyMakehTdovsk815Ue5mQTbyGQAEpM2eR1OYfeaXABwYbLuIBy6LGk8CdEuy2CCXhOhDRBHmySUeSaLA0pAslpLGkcMeyTcXmqmYaJfo02MqloYGaLEREGbM0YQPpTK3nSxtteRA9klh6VPXL9ZsQYqMMbnNZTtKhgt/bi3O4jSQKWuiQVqM+OwKmceQTIh0EHLLroRFdF4SYFYdzkxI83RPToZgbvyRu4GXH+YO8+G00/zpz9/FdvFr0zFHjUzNzBg36wXSnTCIVrpo2KtXguwUQgsmEKDrnbqtGhezyaCsTMSr/vPOdSKTMPwCWNukAVDpIHbjBtsg3pFj8qtG5NES52fTTDC0dB1NoiH1lzlhR/IbHZmw7SsK0SuA0nXVMzdiXUcGULn2U4dBAeYVsUHROZPvO5+rdHNs4IwVkQ4KAcuedAd3u509e1vVARaoBbTP4r2HoiEAROSCzLKeOMcx7ri+jGAOV+E199gDhjzJxXK7L4uMEqESmuG216EgojNRIZV5hEnV4KRpOa9b7Xs7NzBj7ArO7mUaHORuqjK641elRadjdruMAxmLVUkvdmYah6vk5Y4gcZUOINdXV7RxDiQRKKVGJqaGWhPv4ZW3Ci5ARbM1pnMcrhNntOBZchoBNyRfMB8DvpZRBndxgvEncmz239vvq9+Hy38ZzDYwVyCZpKyK4lvsH9PwEbZQauzSMFhAhwUH53MuzmEMo4yus9KHcKw5j+04lhKs60K4OJ8gj0DoY8eAWuoUh53Dfslnz5JTKdHNpnXkyHXmceDeD4PK5E883l2dl/s0rWtOd5femwvO+np4MWFADaOZArrcqWw82XWPhcTXJEh7ifW7C8rMurA8DUGzSHBC+/Y8vgwM6AGNaTGasb4rDzgMN5kRSa4FENYKKyOduE4oLCBDJcMqrCK5qVkUOtp8nk9FSo17Ck/fWei+zX6R1MM05cnOyprE1etWYymSVLy8g3mpCaa/sPCPyBwWJ/dhflBFdVy+odbkvzinD+TApSOkkurs2cYJanKKTHGV4uA4OhiGo0E8PkoORyuNxmFlQCs6Lgv0/XFjtJAAAhKdXxvHIvM955ne3xieue8ZCzeL2gV8RYt4wf0eLRqp5Uag4eS+6wjBmspX3cfeBGGuVp7k6wT5ks0bYkkkRU4XhVusnQKorS9lV3U3alTiQeiz8dRCd/Bj2/P2GSlBjPNBqcDbbmuwSLK+dZyArS9sXQxgLSenV5VrLGrUlKU4l6qo3uQKcHsNjdSmEzeNIM7jsmpEpX3KaHMK0n5iwTyUAHb5PXwXDrzUpPv2Z/owQ4Gbt0QlJvvGd25jCuvFKwGe3VvlKMmAi6VgKCh1aF6UA8Z+fFZj8cYRm2ECxON6y/e8zrOaxnPlKPkb/456zqg7IDHp2ehTyfJSDMRcVolrltkhl5KrRra0rfMeaRG8J1CVYQ3PPZmYy2bhrYF0+N2zei2ArrIfFw2NXRXrljIF7ZC+zqJB+P5B4Gghr+uSsGs9tl1M/BgthJ1o+K9m+Aq0cZpr1Gfh4ntoYGGeFSzcsRBk9BCZPoplL3lFvoLWorulqBpaH8voxIqiAuzU8eoCs03M3wSygQkK9/W2PBBBd54Tmw0Op+4k9p7qM8FRAssY9xwij6HIKu+UmSG+xj5oaeX6DestunArN7z8RXhNGaeVWdXYHaKzzyXLP6NXrvX+PqceGIB8hX58X58t9erMnK7MUvKz1y55menvkV//kvaWFgM6NZJSFhnRaFI9dZspjUl7GEidnEbzvXIKAvTcxQ4kUdq96ztLKQyXOu5SL9gt13EhCatcL+RGZuHzR7y0tWrQvclNgH/oUnIYK7mmzCnFVCoXXw+EOyWAO3imJF3orJFHodSV44SpE9QtahMuLVgzbosRCL+Jfmd/uFnE0wemV/2zL3YSXFgXxJK5eIblElTnkPc1JNuTFkni5cvbyedR2OY9b2bjzv7KAvXF2TE71fNQIdEPjUoKrvgcMexdgJlfMoC0UV7bJCZNTdHRjDdh+Ymso7RVVJFN1EfnHe4AXTFCXi91TwlTlpRQS0Mq4RWnsEvKj+FuX/inLTVqwPWK8B86DcsQ+l/q3g7gJ5p1fyJMEgYD2g02SmoTu0r8xhN3TMZXKskNTHnd36+L9KCY6Lln2RVTc+WzZhXRKaszMxyb5amez7Rvrv7nbVs/mLjlH/EqJEHPc8941y6KgHaWcmH5UGrSXEClWkZCnQR/kQ5TVWBDWAGsr41E4e5uLZKt/GR8ExtzgLzhHUiMG3UPp9oEAhsuFmWZmV5ejAUiAYOeAAMmgDQYW9hbeYUIOXfangLiJDKepWbDiiB0DK4FV5IaEMewa8VLBs1tnVCjkR/CZyzcIqUFqdiDFLYbAoJobq3EACtMGmmqPwB1fZIwUUj4GK8C+Z2ER+AuHBzFRbqPCa60NTPI06q9WM8rwxLGv6ELINmBKDNOmwnQ6tk0XUSzk3B+/DkWchJIPrLGHeZiSCNS1DbuF7flt+5hX0IpDVyq1eieYQDnXax9ZMruYxGawwL0CY+GV3YXX43/zfBBiI09Y+A/pUXutS4h2VLuR0NLppJQ2P5LO/Wtlp+m8gUf5aWd/PF/Mk6hEg4r84y8JPnKH2F8zP401Ov27bN4MU9ODj7rc6m90jLTEGExv43GHvnUnqMHJP0a2ciMRzW05RGyV0cbD2RMwNnG7/5inpxYI5jFi7n3Qd7lXlkvFGp9+LpeyaXUJY2olzDwGRQkE8G/1+JeAOr8cxCZwT5ytMglv3CsjlByHgvwL7fJ2c5DLQWGGn5ZgpyXDI1JnBSQ4GTroO3wxbXcVnGqh0wpEXs/2oMB3HTE/nfGQJStcZavx7lc/euVvlKhlv/khdLlC1QcReq8HL7kMGdKHxOVSH6IMCpvYGCgTkkyaChOwhHkP/MJGRks8gS72dvrApEkksWjtFqVBWadVeWcwzjx2E2iA2VBkih0yWvFbU7xC+hYzui1jUlc+9zWwBLy10PpUk3KhpxzBHst00LgOaIRewb4dflClUEdhuRyYFNWU5jlTopniBd72IqAr/EaABjVpKtdCLjO1jL4hcgu+iOFQaxdoktm3/AKDNTaYC5SOlYKj7+HeFSrT+iC4NftHgPf0GIY4M0JwiRP+543IZ9sHV4KXB08IiZbhOMUlTHsp5jCWpWCcjcrCQLe4lbdCZEBE7idcmvG/Ml/+6O4hodSFim1uD2hIWMlL4tXwE7CpZYBRjsUVEvoZDyrcOhG+I4WjL/PCKQRpFQEzpqHa3RUPLfgZMpOG+rRZx26HRL1RWEKU+uf0DoePTFldeNOygLopdBDIV/FL+ozruYYgI5PlscG7HWwRsO8k8LWuSLDkTFLAtliRGAMGAtGW8ysTsVtbikKmVeZjlRCFw1cgdpJ7xlfmEbU5C4JAQ79klHQnEGa5eb/mksb4AMoJcCds1yFpzzR75IUdXnhzQ4wen287q0HVOQAKlpmFX/aug0vLR1unAUduw3MFjwozJFDohBBDu2UGEQXldibbpHfWJPFc/8yf6+Z5/ADZhhA2XBAXZZxEGRz3WxT4f6Ld3nUfSjFrRO2zrCV84VUX0jiCsOzOGICmb5UD8AX+mLl4WQ5faKJQKY499UHL4x1fSqUTNDC7REpvMlccwu7nsbdFfgCXBMwudwH+gASF+DR8Z1K8EaNHvSJMmi9jcBcjSKf1/JfM06gK+islXDc9qFonzQmAVwzPB8QosI34coQGw+cp1u+RxFWiX2WHB2CWCjpzBZZY7FHWsMVL4e9El4A0RotSUZdRvbWMG0UFx/yMO5V9IQVBlIsuQviAq4PE1Vv6sF5C5d0gFcaWW7YpYHnG7mNnSHJ7Z9rbePuyNXM3zzH1+GDTmv28O2vo8DdQEE2gU/TMof3nYo7J1qRo9Fpl8+ymW74d5nf/fjS/9OEH3kd2Q675PQUSflCWP/8vrnnsXtu7t7T913y7mp2JNNvVmKliuzOe0s9XwkYaFHjSInPE/L1o06r5m30BLIumytWpH5zWi3jj+8RqZOzV3PwaXZx/Qko6UdpJgdZ2ASRLTryDmi77gDpMpl29/PTudgRuPct1fQejM426KKtE589LOqGHWoeMY39z5Z69bSPetgJBW/5vML0gXygGYieFOVmDZMSQUm9dOk7+UN2Rpj38FbNDt9edmYYQKc7icbkn6Nb5KfkdBV53OSRbbH2qHTi3GN3xctTfeaS54cHvGpMArCKgZtgNp1VFLf/6mI65BV2xVr7YhSN8dntU1s/6DTOXMYf7sgjK69uYLpyUvvNpZjQfq1ahKC6UFmHOzXzfZkCPUFbZtRQ8oUDbtfpAw6rp5pxbqRX6nKRqFmI063f55Zts6i8k7FMPtgVlmyEGPiJRVLZkTIr1aa1NabdVfqCz8U+nzYTek6X4FDFXHpteogLQts34XSRo26NNcRwc5gYKoJOL89NnWfzPlyphbbAqh17hxwaDe5RR2/dpT0CMzkbQyWCyeqzMWEAqrPa1OnGYU+1qHB+LmATQ0dreNk4TX0xDqGr/aw7FqDHkFiHVVdP+E09xgBkRoCEUQixvKyxbZEQRlBwrD9Vqg6qM/VyaSJ+UNCR7HmSiA/j4ePMshylc6cc4OGIB8fnx3A0pHHkJqcY74/cjTQveSv3sly8IbvxzvTezVOJm8dnBYp0z/ERksUFpyOV918rwyrpAv1tOoTsO7gpXxx+evmHAoQvgi7a8shnuPpUBlK5qbB4CWvryhSovAJeqbjnglOvjvwqVBs27Y3AwQamfDzwjNj39odbtgQnkgUACrnLDLRECM6l24i9KsfJ+bTGztEZOfqTdBrsQXZ3ooUVeVt//d4+RR0UXJnOxZWEJtecAN7gYzMlT/3Xr6zrHud/C6qu5MVF0kUkFL4NeOg4zA4/EKN/Rg5iUtiTUkzs/veSsnRTkn46RMmwD7176wC8sGdfozNCc0WYKkowCejEL+8sAC/5SBUEvdnA3SU4ve/H5bK5b/4JFzOSFB/S8cmPBnFCaBKNdhzdMiYFH57kwhXcvcJ0xadLWs1nJy79ZGUopMk6D13xGp18PyYnIR3IgVGWG7DUtwkma/iMuJqeFbussQDnYijzIT6Mf70Cp0j0eETF4Z/RzY3Pwd/Mywj/eu7kAwsg3rNnpdHCkgie047u66z+RUeezHn590ru2EH/+iDQL6hNiVPQMUAgaBTh2lkAWgzSFtVFA3uSZnMpOezpdTbTVBTHuTU5jDqVGTTp3YE2VffCHzUvynh7xt+I30gAMursul1vPSV9hqgfx3SdpSEKGpFJIVLYM/gkWc3bfMDrix/swsHtEpvXVe/Qq+pana5XF8PRlIvxEbsAypfgR3ZxQleOYLn1tdp3gsZEgLRFB9Ct9NM1nxhc2ANv+XYCMeI0QVyGCHkH4dcf/hQY8SRDA23y75bqS6cCUQGOlXrnFbBN13KkbedMNdeF06CzzpUCt+9vQhxIpSYKdbayXIQU92fed0jxImDLkOJCJ4nTLoI7D801ZOO5HZSQ1f5bCRlL+4VP22hk8AZ50TEKB6J4h4fDNsesZQlnzFXr+KK1NznnygY4anZOtLZxyjHTA1byaHlb29AHrL99c/bJJY8mhvGZmYnr1R/Rs0bIE4NPN9w3D6sS83qrKlamJiXPzMPJ8mVM6ckbr88r4yHXI82Qm6ipOmCOfZghDU24AhXqbTCrvOjynC3s6AFFzqOrsbbunYo9Kz2wNCmnO6jBLdkyJWWnb/dDNjK+Bl+zHfn5f0AT0mRYgm+RFDDVZ/bBlwTDKZzIzd5S/yE5Jk4KQYUDV6smu20CxZIPPPPIXVKIbJEKeoFVl70NhvWlQZ2EINTjEdKlPcSCZG2tsz0nZ1rfhoS/fTC95Oc7mXHjEPyIye9tZ42Pw2Eg/vcQ+NhTEd271PXT1wgGPj879CqIG6fy2zXje9e4dbUc5k5kcIKfF2mv0ngjPz0OPaPbGt/VJ0YoGFvuEd7pYLys4Mt+X7JQiVo3EAH05D8rCYtYX3YkwHjL3yo8hyEwLy+JR94/XSNxVUFV3l6i2gNtTH6EJ3HxDxa3IMUN3RBSTD8LGKaxSI1unGWWoJI7RyqmdJH9KSYuMGbxW1y6VgNpO8hw1SXnzv0B+HMuGhyGR0Up4zyK//neWI7yP6wRJ/LCkDtlAXhC0L6TRgkXv/AdMHkJ/O9Ub+rx1nd/aozFyFICDIV1KsaZhbHyY/duVWSGUtUHZK9A1a00wFlBqB3cqvacVhWu6wuwiT1ReTV+eVMOS1vyxt+aOE4nRS8pK0l1KnzT9xLve6bAj7Q6ZqLHCI9VwbcvxuGcHnfZbDbFzb8Kpx9v8YQkzC36Nx45vwhw17bOaIIXOp/C27DRWOCToAK9iGGoROBaXwhS4eUL10jx/D4iXLL4l8q+rbfhHXQAc9dE9gLAy1qB8m2OPd8Kd9/ea3Z4oHk2ul18sE0Dymu7It+CVj7ePsdo6FNI6xGrgQjaOTljo+jwVN7Tu/ojYqTbfXZFz1XmjyuJwwD+3dVVJ7705gjIiaWhLOxH7hnwMu2eHAl591b6SVst3fTSkha/Xre5DF6zBOeDCFy3TgoUPqI7UhN2zYJuPQ7uMs16lFO1nHJLSkRrFaaPA/VnuA/mTP+VgHb/AsidGvNqffWh3XU9bYnqDZaOw5s8IQq7vKH89wagP7tYWNnUEt6/4L4g/UMeLvzLvZaJwwihdjNJ+0eCa4qcIC8cL3BhdABsuJIZm5rMjUoSdyXU7sz0/q+4nZxj4lUib3RVu6bdtbpXNcoN5J4EZcGL2LTFwh3pmFk9qfSNllB05w35AKTyHJzqCATosUtA/hLhUuAUPHCDMlZs7eoAJen0ChcUwN8hcH1Z8oa9tdN6RH+h0G2GwVwbl1vqad+FPfwNQErSDtfLdasIAHZXpnlVULko9UipzpQMaxV2fmJhQeIsYHngNLNHlzgGxVvXsTCzqolGagAXiEZJ8E/GYb49i9IS80De6mUuO/DqMB0V5q6C3RcLvPJopk/qi2RnaIc6OR0+wGMcbllJuwa0ZsV4BbswY/K7tLztRVqpO/i/Q/06cXRIDucwDBmSpZ8I6vIDG9GLQwdHZ09V2dUnfQQyPk+r/fpOnm+8B+JIgQF1O4vgqgXtZrhmmTQM6NMjPHU95flyac0Y6OSghF7sU79AX9Qo6qHuB4D3AyfWPO5UfhjfgSXvnNdkl/ttnE6vJwuIozfcSXG+gIspJlk4oIfIqnhUYGa4CVB7GmZyo8IvztPVTlqbCujki9ysypWmix0kdT+ULry88OJFvQ6h0yh+Ir0oOlMyVaZGJm/YLaOiUMOmWHjLdAh0X0H+Bulz67naQCgR353anRqDeXPSusEuHF5T6MBJHFtR/taGYRkcPGookb4XBLoIWwkh78Mv2I38XSNBZyxmFPgk7MX+7WrW2B0/LshECaGBdBF37HVOjM/sxS+7FVhK/nAa0Rrk+ffrCZbUxV5W0fDQhfEqkR6cZYji6bl4BcV/Hr7OkJHaU6XDhfIDWWTaBxXUH4ed4YgQHZxO3TfjoCkuSxi8XUhlNkq8L/Y/4m5UfvjxIA5eKJrtGvi0Zudz9gY6jplmf98oc7+W9MvPaOz7GXlHSBjN+xFjOOTGwbqwpaD26pJlBW/CqzbTS4SqSab7hT+6BJ48hxaL9PmyfxIbSY/jSHT+QD2hysYEPAaNI6HReCoKTSTZaq/ghVXyUAu7FuRWX/L2NKCthZP62gDdlMCZ9P2pZfdmB9YKkIY6ArpFOJfb2v9NKB1z0rQl57KXeiegq0lLv0hKDG7a2j5Rug94WmA0cK8VF5/2sbWLYTSpapq80xizVKTeSfXIBHTSsKGMgFIhB8eYBTWz/fVVWR8XbeFlcZBmJFRLBeQmNmgXC1knavs0DKMPKg/KsNVnW1MaxbUn3B5WLSpN2kUiliht37rhnIOq/TfYmpMvU5qd9QQGDK9bJ35key61B3lEB8HEp4KDIpOQ0FAChhRNPhBCuRzvn5cRCyfJ8JzKKPl7FPJ09YOYJEQ7ov1zc1frVH12aOnbYucIAjkOfOVSj72jTdwMnvPKi/IUUX1AtmpjrP/bn9HK5UDhbHbTbFov5sPVFWYqpXsGXVcwzq0gRVE4nl0zk9qDnri6xiyk9c2icQVz3KLkjMKxBQEGf/mAthXe+Q9ZNVtd9n+q4AUy+xwRb5Nr8PJZbVsmtgOb1ZaV1yHe9RH+FmkxpatkenjIZbA2STwJTYzB4ggYLI6MQuOpGAyOouuyj+fbh9/ig+I54I3rsR9Cbq/+xoYUXT3+e/0XeRkilvW7vj5mj7VgzP6y5KhdNtSEljoKdx+Yz6nCJ81NVmd5lb+0Pcsarn9UK52L4coksSHd6gJyXmek4Imt/P31cocHag7dV1/67xqEl4ZYCKrKutHcwGboDC57SWQP8qPa/Lqz41Vpt/Ga2OInRwM3kKvxru9cI/nptA5JEVw6MIkBbf7q10Fd1xjX2Nco6hQ5826/e7w+6CxlDNBAy48Rp4KBDWRvEh1Pf4OR016nm9+2dLwuff9wVUYquTm8gsHPN8Br6mZRhMfPkqfi65IUefwxTvWsiRZvm/CXEkotaTnmkLKx+WPxALDh78WHeTEmP68jE15nP8+qi8bnQ7DJfjXYIV0ADLZ/M/+jkv8jWAhLmlr4PlC6Q5PYYFvf/+QI5eruGdlMb1T5yGuTEgvJXNDh332knuaihtDcwsynAkpRUpLSZZRySiX4poZnJofuYW9Fg3Gd0j04qUR6FscLnoKadRpzLXkz7qAb+er4P6OnVnEctkg+vfl9aR+w6e9Fh/mxJj+vo+JLXsVm1Ean92U8iEB0RkcgbLrxofwcEywJPbrOpvr0wA9OzrdHcu64wVX/UafHRQaoHY1u2P8tKUjuobEw2q7mmtU22ibB7NuQ3LA/Q/LgZHCC+r2aSaQGD4KMgRBiQsQRsXhMksdb0mamg5+P7Q/WmdinhscmhERjApkUx517+RhDHBGDxlPRaBwZGv9wN8txpxwVQEqhaaAVXczqzCIHehbb534BBkxX2NA6Wgsspb6k6EslDenEROk1KyrNVfp/8ckr8m2eOw4u2n4OLc702vdf3stPf1cRCz2mEGjEi65hehjV2bZhQWadKb4hF16be8sX20cq/34451GecSjXkM+W5d6AivFShOLbJBWbB+JQddFaCgUc9fXocYatazD3x/2DvFRXqbDZqX/e7updHjY0R9oVeNjI7cPdHIGfiOYr5JPLFIwB2mrQvKsVVuz/uNrW78jKdr8L/byR2aCclR3nhX/D86wOAMMx5lIU8d2ZZnteoB5sc6sKDIfqRdNaoU2hGdOA6VNAOpQvml4QmhFSkg7T099Cib7jbzQVlnuzWzI9n7y3p51vtZr/RifwP7hiuh9exJHp7GtjAhjbVY45B6Tj3K7f7o1si3lFs7Jzs0qFeQVOxRmn7EWR7w7VvWCtGTh2A2A0XAAYG7aXT7itrgG2mc59GWouoEzPw3RWUuUH8PP1qgH59Zk9HAdscSuzJY4DwXRnE6thsJUer4qcDZaOsCo/WApUYMgB2cPP3HNwAQjFrzKApJo0RwtvKI4en8NyMjkTFY/Vr3UZF7d/vHhVBjhOuX8cr8gExa3QW/dWvsP0ceUzhJ7mI3Tl0xpU2LDpdl/sB7RFprJVDKpfPh8jVxqQ5c87ogeqeb26g6YD1o0YGTANtLXAsqrsJLTVNbnRWTl9AQMllYZi/x5YzK6y9JGePaz7BZiBRkDWM0ZZN29uik0fejjdl39Y+yIgipotWTfbsByYR3eRLK5/mHfCnR6x6ghP59F8nC0T0bUqvdotYcgGuWPnx7hZky2cAO0Ivq087ZI8oQvsyH2LuyjurGnAIbhdAhDLpRseBv6WexT/E+KVwQwBXw5RxMlv0Im6sQCk76B8v8BvGR0Wv7RDx++598T64OoVDcTG5TJjEchG7Dq9pvIju5d0OvfNZFY7tJlKf9OQIC41f0KW0pPpgL3r8UGQlTDFZvfkqLzALrtjJylXWL7XG7ifdRmpuIP2sKSmztbsWa0kAvpFaQX8T0BNfGoBgOwF+NPmbctmqxt6qtl+c+z/NFvh5mBi1xmJNKKNjDgfkTki5CWtEVEWoWYPW9V1vzH2GzpDQE/e2caq4ezUAbTw7DPMH8A3lzX8ayKhGCBT9eeIRPQuabQpbsTR6bbUg5sfu8aPyd7EFWbGu9WQVZHluQuCw8hYx1veVw/E6MDHkgsfwlrBdgktACZJCLX9owyqEFRZNJgZM0w0Cp5GLmbHFoz3RkL4Gt9JYQzMYDHi5nRN1IkcZbZx7rHMnrTidLOtvUxuKw7I/KEYHfib5MJaSC81+Cmy6QRQZLdwPVVwZWF/NqqcCM97Pbk5O6Zksqc7fkizErKbTxxIRyN/sv6P8hj5gLS0ojHXfRTGslu1tZnhPTA6GVyaQZ38kygFWW3g6F5KW3pSin98xABztquvPybdJqDVzoBExnC4GGU/3ceIwj6+Rl6+zwRhdF6+xsQpSFDfkrne1f8a0yd/SeDr6Zsc74K0MP1Hag6ugXp0hC3dIm44ePHr+Ae4t2IrF++XaYpVzlpvYBRG4F/d5iWL2U8zEMWJeZfXHvFShQJRijRtpzbOOze5Oa1lQYAAKJKQ/uBgPJ2zxbi9tDFXtomA2L6BpevCIYEIeJC/LiIQgkB4ehL8kbrHdNTQrfVLhN2GtmZSHw/Sg4JXGhRXmXFVTqx9fqrJeRznl/0CUOTxL6plFcx6hZlMd+qRzBxB+fPZOSGc9ApPPbWE+bRt7G+6CexiNeeUcr5jYnaREBHrwRXPx1v1JV7bkto8z3akpc2XbZNrc6XRN8TooPCb16fpyH1TNBCJvxfMMTJW+/PZdZHZ+s2Jo8KfIR/CWmlp0rnOEQCVNvBcN1UxsWYDLcXovsHBqJ6XcRUd/Mi4o44qmnjbkzCgwRjJiatvEthKbM95XjTRG326d/ZQnJaWIDXvO2gKCQPqvRoFVNrJijjFQxEln0pNx2Zv9B2mNXaFDFwXRnsbscJ1mlrBfTbM3Pu76kFC48ZmkvalEcfRVZTo50BVtuUpo1LM09q9ovHRJnPcbJtR1jF/Dxa6/165XHaYn9QDPmZH159zy9KGEVkhbb59tqQX2T9Vu1TJ9BBywCJPPbWU6aJ96oXxiZml03qkME9sxX8bFTpBuAyyIahfthOU+6g8gEADtZPbCX/YdFQb90wSViu43QFlGt7gRiJSiCf1zUse5ylIFjmlofCJsxq93f1698WnFIG0UQp/Z/LsQBIRgT1yUpDi34R/CaAwjlI5BXAxXTFIFgHYDnUrAacBS+Hd+4V3nLv34xwciVjVYyaIcd2zwj7uVQYNfO9paDqgg3xfZRMUuHpAg8kJ9xle3Z/Rq/iQvQIE1XZjT5B6j39KDMGnSXctHxBNYxv0DFvBiwDW8w7GK5Cid9lrRF2uAD959P5Mv6IoTcU9wQkRRCQ1ce/SlxEsjpuXR8tdjS6w4SOxCzJZfpcH3ePbtNP0wizkChEeOQEdA75JDVo5RjSzPE/IWiKORPYG6glVMg4mBGNwRCwSR8Ng8GSbgJazp76VpCNFk3NcE8x/7iY5Z7nrS8rzSRPUkMezVUSN6f3DI1mtaSalnKEWwfaWUyMdVrgjZfpZW+tXGY0ZeCdoPsnhwzzt6ENDRlHwSl9jH+DUn0u2Rz8EBgmLE5/J5HEegSkpbEhxwrNUeeI9bkp6ILsg7qmsOM7dX5oX7i9/LpPJ4zQ4I2GcskRXdlmiO7ss+SGjPFnFAsUJab/K/LWbz9rJ2sH6YeeKfS97J3vbduqvn06AvYiyLfZ2cCzCgBAhS7zc0K4B0R6/lv3WaYiXYwvvC3NN1wsYCfRMJJbJTOu0FAXOpTlUWtRlEioXlpP0F0ytV2Uo2yixFg8wy7JaodDZP3XZzdFZLwFepZgAEeKyBYfkmIhIgjo9PRDm6SJ/qij/f2+Etk+5yve9/inGaQW60zHNSHEZFBTRm4kJ6In2yjW8VRLqlnHu88XWx/WfY2nJepBaP0+vMvjmhAvjwvSky8aUy8XxYUfG1YV5lysjjlnn6mv5zGtq6/nHq3nGamthFjU1tTxDDfwoM3daDrf/PQGjJcmiONHI4xEyfxKMLhoZme4FDq3Tq0lLjcldJVLV35Wg4erAioqiUO19rFy4MzUuegy9qBE37vQwqyc79Bn6N7PypkXgaoAus/H8Ir/kKLGeMQ4JxWC8SfD2mAms+s7LpUHHyMiTyeS4smRtNT+pjxIDItBUrKSmJ1+OT+4fSU/rH0xLH+xLcWi3C2vDOqD4wuBgvgCNF/IxGKHQ+GV+Zltmfkc+oe/zophB1LbYpjhklFK2vx0uhsT6eVzLFseHMTIqn46n5KFCaVGtKc8r4zKEbGwemZBjiFYMzZhEQv7R1KacyICeV+a5C8mxG2Elfivxcggvb2U362JryVa2Cqe0KMpHu3KVVUkvY0JcDao2N6TvoYGuul1Dp9qJCpZ5G2ngiFprUbV5xF578lYYxCyOQVA1kOytADBrB+UbLVX2us1f07//kq7+O9J1LP4A4p8+dMZotqS1kypckJ73JWznWWhDWOEwkmZEjKOG0TEAqieNJNp+Eh4m1r1Ww+dw5VmJ6y/Oax6oqGt5W4Bt7juvCiIkhodDp1hCSNBTSrCEqet4T9e46UfpM0ZcBDMjrF6mQy9K73aJPyNwgIIyLVEjjtLiRcpmB7BxxHhGfOSIAsWSMr8eeHn+cKDOyFZCF0oiGLgAFsnxlcZib8YAqwMEUQZKAcR5N725P0U/WYJfVM1k3GScUTVtKCiJj+bnhJ8Hb/v12LchLqohqHESjTzlVPTsuxPpF+DVeJfspmx1KQMaqHLg5Ac9ZE4eGolRiVkYLAKdlWN44ak9B+R6Fr2NsWSUv3Rh1/WORjqOs7dRaBVRkOHEqRp59y9ox6IMNoBg+VWtfIbI79AfmslhC4go36pPa1Dz+qQaQYC7/D+gNmmQ/Kz+Y5ya7sv2t6TehthqZFfUZKU++ta+Mb5Yyy0CEQEyGklyDYoKCI2xF6OHVhreDzyCbOSUScHX8tURYRlYGUaYHRIEF6E8gj2DbNwxJZbi/JKA2BpC9jDg6+c7JDVa7XquBTmqTV423lD0kpmOTGVUWWGH7J2uSXm8dS+M6Pw694N6D7lJacG/0mCxamszzptU/fkRLJGRs+bSUNXT/qPwdXVM3Sskq5SskCzLzRemlixPA6atJoQ3hfXus4PBB0VmBtm/BE66/UMy6o2PdkkpAScqX//OtS+eeUr3JdvRGtLvpZGfptoX9Aa3pZdEv2ZCtuai8pMqSgowZak5SZ2WN+Cd7cGz+cU2nz2QeyhIQJBBUJAO1sdfG/UjO+DhEBR95eeNSyh1pKTWFFK6BbPxKiQQwS1zIsSX5stQAMkhtFLJJfvmxFEZfKQJRS5MDtUlaV47VtlhNdt+87grhxI04CPJ2fXHzbQUBmRRUwTAAr/hyFNFddky4yraBFyGX+cufiDqmJYC8NvtIwtkbk+QFLrBvrYV0kctQpMqzsgfQE6f4IoqikaHRV+BAiqz+jMTSpp4L8dVYGOzZkf8k4F/+trA7gzVD8GRVpXw4frhzfPN8s2Lc9ur5fMOaTsR+YI8p+1wrgijRRVFOAIudiA23nCPJUU0Hdhu2jKuPtdPIUC4Zw4mIDBT/AaMquH7+AN10rU077h3iEve9YpAIMXwlYNMRGeb/+tzt7sJTj0Ky/iALR39PfFecEbH6PPJE1Ynn4iOznCrHXWle63+HeuL1L1kpBkaJ47dy5LRTAea0axVHaOwS4mJnCYKd7oOSPvU6rKaLP2JO1T3WLZGUb18lig9CbqdFq2U+byt9NLY1Iz7WFWr2mT8UzykBRDjpZEIiSz3F4WVaKQwtH2nNWvZN9lnNCEW4IUVKA3NEvNCvLUZIDckNOI+I2vaMGqY+oRZnhJML3odOfMQypQP32z+Z0fIh6CKnLpC7zEY027V2nqaD9eocTJhGJ2RMNA3hBrIzorr16p//7g0DZT2/UVsXVD39+05j9Y7y1rvdb3d65nPMmvojZlF689Z5mhDJ8pbO6aSK5K/QrL+D3kreM/g7MvwgSolidPvKPiu3YqGSgnVAx09Iyg/O+9WZ0PTh9v2aM1Ti/cskRHCntGQ07ri1ar2AXOJ4SHlGTtDGBESyk782fG3LRDhA672Dw4w0f11+/JRYPmjHzyyHybKSYKCqHhh2TPA6GK4O/kcm/8RNtLyGQUpEvJ3UOUVPfdg4JW2rTdUFi/pjJd1E5YBSaSEoylXbbtlO1nv23vXAX6AJf34sXx0lwjddukHzufhvreXBwbmI7tmUkJe7MMihuH3aCuDH0bi+I6Kzf8SubR85RHxstQzEa/qqSixPEKAByPAnmgH23iqjS3//Zic550ojChOIMjEH2I9NX7Pd29g4NoQq/A9O1ODWLl39hSppu+JUfaSVMfQaBZXjrBRl09GTiPVLPdWiztKKLJSLVYsdWixPGhSNdXLwTLYgaoIv+IZTcO9s89b/jwhIT9HwM/LSRSVRhd7ua1IOnfLxMU7LYmeP3IMt197pauBkqQks6C4bAaqRReKTTBzSVwVtTQj2GHNzKwMpFkHKxOyve0pDYQ5ZAt9ae7Bkvg8GUXGVVafOqtDV9Bzw+Nmfp9ZbcGLXJD3zxeZAVfOT7Z811tA428szPr82/KO1SzGeiVMGKfF4AeMfb5D6qQEhQP3PkrL9ir7/YZHrhJUnoidPqtzecmoKUWKxAWTYAIWiyNhMQSSq4Og88c3I7W27dLskxdG/5XEjuDiL2heUFsIIjAOTn+XgOYe8+dPawwMdGuQldUNJhWhq6V03lKuWI2b/n8EECCgD5ZhMFlzXZg6zPX15DkZPw9mGqN6lndmf0YFQQZcPEq61jOMpyV+fU1l7u/Ap9mbj+ST37eUlMgOZ3T81dIZF6eHGkv7equklWG+D3jbaRMKyEpfp9+QvWxRXfzaoT5Hmp7ys6SigIQ6vpPnweyzt+twb9+b+qmRLgY2D36bQhzKLd293S1tIAEG7R5+acHXDHEobity4Qfq63a/abqBATfWz74uogX7/LY8tx63+fdabMICwgMDA+jWlyrM2vwkW95bxR7twrd3N041FtT/SUDtUod+Pko0QT2X1JJFiuqPPW5+8sOJGA30SuzXZ902HlRFfTf5ABvFFPBJlc9xYePVtt/mdEYPhAS9cFXpsLEdyvgh6fxbASdZ8qdi/FsU+0fvUZp624CK2QFdn/TRSKinmnWJKSeWZmKdTxkWJMdFJJaGEUjbt3aKhhIodgTqwzQbuxH8Ppbciq9CuHjB7ANru6rtIy8LBR7+kuussdTV4j6/dfGXWswpG7sxFTYpFObtGxZ/GHE+YOKi7BcD5+8J28+lttkbm2ipeOhpQL/I4ERWZGNOXCq6VNtFjm0tKXqFoHazrvfanss3/nj537P6ZckTuQ8uyE+mXBYsbLSZE+YfO5ezo3eSTOv1283869UbEvEnKqa4DmnTcKYJWemWwPSX4YIwjb9p77Z6t0B4/q6lVnrPRNau+54JqZAGjh7uxjAImrJzkpMYkClpSuqY9p7/fYcM6j117n+sW7LctDzAVuS/Npcn5WNmAhUZnQgJUyIpsONT2vPdwDsB4i6OWIPlCkE8jHaM1J/TzNSGTZU3t5Fyk3LmiDk8t+BEEyneuDUdVaqwWpNXId/IaB4LIvhh7dSP215bKZnfTwy1ENVmaG2kkfO9UKs9YLoHCEF8+OxkTIXeBuZUI/vUth9f+rN/dCjsNZGUO/ECrrCmuhBck1UoukqlLbF03qhx7l9xnG+La29tfWNDLzrFHBwT59PL08tX9SSyLq77SNksuEPWwcdNqj5gwMHX1yr4tmY6a3uT86VhsQTsrvch68ae7dgUL/S8iNpvsbs10XTFgxH53K84aYGCP/ao5K7DX8S390oIGYfSo46OjkWuqIf91smn5qLqOPpglU9jGuIGju45Ijvgjg/csnRui8xeTskxKFK3orXRx7KVpV2py/KGrjtY5IvT2P6fTEFk97x0lxjm8DL1izj027RkdGPVj7x65dOVbxXN2gt1p2sdo/XnPG3yjuFaW81NeL3WB1fECyDvf/yjdiY4D8SOpuWkLO8GGtwVqjkTzGd7GumzX04skbBBzUgPI5JYp2wOi8rE/RlB8bVX7hmarJ444/uLe0lCkt9ItQH1dBl/tP6gcla1mLZrTV9J0xkd44hk2WclWdkIXnX/G9ris8cjIQ56eE3tyKzmWM9Dz3mUwluuDEbHSw4SGufuiOs10cL4ojQBDGM7bdPYcIjhGxH7V10VAMknw0m39mWNAcjMBRHZhfcUddaDAOolOioaYxsiJMWKCuqvjrTqVQ4Utgae8Vokhm/Cidu8E1bbi9SUNdK/3cuWV8kn3hAdPzA8Q2U4ajLb/1ICaS0w+mAML1kb4cKIbHbhI2F0uuDMxbHbFnC2+7707tFEZ+q7iY+6QA/PX5MXm5CZ6playq3D75tlu1pb7ZpPLYX54seNNqilncP3LhGbMZ4tfL6gp7wrqxxbDI9fGIL+lY5AyB3PZ7omYqG6GykKGOq8l8zvHuDKKk2ztabBkIHK4LyMvoB1O3z69e5Vti2GkCY4t13YOfQd4om2yUZDf0hf1bRd1huYQpy/9XWNSZZpWp3I7Or9svmtd3gGPyAQYQkY3HWK6aayx0g6vpDKetDdLTeW7zRjtjKScJovCxsb3wTnZfb6b2apH3wfUg8cxbDj3h/3uz3FvMMciFZHaT1st5P+F6mPdHoPdQLGhbHdMYpRwDbD4j+ATHv7u/tMeyBT/b2srL7Oapb9g0ybHej39/ZZYTjz626Ob6R1hbfhB5GPWoaqOo9dmgNK/VcVBbCEvu3X0s9pxnUUtfDrNxFU70Q9JDsymAEMx8ZlE8MnoOReHosbECEF4Ds+3m/9JMYszpCbDStY9gKeHZea58dsNfgCxF341CNPupFgwFA4pQXPcQtscyqjvseiJJxj+OdDq/iorHDXFNNF+G7PDbhPP8jxmWYT1BVB3hi5/wyj0TfngszYheEL+A2RgKvbI+2IN2yrksWFDxb48u2mL5SyAbO0x5dnx+M3gAfZ7JaWF8wC19VzZW0BvacReYeqin3LTq312G3LkrrEaL52OthWVnmrulexypi5qTjLFris2NQQ4wxG62tDlaUVoXnp3f4nsozkhRdxnAv17PXuTzzbDJPSBa4yCW5IDQ9MlTXuT1HZUOAZnIzx3l31oC397KTLZ32fcqfkhEr7x+2aWx6gozDu07MTiv3KmXKaicMvKnHEm97USgVbc44qPHACTQpm4GASZChvXaU156lyAfpuaoKZRlcGGZ2cgRrUGsVi1brimFleF6BLtF06/SulceVAfql/pqwLVYnXFbaFfsug954ToRHimTNwbWPv5qicAE3SKSGW78de3iU6pmLkv/4BWaw9Qu5g+lIr00sQNXuSe6meWaR4dk8YiKQtQ/n1nNXugRHK87P/LKNhOSo+0EuOcpjAV2Co8OdppH+olwdiZbuQgDDGX09eT/6ZvKp+P200bYsaUlpUnnp0xjD7rM6uQm9TiXWdtY7lSxvTHRWScSnpdekxKeLPKq1MX0xf55fl6weXXZRWG+Cmh8xfzO3mZfQsKwMT+aUlHu2gjnOyVN1LeNuzGVhzCq3DrC+51Uy3GeHFVVnlW7hvTH0oIQmavceHcTCmugHlPam7NxnP1Wg3paQJiG1WBfnTQmkDj5g6bFcUAbxiZilIvcjz+RAuI7UzL5zDSfrspEy9E2vgUMqFe2vd9t1eulCkP/ZPhNdrI2Oyr54c4gRlHwGxvUm5SUC4oz4gEDvYCywBYRkYTl3x7Twp9F0RiOLYfhUXUUK2v2yTQ2TEr/6JhNAz4DfxlMziInprPyorJirA3yoZ+scVXlWWl4+WMnwqZ+h9Z4JRXSpV/SepCLGNJcESZJjDqc31LbZ9ktHu9wiI7RU3v0Wl+KnhtrISpYRkwMMxwDxq7sbisCfSjealN0+c1FX15DOCqiApogf4/ehNaR0Bd3xT+HBF1xxQsBqavdsHeKaOzt8+2nxT2nDQ+GUGsd33f1HQlKvKrZmxJPm/E8SOIjofelXzCO8CptAvFPvXyh8+VOHPaG5ZGkeRgesleq1c6kqHjYdUOanpKPfDSpApbaNpAfq+PdzMzExiuoIbYmCg5OkagOX8apOA2O77yfIL8Y+PB1Ry12T9z9ZTUyLX+3sjV1NSn61a6f0hOxZknD8Y1fOlZeQGcVL/5dieXmCTeM8Q4GagIBTcrXacoLonslVyxwGvbuektc/i08/FZ8DnqQcfreyIAMohQEZeKCetALjrlSZyrkApqTQjA9x41HgEKk3PT8GW662iNBEKyoW/2A2MSmxnZMijETGgu2f4YP1NaYNnKl+/WNEyoGmLyithMhoe9g47cTRQvn6vabd9s27zDPGqr7HvtOy/7M/IkvKxb+b42aUWsY0lwxpyS9aTpGZndmKd9F93aHhTq89en03CTyG0h0C9rN4NOJ+iV4/wZA3AKevtsTOPqEJUy8vaZbf1cCeEytcDh4xr/F77inmW3tK+3EKU0riuidESM/Xh0gbv43mf6CpPyol56lpOt5nZUZ/kpmM0xnWtjd4fN0G5ffgxH7N8aeywGZoXmdRVfqbsId+Ljworz4CTWnvMvx81M5WDtyVFlFGjtoZ7ZHfOSKx3NA2v1N0epWcypa1CgdYH6FxbtiC3Zy+hIAW0ZWXkQCH+y4rzcrC7Rb600w0vu493Zr8EQhoqKyqz+FVqchLmWUbJK4hvf5cc0KyUsxoMsCe9wRANr88TqjI0praMPqg7qChqONmeMuxtPEn/JNOJH5YR6ZRf81KTwZIN+QJVBkF1yzlz9yvRl20O4wimyf/kqn/tcwmo+5An0Cr8SCwMbbfiMyGLzTVevgQ/JF+FZ7NA0RZaImBIAYWfEnKoPp6RrojxrCf+Myh6rUyxUxF4AqworGCEXLQ5InFRwtS+ZGq8yQK3UKItn1zPTcImZSQVJuOzrIGAY4abAT1fIXOgcCBjADuAvLP+OSp6ttQCqjdsaC1ncE/CuvbZ4m7F/ndTf/NELwCrx41+yn0JSeHfBaRDXevxBbXqyshaZFPUYRuu64e7jLYazrHJwk8cikzYgmQNL/GsXPVLCWrT38VZEr+xBSmMlkSaKYV5At+kBZeTxfa0YwUmEspHvMwR/Kg8RrHzeANNfQXopoGyqoaeQjRucOvNelHB+mqBLUoSHzza3VnZ1NXZ9DjGzjHe3gJ3dHWlMzDUosjEBp/XYhwq3M/PBQ7V48trOkY7enW004w6/UAkg43CiobG9LKKnJDOt8RzNO3XtYG+7saM3CRw/FmjS8QCiSFKmncFq4uAdJALwB4pqv3K3l2UGtUare8pyG7qranRBdCNwxtvAtDxIRQ4ZscnoEUUKlZUvF2u0e8oe4y2vBgvUjhrQlrh2k/ytDZjcbUVegx6YxFTQhJHPsRlmmHWc4FOJC1uSL37kZ6Lvcx4QVylyts7pEctwvuSCwxs2J2foxvToJrlWuFTkak6YCoqKip5VCP78LOhyMJXLn++Munf2WFijKZ7Wg4PTs3csG4BbMskmHYUpddxjT5CJQRnrjIhnKo56Pa7OfQcq9JhvDuijTwxv6dzJ/8GSYwowt9uAupibjjuCsDaFVslzHsjXeBmkMUZcKr3RV6WaX6IbOQlXVflP4hY5TRFdD/bmmXvhhrzW9okaepbKU39qK7I83EjlekNnejMls4XrRFno1db06o7FN9KJrgSnUMcyAxyIJLEQGEoEcVanjFEV6oDhUXxg9OiMBh6BMTGk9Ht3ri2EQwPF4UCyDj49j6cOxR0xtzIN2cYkJzO1j4u9HN5JERw6lFSk2EBH9oozzManBuYueoV36kj1QuzQIE4em4Yubyg8kG/+Fb9DCOKKcaIresiHx3YMeCZ0KWXBqKahZjFaElMZuKgfbrMa3dtCMHfb9tBSaz9+twbxO02wr/72mzhqj/QOfMOR5LVpYfbFtsEmGukHHTacui7bSUIsf8d2XGtjSNbruh/WT7uLtfyMM87y2NYUNsKmYEoiWxG4/BDFbirtA1Lb3g8yxc3t47cvmQxff1XxsCIeVFoGSt/IshukkJrh5HDImoiP4XFlokcJt4C1P8KYTeNnqxPZlHOAexJrzPEwGrzcrW12/rP0JbRM2YD1WcbE8aDtSc8y79jW4ZaUpBV2idkcwPmH+7jygrBTZ/vMVWGNTZhouvai3EmlZxtZXQvhP368w0E+PAAkjbNzqzhNjvESjmLzZG4fQLwc6I9k5g2flRVVjIlBkQXIAspzEQnpOGqM0s6F8wCoRQSAYkiY+AxWDCFxvHsMQq71Z8fmU16w+NtewH15pmwKNs42oGaiE5ZyMwoaHzxBIVNoNmjXHxcEMKhGNUWORQMG4yCefg8CEZrTKijGtaVxsRFYiGG+wky4pUa9omqv6vonrlTGz+yMisRlFeXi8F7n+6AlHPQ2rDz/eEgF5fA9eD9v+DLTw8Fku8DHrkwEui8HKiWaJyonAh+ehdEKzZJC4dE4kS6RJ0n473OxIBd1Wls8b3GQaridLGamF60aPTOFKuIgx5i5wagVLENL14j82WpbPSLQmReWcFL9Hhy8vhYKLztNhkohtFcHXWWyWfpH9dgSkJ4zNUoNszJlYevCO63W2jsGmwZHMnY5/HWe4Q1NLQxtvaBiPOvxgGjymi9ypk6lYG6e7N/4VFimhAXAQWTG17WU1BlcVQRZUuZe2MRGXQHLi9ljAbT4L7iYD99Bg19NAqGwQRC0Bg4BoH2Bpqg+MFqrs4yvV4rB/UKQcd/nJF1Cq0hAbnMKD7NWcGDiqcRkRApIFrbsX4hUnzc++5F0UxT54gDpa4jcoHlRPlWEJ6Gg+W9z/SzHAC3Wif/ZxQabR2kkYMXMhTB2Lc927FdE3A3fwP3gySTKoRqXFm6vfzwcAfs2WMtAAD+mHI8Xrla+jO3Z3oGaDrKaD3t8na0zL3r8SJx5WZlTESJU7ywrWLV/h8HIFDLRVQaHR+XnykS5WfFJZQ8L/Nw5z41HMAuWBkQY6r2BSb3B4xqsQ0dj3xyKFVBuToYqBbEFgShz2IFCaQo+BsirRnq/3hxq/StiJd70vxCtWJJ9Nt97uqUnhA7wpRe7wH9GmQFJ2De+GmNQY06ceQD+/URcJQmSp78/QDW9qot1iLghQx5uD9pOBeTONzfmzSYk5M4qEs7gh8xU8NWoi5W2DLceso9ZGrh3+G+EEY4OSmp3iaSGDqquWjx/wWZztqnXnxHVah5BFENW51w8ZH91cPecvc0JZiZTtpNTIXgAed0hj5jRQzcwSjRP7dMRxVblOKh8JHMGbwUBIYI9NNBBjXvB1aJLlCA2N4QLv9xsvC2UI78BVpBT3XtgaZOqeD9Q6Hj8nHarq+f0jXTPlu9ljvquKDrEAKyT8WCfkMviraHRmLHL+odsdlHRkWhPwtozFNZUqmfNl7DEnQaKXO56cPIljfkQ06Yh2A9YFVdxGHVV0HnqZY/7laUSSbVySBNFz0BZb7Wa4trHJsspZ6mJ3nfX/zI6Ge5WfymrjY0L3PU9xzKyehRITHok+bYN9zRn8Kb7puVkxXXFawBLQ+a9UsZvTZ3WLwbO1xmncyexVBuE34vPDwCY4O2mjX/U3OLUUYzm5NVG1rCQuCOu7Ym3Onn3jmRsfcZT2lvwS/vFPdnuDuWERLCUW2hNmwmTUlckpxKlf7l7Q6wHIN58apEbz06kDz7KzO2biUL+SnzAvZVNzVsvFvRHQGj3NIaa7lvlYh71P7OdWNxwm3u3TdOG6ph3OsX4SpiSh0DEBke3YiJblrBxH5EXwHRAfWKnTyzL2zPP4UO2fnx/7CPaX1sJ4slaFJf+Mvo+du1+/fm1x2Orx1NbohsfkL82Rdxshe8eWvUws/atn/gaGzkK45yanjYlI7gKpVvpy6mYrYLoDP1aueyxq7iKoSL/ZKCzdvPsbwEfrxtWVwFN03kl5fOUM2igduS4h9renrFasb6CoOVhBq4BFtJDu3PUF/TBwSsJ2h/cZ9X1QhlxpH69uWe0q16BdQNSb+WL6r9UaqJuJw6l/rqq49irQ2nWbwma+wioUK8zpfNUmy9yKN282E51sdVcA94+xY8laJ2t5/SrL6bhB/JLw+xMCJWK9avdO/t9gf+NnFSszx3qgOBmbg3kTQArb+B4IGIqNi9cWSyzsH8VMxO1hXlO7LmJ3lpL54+aBmq7jh2a/YtWRiMSuxrDl9nLdXBFyazDxt71GQYR9MwUQ6e2ltJr+XsPXFuhFG21d7kNHx3NRuwB7rRkTUjs81H5kaUAL2TINviANlKmoFLOCPDsw3LMjyPLygBE2DJhZ69NA3XgAJwJ88Lww0coun8va2kiTc9fDQdL72zlBFlNamJ3P0kokSl1l0mfUmtT/T6d40k+TRlpE1qtkGvmWdxqkF4KD7GLp5UeUUUPZM+unoA3ndVfrhxTO1peHL9E9MnB81bK2sahKwPmCgTUYl5VPkSB/OT8xJ+xydl0qSLKb98kaFB2ftDYT7DsQ5aTEvNQ4b/Cdn1Wvkoexlzqfr5KjLVfvvmUBgdSwat63tQP330Jz4FGpGnSi8zY+JR9kVGEp4bDfqok1pMUsZAE6JG01GVAYHsEf8EYEtkvfULQ5LRuT4e+sWapvHap2lHxvtpmCG9dOPjw3R6Q+6Ar+Eueeo5QCWE8vhy6A/OzfvIcCcCnmlUVGRZRbL0wYDtUHAnjRu5mjO493PwD85yTJSEI9oDEBMzFvQJ4VD/UZR6AbbhCJZl6CVrW0okqftLPte1mjVy9laQL/AvGQWlEk1MvVdrGEeE/PXM3H3MSaNCw4x3PlZDhoVNreJjLUkjwkP0JNJue8IRzycYva8kVTXuBv8p7OD0lPsjb4KWlaeMltXgMv+5gHLcrtoHlcNcJXAN2YKqquyc+sa0kmrPxpy6ZXQLIMXdOZuwEO/WdQOpoEtdD7NIJlSD2a10ia5rUWs8zbSvBSg30oI3Lma83PNZ8UnneUcYCUGac4yaGpaE0B1A1GkJmNPAm2IbZWonX+vQKfT2VmWFhcy+CEZU0HRsjs1SI/V2sWmA41gNnDNhwYTkCI23xIIsLJt8q2n+3qgpe6fgq/H9qPHEjHC+XW4gpShpYjB+VtbcUNVgKsi7/PM6HwvLneCLsoCw+DAT1GYxmNvIJ7WNNLOTrY7uFJJlZBaVa3FYUs2pNSZDjdifN4goi5JDQtPJO90vIbpldughLCS6th9xYppbN3PK1L3dNQw6oxyr47YZTT39W36yK2knpRSWZbgk0RXIxo1eVDqTMpbHxQ+54Cs+f4sxCVfvcSmBfa1PSgoc74+cmrCO5Rbi00aBSI6mnBJqEv33g8uvK26mncjfmoAvq6M6dF/9sw5aNmop/BbkafbIIalUzkK+UoUywQn4KVxrTJpQifFYY3STFjx+0GyHXf5yMupCPz9q3735GwZcN/gIKTE6dw73UvtMQcEBdpX8Jd6K1kYee60s6sjoHhsqwGeY8HWujRLW4ERy7Uo5yAU1UJWOzGRo0sVoSDfDaKhLJQu1rG5SYSADDUy5GA7pyhC6zbvULo78FO8HShMgVydBovcXJT+wAJJYBJM5iC38EqLTM2w72cNFdlFmEptGPrbqIUFD2yUtPBHm83vbe4ixCjq2vBbLbPWWGH2nJ/N2TdHZ6uTePDYNwHoPdon/h6TBRH3Uh3mT43pEXSJOYX3cJZm3P5r0E+0n09wb7iESRkv5+6J+QBXHdInaEPzeCM9k+9GL7LHV0bj37XqLXc9MjidYk3AK6+8gLNw3cRITwNYqvKSi9nRNXMFayFP8RLbH3+C4dBrGagYUZoNLk+1+jz4+vXdLX33DEGLE/lsuDlxlzLqupXpKj2j+75LmDze14S/yTx8yXXMJBzUPuXDdpoIxk21ua3fcCuIH3fez9d1ZQ4vlZgqD0Z7LJIw/cFxzt7o/c/y5T+tL+U6FTydE6wlpmYfKJHrtkhO1IY4mjSLBSgAICWkeE7QsQm3mw+vZpfE86o5P5F72IalCXhQwxku3GWGtRkn7y2k6ZvdpispzVOJXuK/yadtgSFUrXR41o+B3LxIyLj8SyFyja1wq8THYR605qXm/hM9x9cMa/yZHsW7jJLr+/Ly8Bhealzb06FSOBWBcpIhAIFgOWr6k+n74prThvHH/T1KR4nvZgjxo9i9l7NreSONHgt85D19hdQynuPWBTvYhlPy0cXQM29tHWYpaVolyt6QGa0gxsjtqqN4FXbuBXIdcOJpE+JpowLJahU55rk9lnKespwJo3ZP2/DBJF8q2WqGwxJoUL2GpevjCQMyxhwZ7FIsmi4JeY68OIm4r2tfrR/ZPFtWSzRnCaA4+Z3VHxYwWyDyIrP/9U/BW/LTfwYJJoaISiavTKKHPJRXehYwIvdtRLhWVrXurqzfHRskfMwO9Lj/yluUUdXW2l/a1KndVExBPRv9wLDjoT8IqDqH5KZTuZ9k0h++b5yEG5WtZ4yzejLCAb8MILd1XXU2KsIIXUWtl/FLDEo25tvjLEWCjGfxaju2NrUtX1FeTHzu3Vf3oh40d3wM27Inh304jptmrcNaiGmwRp/OxybCi8uN/d3jf72xnU904qT+3WOHyXl7zxhl08Xlxx0wHotRz0AQw7t2NO8a5DLgsUE6Qx1WZcdVOrH18qs55GOeT/WK0yAqQ5FA7YXqcHrr43eUZeV7Gbw5NIdBmxNcpxgs1fEtKeqfw4Yq2HjTE5Pqh5zNMcEdA17Hep2hFdTNp17n+8yBEtw5Nnn17gV/QBpMVt05CWtMgEteQq1QxyNldIHZz+oXIx8LS+yUQPOhmnGRRSefxh7W9gy/eaBXYxaHdM27/jJMnxN/XaPf/OY1KexHgdcXLNyU1+2h7e0Ffrcid2jPsDJaAGNxUVik+ehG7B+7sNlwZpZ8UtWFM/DjW7TuDfsg3bZwVayk2fhCDKVPxRkHX1IasQeFV8F3brPOCtXqUUIXGb+OXXV1Tt7vfMeaHjYNph7NSTG6oaBK82H8Hupr+LPdly/WKXZdC0pGvs+Os4s1l7YdpsbtHIHukL7ztDN3xBprwkpgTc7jkoZdyn/1SBxoaugvyanrro1lnbH3yvSCMaffMowtHns8x54e4eZOrOLnAsE7fx0uq60l7hb4rfXJdRN9OvrGT2ty9Fxf0djoDEu8VTroifgsPrejtCp6VV75QVqpIiMX1tGEJ2zyH74kKZxu7/h7i15q1VbxJG4gHJZr5l+MPTf3exT3Pp5ikUI0WfTeq9tBE+vasH3g+o76eHItirS2eeD7H+D5Ln5ef72ErOT7X9HpfaHj/DEso+qS6mTRQyLtiAyEBukJmcF2uMfzxcxYtqa8n70IgvQ2lL69+dA14x61Mjy9AFBpJFDvkZ4G3g9IvUJRo+MR7WbHZ+mPDLRUlJzn1LczZCixGMQRIqW0EJlzzU/QDRS38uVGD9C0R12tfxtst37CvECcX2k5x18Mnz5XysBWtiEbBBO9f/vXwCR++2TLUVlakhICsRKoFhoPvbPk4+ftH6kLDv7gIYk5F+LFE1mZwW4xJkG5BLMpacWv/O2jPcHl1k2ZWZGNo3hndI/rf//a1HcBGD5CvKM0UhCN8AodVW5tFXvFXGJf6Rhj5gviGc4RE8v7QCi1Po04DbF6f7et7H/SfDrLR0K9tjs6p7qsZiTnhjfzbNdu21+4tNeqIXB9T+q/l/RdZH/9VUsqFUobVaMy28fdmKZ6sk+sp1yA1BAhMMGJLQI7D7w/rRto3ivcYXKboFkQyy9kvz6SIfH1d6VDEQDa8GSdZXdFx/GH5f+NDzKzGXNjvwU0ZqX5elx/7JuRU9bYEhIfzQDXHiBd/pn8DF44viUh5tMm9i6SlHioTaO9vUV24y9JZisrzhEQC1k/nWpvfEU191HOpstPG+D4f6oaYpFubWPzySGBE6d2Mk6yrrFn/SDuiVKF++do0SD/6yiPfJJlkgqgJR2HMvuEUfacS/9/6qNuYXCct81CeQIuhOUakAcrjbwn2zQrmyHMroq0ua/jNIy4nb6kPcuMYs3vqR1jagWykZZ4Qv/FQnUIfSMsQR1A9CzQbaut5/MV2n6WqPEtI9D/2e2/vZ5FN8Qw9a0raAn5xnq7Eq32QbrLKrPdgc6s0S/4aSigfnh6BOgxXtreop+Ebg7PqvDDvvVYiVGA6OcIOYMOHyFek1eXV2P5uq3Ubq2l+BY/JkKp0UCqgrrZeXeHFsw8DxnSsyHOrrP7jMDkl3erQgtoxN1mj+XV3xwfSPkm7d1pQhr/vfDfgfgzZOzB3BiG+XchJ3yZ91nhVYXLVxf6WhOtAW0WGRFskhq74Z9Ssw2RIXPtqqKH4R1uxuk89n2LO4aBExKXu7ZGx8C4cBKIIc3WW2ytQRIV3t97gekm5CR14NsHuIbQ0NqH2WuG8IOuvNPKn48ZoLQbFqL/2Q0/+HH5c+Yd1JZvRxuqS2Bpv6WTSZH1gRu5o0EypTCD9wfGX5VXRI6Z4xBsjkqWR0e2es823O7OLYdMIO7SgvfHxscrIgm8n/rIYGhWu8fSXsCftttTU4qNearog0yZCcgSDHyuISMvR39E6uzy5FNmpH8TyIR13FJ58EdtL1M174/l06hWq/8lxRHVuTwe+a/B5KrvYJz1iUCbKX9Y7iWH8m2SgOr3+LZYL2MR0pIkohyj2QbwwlinOmSasjG1Nr3zeWSNauLPUZ816RaXl6L/Qqqud3S4jyiP2xtyvyGmNz3upH1y2WFNugJl2tV2FZa3tvk7pnVQC+kFyUnfzSEYsoRaCivruMt797UzHW9CLJ55PMd/LE65Cvmd3jI7MxchY1b75JzqCZL3u6J8mWfuUQ8ZtsL1jB//m9rbPfHv8W1Fy75wSKJZHIvcr/KS6nrSt7NBme7ik1ZhIX1KOjWEIBDGSrGnCysjm+w+fNt69C84G/SsZhixx3EDUTKADvR4bG8kVsqIPKlK5sra2fW42QfF7LOowkjXHSV6yZQ5ylNlX3hjB4gkiK4yPrs+Y9PoJLvtOVvwYXF5MJN+/X5QvfZ4iGvF8t0lA4DweMUSITsXbptV4PGlBkI8cAufc6ecmnDb2bnlw4DXb3bfjE0PAaJUBP0eSjgYzHbOUMBiqb2/xtc0q+IiR6aIQ4Wj/6PDbuuHavNLUOHXf1jh/DovHY0ozp89Wxr5Oz3/amhh/PDyvd7VF2f2CcXBjYpzXc6Wj29MgjsETnLxke6R17sOUMvDK1T936MS5u/C7XjBOMvUw8JSioCLbk/Wsvn4It38cKXvnS3nG5U6XqbmOwu47zExfDwRYybrYoYP++8kkRgsgG2l98mXhh7U+6OlKg0NnqV+nR0HG5LhTppn5izecHbaqA16b+gC96nOfkoLrfWOzrAweW0WKgzmREh4LUsja1aEW/skemVdjvY7guAqvQnWjGkJy9Xz3LoPiTTkrX9FZ81kQ17uTWKYIWWh9OrrSF8fTuioXSIjWK/I/4LvCEnL09+n7KmRq7pdwe923tPYrUb/Ay/FTJMRQH2ECNQAJYxe8/hB451Mtgw2Qpt/ZQgsXvBTivbPSSWmZCE54JLnQNF1vFKcc8lCb2VNhZM//ZOTpwj8LdUyRRkpJe9qq8ynQyP04Zww/TBgMxMZTZuCKOE1zN4OlkREpWuek/FFMDKitrgiYEU+b04Onb0YqNFXncYJ+RnungsJjDnBTaDhlLW3B962RETx449ETkgzJ1lrKX/wSbM+MkNJCT+kjx45aDEVCPLm2U4CAK2+VQG2kQkXNS71bRwpGyQhom4FXgKEpzZGdIRGhIGr1rk6WJ1x1vVYInZPu3olldv94duaWfnDXe/+YtvSfLGjwEVLN4LOZT8+Bc7y6cnkNUvnTI8CRTBAyueNjYtlzyGDCFLLw+ZWeHuTlBP3McVkYf8w5RpGeruMyESVr6Tbd5AWt1MexjEZFhtuQwehEEj/mkAkJktMRIXxonXN1r/H104Ku9uhdc6xJ9hBzvZdvunJ41kCY14T+4teVFtfQY/HUR6T3SHOCpP32jZu+GrxhvxT2LqysQnSg/hgz3sjpS/E/S4jbksRhm1pY+L+AP0xtPsHPNIvbJg7/k72PSiUkCy0g/3cCVf//ALO4vJn7UcH/kVOsI/m2+sCrAwO1ksOQZcr61vny5qnrFgFvj4lEmL3HudUEZxWZWeQnOwnSQ/VUA2gWZHOkp6mBR0RpoFiY6GgX1GCm7SY1KSBoHmKa6oaJamMAoueuN9/15mLOxTnIxYwaeRVXQoYX2UTh82Plo/2xKofJUygfVW7MH4rTUn2TXfj8pB/EnDnq/BB0Wqtzocv2MSoHnWoMrJTgdlmq9D2WUEgOKtsxygQewH7u7R0A+1L9W0BYaEoUrY6q9qttk/D9D4LyOCi89u3XRrsSjQkVGwI6ptPzEH7N6D2jWa+SztNINskeTfiHYCTzFRPJu/bQmHE9MGCNcwE01AZdNhRKDu37N8Jg4ClM7+Y1DnOdYMNAxWGuoRrpFl2I2sygispKE4UpccJG4XsbBZcmDEM8Jzie2gS1NNGjRVMUUBvZOTN+eCW3X3mxh2T/dp/+5TUw/usIyds0ZcbsTJeo4fQupuh2pi5Kc+Om+jLeLh82IMlA0M/jEHTzPFK+Q9oD9CjjY9x+5WMekr1xQXftg7aMahQZH6r/HmBBUCLZe8yVtB+KhpX0wPhVyOVGDoqQz9ILAl9JeqH8X8Yn+dEICJy36bspAZEyzW9dT6eQbDo9JPvnZa5NBDrzRrKpwKHOFWsq+kNpO9/N22hCpN5jws7tCTMCZ2rg5Cn7/1l6Aee57FqspVQ+ViaGSqn2gIav31SADoZC35sfAywffFPgKxmeUn63zHVJcANPeu9tNNH8g/oITbpUxYawajb7YK41bP0xPaf1JR58A0j8nKb3c+ZpX9l0V5Xz+BMKyU9vUnCDHbQHudCR5zoGhPtdAIpljosGafjOtNdNFh5P8T9I8+Z1NGXKjDzxcpXsXURDc9mfNc0iETmdknIHPZh/g/pbaIJRXVG3nYTnyF8d+lP+6lOoxmHNKkEjc43glM8GbxrpFHc5P94tCKlAD+c/OgZgvyw6KWc91qik4hLTg00FvpILU8qN/QIT5wUPaqzLWAdaUELlP8QxWnRBPPi/xj4Q8YL2+ViPuzepcOytNcEpJLu/lUj2a+W8QqaFbAaN2lf0Ua54XYqPkPZIC7n4lvegxNe0cNdfcAz27VB6ajl/D+gtQeDn0DyVs5evpGlKuYefR+dU3nFlAVOB7Zp4wimI0c2qMZ6SonKmTRp0DEs4xfVfU5Qx4xf/hRsIsKmgULxT2QHvIbkWLuwGLp5S0ecSN70gbR3rvCLZqLNOtVoRhUgnLOn+FLcYWaxwg89eijeppsf0yxERSh3vVv3fFdN0JYxX5s7sFhd1oVv9dSXFyPay6JpVXjwLZ1uWC+azIx9DOhLmaCgDUiFGGOYhApGIQjTikZIlv74Mra6ks3Vx/qTaDzLeSP/hzXS9bgnm7kOeuFNwd8FpNnHCLFZ1hg6aU2Cc9zbAeNj171RHvAtHJ6Rmk5yppvARe2FliTHL7zEhLorXz7ZkQripdqti7wg6/TYaR95qyAVSVmu1S4yniBiIo3XIMkQP3o25QEx1B1mkxprrGTPdH0zdUIBMFK0tKmCt1NR7BJWFwnVF3YoXH8rp7YZGn1B9atNb39/Q9yDDCKlUg+cIykrrDuwoncc3mmHDyOhevwcZnRJadPEU2V1kXEHgHY6uaYFUfkK4nv706fthXR0xvTf2y7Pu+rsRNNRTeupiu+xKav4ZTd9lbMyyPFD4g4al3Obitop7Xt8k+lsjpj783lSax/7wG8XvPXSRqQQ7jGTkUUc9KJoBdgf7kSUu/iz7XrPceX+c8f9rR5Vrm/+L0GszPj1U11sXu0tJU9hvvSdJh5Dxrxyp6JA9U9L6JVapw73wDWzbgtz+/QSrtWrYARJMeNe5hDPH1nQsKCw3+ztonEl768eIR8IJISMKK/SM8QIfP8jDJ6X9z4C4dQxsNhRcpszWhd8l58T/jP9yF3Kq0RCkoemFrcfWTdT0Hu26mXRlYtfjlYKgTb0QmOub7x16Et3mLu06uLiWtuQrDmZ6AHqRnnZCkglF9EV7Ootd+bw0Wr+Ag3nBQdZJpRO7S7vopZggsqM03Pm/Vr5czxkWrcsNtGQ4JUu6WNnSInQ+uLR2/ltv/xHm4k/s5jhkG+QvTu/NxdqfllKfXGFHOiZ5D6tKVDDWt/BXa6q62jdr0jery1+T8GCNOv1iP6krSSqV1+/V8cluhvnVyV6vNvLXJrG5VnZJtcavkcNfl3hcNzo4/IopkZpmNdBH+5ry92pi1dCQ0asUO3RtVqmXK2A48F306f5W0L+ex69nIKSn1ELqRCCRBNJTEFIHhIYnsL1GYl/vrYlKvZorGXNe7FPYJH388j0LbKqHBODirioaGjnKZQvTvYYlIZfYbmkM2ShssPTQhe3VAvRylMtaSwd9QKCAQAG2BU5L84jZeOEfXawYeHpQOqp8isDPoKLI+MR8YrVCYIbD8x5C0y2piKIQYdZcIwAjmvCQoUBt3WJK6gI1RrVsCqBr2m9moDKDeJmD3mXLoLVauG0ZLKylHWwZdFXtesoOcdHbOdHTNgVOW4S6agoX0QfawVFPQMqVgxn53dCnknBWxsoPubSQzRnvo2ndg+IKJeFXUSM4QpOelpqj25k7GvZaZRuFnw6NxDfOXzfpebn5OfNOqZy35IdHPMa0HSVA+QAEL2VMqs3FtydwpmgX872+2QKx395BBxzElv5vhwF29nnD+zTm45DT5/0//f/rg5cv3fp5e+p/Ov2JD/hNIpRN/O27rf5C4OYfAvEXK+dAFCVwALjr175jwIkAuB0+8DjT++ID7tbtDv7dnASu+QQnnQM435wu94b1b6nIdDDPKUXjZw3ZA3w+KR8mgzLS3soDzJNUX7pwAjHZHwy2RmJ8b8mVXOR6rs/IWI4k+U0PaExuGhTmPslB9kA2LqTRTZMHg/v0dUpE8z0IZwa2aca450ZvK/pJRrf0MFRif3Dbke7uBeyAZlsB36gbtI0eSjUmtoYqclwna9gD5cFCtUGXDibGLExScg7gZyC9oTncekwNjckkzCk285QVWySUHrMu9g3Tt0d3Bbb7rrgK2efmBPkD16T1DbBaJBJqJelYc6mG4nHr29TQTBeRlZg5YU6xmQ5zykrSy7Im7sfPSL3Le5JSGhqDyCrAb3EmM5UuASLlazh2dDYB8DeD6nyvHs7xPM4dqaHe3vgo6/HK5QVN949fWwTIp9hb0kGEuuxgTiDPr1LUFwyyzFonGT3F1uXz/OgwdXLgVvt5MzkF6bC8roYi/UzJp+47xreynHxI8f22azKPXg4vPuMg/jjPuU3Cxth09wrpaRYDyDi+J2ItpIbzAqRGwDDx2LLsUijtKJuUTYeSssmnuViqdOuKE1KrO8Z2mJM0GS5zkodLL+sVzGQZ0oFIKz6WlEyAiYqWlOW3xx6Vf3ecpmvIq6esplQEjKdGa8Y0s7oCzSk202F5v2pEwwEY8RHSLJN9LsSeCb6ShtSVup6VAWERLas4NCdkZt9FNXh2wjqWX68jbtKN8RjJE8lBX+7tSk73cZ/ShlwOEmcutslb+CVA/VDTfnIrV0pyZ57frG4A9hzzQ4eI5HGdyGOwZ9wnc9wLBuZZ8K6gleogCpvyE2bvWNLA18OcAj1FcTJun20RF+apOVIGVPLjps/ZJqkK3e6XQawNNVd3Xmv1omVMmMO69HjIp9LNhO+23hJZbpaPfU/ApxQC+YJY7fkEwX6WrLOpYFHO3fmjKjboiZBD6FRLPxXPx5F62SI1vOP9E3FBzv1KzUXj5zh/T5hDDFculHB+iMPdJYO7w3P4mGj4Suni3uB+EhPfI9aPrcB5yvUjqenBGQIebQq1d+LQEuZ9L5RNOzzd+9BZFx22NB39pL9+rHfjnpLFTpTRywf9rfJChWvOmD3kQRyqEKrQwXGF5Q+yBs8lbbvOC6cZcxiMCv+TgxmtztwtyyE+c35dLyajd4w5VmIj2rb1GA3NOWv++lPIjeetvcFvNx0bvydinfZdEfFvD2QpULIzXs/RIueezCrIJ6NXPcWqSxeUBzFwN1lO+6iedot9sHNH4HVH8KgCBailJXwX99ioWhDWJlE3+LmYSgejaQfEXBOYEWLxsxtaSlKFXTaN5UpdqzpmqT6JAT53VEPzRlnfpkea5z772bRBIFJTduj9aAX3plRDdcOtb9MjzXAn8tK0ABrrYe/qfUVF+cjlYK9kyiROB2zNi1rR4H6m5YRf0v68r6uKhKjJfSQ1gxpMcy4sAUSc+brmWfnMwCP7q5bCOTxc5LKlknqNjLaGs3g2U8M0xQycXrxuxYmseprFNEo97lhDWybAiag7H2023J4VWYrnuKGuMYCWvWqZKWPQmSQR8YBhkde09XrM1KKNBs92apiiCswlY64KcJ6U15hT+YXj2ZBm4LyBDs6SyEfWzcT5zMAjZ6uWBp6rEb5IDYcAqeE2AqQGUKq2GnlrJt5wG9YsFeT4WIugSFg8qgm6xX2fMWupuP/5Oq2tFNVAHZgsfDAMt2HNUgFj3MYiKBIWj+KTAJ4Bca7F2TTcnpWyFNT7D5lOjRVJSTU9e/ZXJjqcM7Pm+gj6vA5lSKXPl1QV2GeJxZx4l321g/0xTByR6Xtz3UmO5kDmVo20bpu7OKj+u7GmhnpN3qFBMcXmn6332cmhwDkNdRPqhulKgG/2nhi3J97uyc4xLh5uuqbZTvCObjT8g1ESRy4YkwJc7g5HY8n9vTma46uXJy1HJucmTUhxVPRXpYOHa2ge7P1hCSimzHF1ljKk/8qCd0oE748fpbh//Vs2DZ7WwkhpQ1WJ4+zqsBhT6neUwIE2jPmF50/YRWzyZjlnNOcTKR9A69abUoxh0WEuAx6Ood3e3x8O5oisiCtfV0XNIkMS4YQj0d3d6W189fJ0Kuu42F5xyvIEFwZYmFp2eMH4dlrYsxlPQbnBqyset5LnnyH/ZqhjHCDtM26Ew7WBozk2h2Ov2k0Tu0Z/7MRHUskFPuD1JrW9vbq+6Sa/ZTn0XE/bqxuOBzP84xfIxcUtfr2TPxdSyre3eGNTS5uV25/6AG/4/X6X1dBeNO7k83xe31xbf7HnM/TXvp/37PVxW91e0ZsL3DSovhhv7CjeGulL+eylzbDdAgYYIpyejH+EIGFnPfXjSbmMMUdOFBUiZFgSPDjVH5ehiFjgT51jyQUKo5ZoyVrPKiDhg2Ke4ibHGFPCQCXbf9+S8UNCHFzeg0t0BzsDvz2BkASlD/PL+d98+GFFUTwUGSEII/yIWAimZS424QAlG/1KgED1KjxFQOdAmMgNFh8UktK6Bek9HBrh3yFSaqodWwMy5BUIoMEYMSdWNI97sJt6vxTv/2DctO6SpC49XQwmf8ekyR0hdz+FNCq1wb3gOXEmuUt7ANayW/H9BcamiV/QNPlmMx7OTZp3Mcd8tMNnhletKkGwJuXBGAMtjrDB3hH8lxtRY7KJtajUk8rqEWOtw9HctAsc43hGkcI7j9na0HEB5h0AfgvtTwmcZzMfV4ZRZAZZb1Z5DiAT7rZ5XVXnL6iqJUlU60m6NK+ItZku5DwdlQMKOKGgsMfkJ8hpmRULBJjV5Xrmd6/SEvN8fV0AvNEhTXeQp1gLPEm5gZqlV1LCyQIfeLGTalVTeEtTilbfvsOnGH6Gf1p0g5RtYR+r+mdUGmfkTpE7ZU+m6D0rzZaC7/wXHgt5a2tW/eMOX72//2F3BpMRQNb/jBvhe/wmvqP/TyK4vN+Tg/j9oSkQW2D5DSUIv1HkQr4Y3wyKp/x5oP6hC7XfasKceArqeV8Hk3noIZ2hmge6v6AxaoA3WudvaboZf8bGgeOzbs5tFsse/rz+szR8Uq1fQLWE/Qv+15+s+E9j7prPPqNF+ma6w901HI4IVI/s8jXHQuwRebghZxmUGB1ryE0nrZIbpQR4SEapn+IPzjDcWC5rOha66xQNqfHLKmu1bt+itQLYPwPY7p5R9krcXBKVtRW71sEzR6VXIdlR6ErrJTVg6IbJG4Zc4o6oTRgpMQUfI0Kyi3i7cTdH50TrCWLkHRMbVBSSZABkRYqJB1L9E3nvcHDyFGHwqGWuJxk1icuScFfSUw/dMwCSUo96QWVTO8lcIF8GTkZZDBEEcgl0k2USPaEM84v3Cm1pnyWRQkrkKBdjz9/+HCMhfdKyy8Cx+cJ5FJrnpFpLIBBeDetQl1ERHi8kglSvDzjJVbpKKgWW1LN1jIh2RRBQjwKJllaLA41sUdWygHBBxHsrtPnECvRJCap/4w34PVbsybUk6RowqoYAs0UZukAMrQjejmEsrUs+gMO+Ff4ZQampHaHDStxRkCTrhrUu6JFYFOpH6mhI6mjKRm6lGSW/gc6BfeT7oWyPPiUVJWUPOPib9Q+JZ6qkzHqqawcVeG+78zQGK8A+AchLYWV2I9fHTmfdR0S9wz0J95bZCPTdVi7Q++4ZT9H0RMAgbA8qECRPFnCkyGEVRS9Ji67XQ9GkCAFnawRK9Vm2HZ2ejokBCiMb1WIETDRqGy83HHpCYJ+L7mvWD6WU3JuBjQvNGsyPtUSMVmRsT87EGwcTsKiemGkJH2IKJTufgchN3ycYJAkKq0heCUrRQFBJ1Bxiy1m5NDhwrJyisgjnlB4XEEOKYA4MARBBGu2PQMEjwl1RHG3YcaPzbU4QiNA5Q0D5iS8DlBipgLEYABXimACvs72P0sCDfk/MMTp4QyPsE/0hYIryGbh0xzPIzLMkycPUb7Js6XF/kbCdXP4EKWUHPuozUbPhDAt/SMTm3t+pPS910Rgsz1imRE/LzHDIkFaoHwHN+0e4KBhoSJKj6Fh6iRQGxsCLpagG/YCHNr0YVqujwJzrPVb0tBQGeIa28q4TXaabHWjV5k1REMUJF0NTnMlmaBKNTQPcr2dhd6mu2MFMBifnkmFZ12niCEhoF9Af6HFcEFzIWVoreN+6YXmoX2FNjvLldbL9K3a/ldOdu2mBze205f/ut8qy//gJWbbth0+U+qdHlNr2+79++w97fZ187lqFw+eEXj2zKgkj/GxAWp0DcAi0YhYh7oLKZ8ceOV0AxjVK8F8KF35eh9IcGBAnXU7pIU3+lN/kt/m9/lL/n/5Fh7qGKzw8HN7vP/60PB9bl+eFSPWuDaeFnvRBfyG+Et/1Z/2N+CtGFfVMgXwE+oCB/R0vWzzMKEEvNZtFYtEoXVVBmaNL4JpXnP11RPr1pxEgggYK0rsxG3oHgDCMjfGI201d5lOhTrztkhn2KBrtNDd4fIf+RMTCNFvMeoiweAR6N+5EoYAkB5BS6QQpbDF3j+Qaldohhk5tWoa3l55NauYVFlDr8kilM6VH6FFNtmIGJfYmG48Bu67N1UGt5q6PagrS2hSdVksQJeYCgcF7U4V+Li4OU6hff2XQ4eHu4T4jveO8HI4G+BToepX3WNftE7XG7HVnCNgGJ6bkPJSIE42hVB/KStWG8wmV8pAQALfKz3L2t+SkLGJMXWRLFnzChlCwsmpZZDhPM5D3aCJ3GIOtUX6pxpvAuh9PTHjIz6VkAdFZmmk9Drlgq7uBTSOUO8xW0VMcCt7HY21ebEq6WcriMCZA8Zo8f38ZJcYW0noIbp3TYAjA1njztPYZtDw9IqMA6NMvtOLIGLQOvVoVel1BP1tV0dik0tUPSTPguDBJ0uj3+I5+Sf9E/5WKKO7bIi8oCh90yWx2btLu2QWLsxOAeIAK9N7SSFcWCsBHXE6y71X3rfvd3drdp3dv3B9d5ygOF4GWwn3FucJdqDhNpvj3wGiTCJr4aaKjj88f+AP/ZlIPowKM7iagmw/iz1o1oUqT6IgmDVCYLatu6lDrpkqSky1joQ90XLDEHd4BSU+SjgkfKveO4heV1Kzw/uMgb4X6p6sy9pOQNxdylxVpp1wiuosNn8aiaZsvyNSBFHRoLYD8CNJZWfVTtoHRdN1gsTsZ4ZMoVtUA4MRy7S52+eXeAFEmYeHBb1X6kH6TqPzKI3+Q1m1O6SHtWKRcxgU0fNpEbp39EsA2Ty4RNDvp86rVX6SqZVy5pBHIM9GOiTH7VVxgIsJsgZiY45D9wNgxvvK1evk67voXYNhd+ykCSdq1/Zz9+Z9h+2l9d8XHLGs/yMh58zmjbaiLDDh9Y03l4DCgbRSuAi7sYyU1qst0EYVa4sQ++xRgaGbktHuu8oP8PmPOL8eeXq9JOHjkC5hP+1eQpzn2aTpFXK/mopgXBIA7TbjO9In4/xo++hfuMqzXkMfyTpjMuHiuLUtAuBw/yCUeFwTIl9mSmX5zoIR9+yEf/BlnT0U67AxdmxfjLghZpzDyTz9RAKNU02QSY7qsob/A1V1Hr1atYzWlPwDg3QsSsePodLwE17tWi4AU604jLSTzwrlRQpJJyxLapSX1xO/JWGa/KtO48UmSp0CbKKqHxHuyBE3dkrX7U4mgOfg0j8jfbtC5qSb7tnUtunmqxFPzQnszgWHppy5w9oKcSZjA+xCyrjVF5XdKrKGvko+qdZGL6Qi030dgAUk0acJCqFLpkwouJoPVXRvzjxLZIyBcxCFYSkYAyTRbqT5cqtelqrpjBRePizxSnnGxEQajGj5ScW6wZiMSqypjKYFPVUAdE1rFEOUOcSVqIdgBqYvmawuIWUaffxPjz6K/pWRq4OHNm984nez6rSOafeinclhBnzrXr/ByT2OEXvdJQcdeb7fTz9gyRXlXji2XJfo1futatoPrN0lxsDZdv6PkbezudFHyBusw8yPunDF5VTEAiwDmbpmv8/Imz9F33CStE2QIx531RByxkiptcPQqWKojDcAS/Ragp0mi190decpScmanooSRhUCB/UK71vPU39g0ZbNpcwrkTaREKx7JjT9t3fbcnBQ3wWWX2b4znhaERM1t2M0FvtoSgMPhPftXk1WI40TroYYhBwmxydtNHMNE51bZjDGV8kQcbIcOnjsvOACpoKOtp8ng2BnBL7rLavucCgOlMRrOXRC+NIgukkxmXgvflOXWRX5AgINBQjxP4fKzBf7QVje3UC3TLXvzut2aV7f+1f6RV2DiGeM70prTaTf/paFJ8YgOOJr3HwY+bF/P/HQ9q0BvX4dw+8ZfAMS1u2X3n6l5U9LvfgKl0eV1+JzXrys0r86U6StT6rF5YnN5ZtwEwC//XZCgU4++DXOPoqC+4lbcBvf1tP1t7LdYivQk0ngAAof6V97JYYAVB99dwy73oTWVZlGppfdP8eEJIWhXE3z7BK2DPwyBzybPoNMuXIhDvikproKjrrZvqjm5QecUiDtdDGl5cKXjpt0sX7ULEILdstyDldPW09Q7UBvU0ZMafLOOpMOjU+P8uHIB1y4tiWb8Fw8ylbgxckonLofvqGpd8/VKeoET6B7DI6jF4hGB9gmaTFfd7twChROQXNVCs+4ie4ErxYntOys5Rt/Pnf/H6U7odknLuQw7xLoGerkXHfCtUvUHTqkHdVA4o2p+gsbz9SO7Dm7p7KPByeOLP8nvnDcuQNyB19vGibCuZECZzYBni2H2F0pf0vN3bPy7gc7n+6afogTSte60AVMjEy49YhbMsSU+NBgKmT4BLaXVXfosRmErFOOpibFVYhqnuacpNwTqVDeUvbjA80yXU5MPtSzzD0xZntTAHqmB/PL06emN/8//709w0sNywGHIFcOhBNHy/Nyuh+6vSb5sdTvKhooqT9rBnxKph3Woa7hUsbW2SaeAUXQJeuidmdibtOpJgQ1jE1uJchKn1WZe6WK/QbHtXhinIqUpAZAVbhVZI7sPK/v5nCQKAYQg8epkGrV3H69CfRFVcJ8dEpwfw99hoMDY1NET/SK/yptzVYkgK3i3eYXQ1rIQF3niURtCl4XQqAVvSKO2x6HPnQoa1QARODozVcAjUdovIbUXusdpdDoapX0sHkXtNmJ7Pk+BmdPbjNgsDMb6NeQEvhSMFWxKaLNbNBz8tKDzUlkvlo4pVd1WICQg4awTh71ajbYZmQBCOLuEL8vkzW1db164MU3nbkPvXpUJx+/qM/J2uE003t7SvH19RuXxi0dyDfEJoPz0CA36pEdFYqZgZpZiUkzziSl2xYUi8MKuH+xVPi+44kLK7SmRDACBZY1VJq2+fSIpBxxFGvQmAwdKQ/Ogtf1kU5lY077Pk5ja1vJptPXJawqZ0w+k9EsVa0nhsbYL5fzKWfnAQpRNJM+6doi3Jh9rfoLJv5jk6N7kAi5VjzDuMjsrB/wPJio9M3PfEHciHyWKUabUaZETMdfCawYfSQp5UVzBpdzJccwwxHlg81IsusUZ2XUadHhEp32AMLOzHulFgkcYNQCJD2mqY7MoXh6a08yd7ap6+5ZaJPRt5M/iTbyBqldJ/eOve5pqqc/zKrQTGX7T6lOwLHK4fk/yBZUoD5blVU7eY82ayz/G0ciJHICQxOXCjFZuJ4q/mUrUm+VEQjjCIDhTYbYyqJEpAmFe2CY8FfYLDAIVqkmn5G+AFpHwC/exdcvmixu3c4wEl+lYYPMsQKvtaDzfGjNKAIWqyBxk2OeNlakWZ58F4HLXfZRHOBmV0VTQFDVRqwE+M5sXjwzEclwiDOQxX09VFJIpU1fF70fCn0M5uT9qfIE/6J7uXPexQlPC4Bz2Uf19kWYP4iDQlhK282EwFvdFlBVEjpWGSb5OohUuFhCSHpxtXZrNWQwaWIIE6ff7d/OU+4cAl+7HKws/YHi/QlNgq7y4kn628yKPSCOMcYqpx0gnoFgZuvU6pxaPdDU3pREhLUW4GBrODg7HH0vM997IjP/GOZjKzIxv+NY8VLy3812/AKVlveb3lOCV9b5JrlOK6nmAXy4dTobUGKjOiWP4VcLEi9lfYFgCwWbmY19CSzlYYAFKwFrwZB5tsqcT13g80+QoJC75sRsis2qdYky8k7mtqFUhWrPRAsrgqB+bwajTzBIRKAKQtyKtOjNg0pLaMM34LHxvgvs4YsdQx2u6Wgl6ulCinfD2Jtu4zVuiA6ddohG+ya/fMudf9tedzLnLxUrFrASQawkVtH/1/g8+ce4Q49oxftczXrLPR1wM3mTmGZoFf+ekKYntbVyICu8nlYUu5/cOSx/K3HR8Xmp4xq5p6Isi2E0rmhRxlL/xlVM9nxmp7+7eGPGWyizAlHY05x+mWzVtexkQ74Vq9yu8vQlX0jYKRpm7W3pBtu6k9miu0coWsUD3rLHLgvxTwod2Zhc7Nk7T+Milq4B2hCcjkPZqxAWcSuGDqdMCU2eArXOG0tHnFBfFGgbt4RyyZ1QuoJcf/gyjSs5+0yfZ2tPrKwvz3DCWrxjsLx5pNrjq3ONVtdzPqFt6piC24Tu+3TiyXidncj2sE43WK182zfhEo2V6Jb2kfGDi0GMTEyTv4VhCWSJdr6Y8X3EVTe3rJzx0YZnY7ku3I3CAN/WYLP8hKY5KwaZ11d5Ov5GeCAGFzWcEoQxaDdPQZBLnUIpvFU5iZGamjX6Xbtwrcl4BaFtHulmP05TDmrOPcrK3J3koHFja/gFaKT1CyuTS0bhoASxTpvTrnvK0h+1ud3m5dnwA8Lnt1/zwYnlzU+5oG0J7JoRyt9JClnxVnlDSrczOLG0vZyfI6kK6XEfNYpDe5VWvnmjF4SqREb7MZa0s7MBzYTHCeeEjxxqPlQEsWzHaAJjOndf6lH2ufzKlKMypa3AaqdYhX4WeFgTN1pxUPHk/SKkLVAqa6NjJKOITQvHEIuVALcSOW5gUqUyMzhbKVpxOpQI1ncFdaV1pLR3HGWNQtIEtiJDveCjXzXySTZNCPLicAs+4XnENvR7OaD1Jkq8iCeA/Tsg1aL0zA+2AG8ps8S2vzjzVesZViLNnTUZ3XO16GAgTkpGf3PRgJnaR453J8Ph7LUhbUtWmIXDcXK5mHPoO0kWpI2l1+0Spm+TWaQ/pvb4mRdf3cdwYo1PuI1Ef8WK34VpDx9VSQrmaT0rgDiAHO7p4T5lHHC96MQu/MeIkTtYD45htzXrBOZU21T2bxlA+C8Eq640Fv6Wd2RxuCkMqZPXlcqcIuZ+onCIdeuSTfryuHlBPQjXCiqU3a0AzQex7yv0SbfHtJq6dF3Hqae8gDnuuujzKCW2YnTEYayIA4ZcWLVQDcKD7QfXPqCL5m6LgYhQGdY5n5TSDut34A6WNnN0IXMW9quWZ+rpiN1UVQUcpot3kOtlwxxNlqanG3u9kCwiCXhlYcvRELyyLGLOHa8RRJMKx8s1NyeBprTNHNwyQtCPQiiAZOpWKbPCKJyILsg9F1lqUL7Ti0u0nKKAEClmG3IhH5pSeb4IXoEJVg8MUuyFZrBNzVkjyTQCmlCLVNOEDoXuynnxiHcRa1+TBpsCat5lf7DDEGN4SY75eL7S+pnnnXcgdhXvaVpnGaE0QwWCYivUUKHYr1M1Ihh4LXMvJnQd6ohLxZPBr5eQ0Rg6CVWPnZs/z+DYB6iA6OkssrPJATLMay6ddT9IJoCqEUmfWxNJaFiqU6BOZUhl730JJ21gNmLZAxgLsApmwgZ4hBKIZbBsSNlWCBtiL4RCIX+riY2YDckRJFCwIi1XodcRNmzpeSiGh6qFkY2+FJ10lkfSFQrZ8I1oQWwDJvCZLDH7yc8SFYPX75KQ0YHMFfVDsawWa0iNdC7aNzvAbTXljuFpaDFtu5X6niLUMa526kUnNaKLFY11nGMpK+y54QagXTEBHGU3nSAttpXjZgit5DsHAPD6jaHojpUWyoQdZejVuviNcd94Os4POfFFEgokRB04i7wLCAm/VdVa4kVUOn2zIpsaos8VyCaj9pTbrwit1ekpcOcO1eUyhwMeUrD/TNboAlT86l4OP9Dgltu/tGdkPfaLR0vMi3RW9UHFWO597+ZMITxwSoxiIaEXXxgoLqKSkrLnnLiVgtcTMkgl7zvWZXKkFXr+8S+i3U6spC+opxgZp0mUlmmrHNx+EbF5aq4xdFaGuG4peBgV8DCzPLiu7a9dBUqvGK2fIc3HmOvQrGYxWQTUTDKGE4rsqiJ6oNi1kJfHeI6oWqGlMIEzRea9IGiISXfllgdBYV4ZaUqNhiFfYCtyAPq5AiAelhH7ZsRgT6PEmaKLOgAZ1qtThKd4hkxqchHtBaXS3UjKGK0IiJWqgOfq9hJIQ8R4tOL+m1LmjVBncPSpSZOyzgdeEq/+2CbvQaqnWBaUsB7ACxOhpWYIchyDDE0r2hef+CeQSGFXD+MZzh4PJ2Gtzd230jK4r5ivgeTScjKgdxzSqil5VgR+nQ+Ura9JAdasYR/kE1wIjf0RidqNjUoCVWqq2xY8BmWS8vfKOS4bDCmaJNk/MGUIs53QllY0zwssMA31fg9ZZnUEJUK8AwibU6jNtbS/zTLyHtGiO0JoqLQ6qn3draNqymJpsb4yXeapbkvVAu1Zkr6Q4iaSjMJhd+cRpYChgSIesxuD53oRrz0kmHUWxvdmKHpFGj8sGwpP7HE3KkSZTBRAlFt7HpJzKM2OgHtesPq+MSvNKj3N6STehXWA1S9lPm2bD9hcLNbfGms8J1s71nD9SM/nl06dPb5J/yf9kgic9L2cU9DSrGtRqf6aNzwxOU5QsRpgklZUxiKGd6bmVcVSnyqWq4H1CfKXqdAD9NJum1osFJBVCXehqNrycUFWnVwruzVbwuPHRBuW8pKMamiZTKWUOmU6bFM8dqkV90TiVKq9Z0/Gyu3k/AiptBnlFlV86K1gJQNM4gi0+A15l1FdT1mMhBxwLTwmHohiJndPwpGb9A+d5GBXJz3R2uDrQq1V5H4g84cbl7jxITD7R+Kx/BznBv/e3iNY6qr+PCvVBFo9ZfjDV0yQl8y64z0jwj7/udYp8HcwbZ9Ru4fwDLtfHH7pBGkLe3XpEVMUCcMgsJPDxR/Seg7IXf8BRrJ9Ood8ABwcWSHmeqUhLEJJUuCteXYyoFqM8aZHHj5lbjZRl2R+aMNObwSty65xQDRa90+aKaUyA7MV7CV57QgpABB7Zz1A+1Xun8uYx1dZN7WUMzIWeZxYDR4dgPqiGG5VipTpZVxc8l5JvviyJFi3SQIaVGFcjA79E4u/4FEb11thYmQx5j9tNrzAws2X2GaUCMJ33qF60ITbLbXf9yZ+//OsfBUyFCc32+s//+tfTf/XZZ7f/+L/0t98Wv76SP1dS677FxmaOLo7H/V/xzmtitN1kkpFSJx6jd+wpqCBc4p/J54sBHH9KFDRZJ+hc/I5GClTvLH66zGBQ8SFqJxVYJG7MvB+BqB50VFoLotKlUNRHPQ36SzRmQWqd3k3zuuzz8mZ1zuc0U0+gJZ7TD8arLOeHps/L+y4pZ9/1fLMuFuPiSzJ1nsbD+E9nlaBzK7u2661VbVtjmbvX2tSGcuu4Z1oeFhRgDTJRYSG9ZA6LtA6brv3UdT2m1n3PL/eZqcyXFFU9DlP7jmka+ma4eMfAGfyMxfBmenTsRarqTmII24Q9y6emX2zS/QV/Xgbro2hi804qcUdqjNGtVvDOKnXVO66ddMwfqb3g2tsgoFuozncDJBxP81X64nYPLWlbDGXpiNzYTGjThQAgFX/OpLOTDxHps27bgqayo7st4pKu9Fi1RVXKAulhrNPiIAUpXXmde8DSC0fvZksaQkFqBTNeThW0h8srfnuDxZfZtp3w4G7ezb7K6Kv9nqSjL0AKmGAT6oUngqBGpq5dk2klcwEk9KU73rUp0qLLDBBxM5XgjVw5PgyS26eouprcyPJhojSvcnKZw/QwJVM9ZQaGTEHM3jvkU7ZTf8fo7lp4eEPSaMP2L/i06W9nfsoiIflkuRy4XoFafXvGeyWMPxJpe1C7pEKAeNVeQnwkAyBPrXpBIjARc0nQiBl+sKJRQEqsPKek+x3zHXx3LpmSIarBVOl0IcBOtp0GYHQyuPQ3E6ZO5z4Wo0emwKp1Al6cI0+mIRJz8BjgcuhAxFFMag0tcOxLWOQgse3W/PZGTY/R8q6uMX3sOvNQfrt/X7CAeNQ+rLnBiTx4Q+ZiRjVjE40cGEBcDQzNcqjuLujLw26BpruseHxHHpPofHG5Vp1wqS979V8tTkF8hdViWjwsvgq+Ddgi7AOkLZrrP0SByCkk1qI4qAMk4KdU52WK2QVucyL9WhOYKIu0n5ZIvczMwm+UuIgv6puidhYRwJDHOeQOi09ND+jvpVFy0gd6pfgeCByP8GfTJZoRop/+5fXwnhNPuSIx+glRYZuI24DCeGIQvNSiRIDysJyka4Vm2AG6Jzs2CJsJnWPYGzBGF1y08JkA3ncKSzclgjTxpD4zXNJPy+g05jzDZGQMwk1YkfbZYLW9EQST8sSBPc2WMPEWyLBJio9VelZpc/vZlswVKabCY+1RLFjhs/mhhzCWPDaA/I7s+/i9nYvCqbT0dlOMoLpKHpzhQ+N/CtXxhvaDF0Q8LErR8Zhgs5hOXT5lZnlJs/ot7upFFis0Kk7j5eFyaRhNLzB1xezIZa8W9buoQd26jhZY1uNHpR4SL8VHnDJk+WxSI+THR7ednxw8ffrAirw8h+kDwUi00dTZ7Q6gTqVLZamqM5tN6Mh7BBwb3B4K+uOJmWyuKM9tmxy4cnOD5f7GGnNhU+/U4CBy8ODq6QMnb62eewcWVo8eQA5gbiTBcOs4MFhFVoHeHgxFmTRXFqu1Zmv1AFPJEOuadljc3GaqZa+P8+s3n91nlDEjXA6YS7kkHt5vrt+LD0L7uBiandHwlsFn8ifoc8CPxZZOdCSYppHdXO/HulqNVXVeYQJwOcR7OfRQHAUn/FajmdnJzXg5Jncbn+eHNNG+uWafvLm5ur46s7y2Cl7Y8glYYtFaki4O+gVhJ5WfmZBmigTZYVaJB8ZYX2e2xhi0Axg174fknzCq7VIbF7UmXrvXF8W2dOO48Cwiio+p3KeXe5ctFpCllWf+KlPPHXRPaMgM3mE8GwXOyE7ZpRGJrbdtJx8JVPbQ+F8R+VN8T8nvQahhgn4jicHl5/2Pwz6tQMOkBXAhL6zvYlroX0RNe/fjuEkdbtW4AnztmsPj5Aqs9qGGhQ4a70H4Bv+LczB0R9CoOvElaLd2ATFN4c3al9jzUy9jWsWOzwe2abwulYhyj/uiNqboO2pPn/NPCKkHi9XmnYNuh7nRPSTgsrkix7YlpjZjkZ2z5RTF6cBsWfyEuobwUDzDcor0m2zAb6JiuPwojSf6FcEluCRfR5f2N1eneK2vr/cv83Wg+Tas3fQO1+3lpWth2S6fSVt6u3O7Z0rn3H7m11+BxA68iaHqgA9X5A7Rxzo3fOhF09icNNlVaMZH4t0DDHMvZWGWLCMjpaNz1rYNgy4GQFvL6MSKx1JLQhpiIkSrYHMjV2CCR/Bej8BaSUWWQm60ixkNcaJ9Vxa42fJ6QoKY3CTegoI7GwlzzIorgcf1+qRDfuC2qWtP5fAjW83cNe4ZbBof0NsBng1U2Y3yVPC1Abw3dD5vcoxcKUe+7UY2Lxsq+MpveppfSwt9t0LvXsqW73nWILXYodAQfO60xBQWp1OBkuWTpSQ5q1EiZRr42d7nv79V84eOaM2WXDykXFoYJuHZv8IyfYHDqkSbKPg2PLKr0enUaA+OZW2hTZrQbHlaY0ykaTRs6JtCCpo/u6VeGrzJqWuJpDP6UNU4YUMz1d8JJ6ETWdzc39+ZqPHeQeMW72NxKssqrRpSHbVv+mm12ZKdGfmkNsYlPFJdVePUiVDjMez4T52mBsnsbgt+DnHKl6lWIS4ANLYdW82+5e1bxoL7sNNg8/Qxyx6hJSpvY5IYJGbEFcWwvq0cCXrvfRizial6ApBQNzYv64Y2n7MQih9okojZbwrWjD1w/nCGq7LwMDB/JrtKLlSPOcSXvh821bbw9Qra3VjgU10GfrFbdKniG7UWIq8DoB9V8VbKt8+kkkYCUqUlct0azKPcJYtBQMWOlxzuSipnWSs73xosI7paAa0T1rSo5BJQs8hRnnDxptao65SZNC2528CG2ahIPJ2TGm39YK7rdKJUmkkIQqE3xAWvMIWhAiPVkWxCTycZJqjdb0RQEt37DchnJEqGvJKjD7lG3+BLhX44EoukuHaeqgkyaxB1DRfDlCSuYYaogdR3ZFvMxPE8Jw9jPB9uiFU1kDCjDERJzMtDnUiEp9vDMC2AYMrQf2IlHj9Er03afRPSnzNBhYnPyjQo5kzZoLjRGqIx33FPfTRnYsQ8LplYZlrMpeDWJQpH9eQj7ULOPMkw8MyoHXRPHEjFHR0DEysBkBXhEDy6tdJoMYY7Rghpd87y7X3F6DcTnJY3i0WznNsm/PQV2s8X7MXtcOOXjVrl6MdsLt+yQRuZLUpAkVWXyepq2fdhnGIwUCroNm3ZzXV35ceg1rgIry7UaFdFQzKpFscNurTdY/kEIYdYZhriw9imEY+nzPTNbl/un3G6pHbCYZrOrsaXyryzm3GFLm32WD4We7zrq7Fv0uYpuq629M08ol2pVgyXldt6JV/o1mMYtKsw3O4OpQr19sCgLNfSC2lQIApTWKHQypTboIW40ZYLeFye/lDjUkX9Fn9/aLTIBtTd1PMVp+STo1FOMsVjQi1cfhPaqswIqM5C+SOsUKn3DIsLDDvHCkIazyMkmu8bhny9pnEbvCleSJp9oRw1lORylXq/ySr77I6m6azm8UgknMB8dhVmd4fTM9miqJcqmUJBlDCTLOZmSi4pHLa4AOWiju6LswFLgJV4BriJXzj5ipXIlXwiAuamk5s3suJuD/SHGUOMpUMhplKdCcDIvIbKThH6lHVP+ZsrROPbwSskW7GJoafFRCgMlR/IMeRndh+q+rBh2+BFhqRoxMhl1/EWym6oRPIaObmU0TkOuQ7uawc4cSU9LaPlhHD5GO9IbQxvnGmWgTNPOslB4XI8xn1XkYtZBF87V34n30sinyNRPyRXe4JU0YeNlWaL/9y+lu6G9qYTczl/Luqf6cidgPSK0WyjLC5tHY6mIz7a5LbaoplkatuhPZPmYq+vNdrcdCSVERv9xdHWySHiyOEoPY2m9MozxDFcTvFULBbkD42W0D2fFd2zf9POnav7m2IW7W8EzxT6g4IsVwf8/sFgP49ihFMi1enYBHhGtlq16CIPrax8EyC1Qpm8xH2bxeWIL8lnpk56M6f9kS6qNEazzTFN+QJhJsVn8kRFjLBJ7uqnPIRwrLsQFyX5YRIihexVNspBwWwWUg6OBc7hkjNcDoav8kLv9Mch4VvfQG7wOJ16H11081IWPFtaNxR7SvAg8kO3ZJf7SCv4Qi2JoMdTvkiBDlau8YYhu0ZB7P9I16LFdb4GYoKzio9yYSncjzEolrN2CjCl2RTU5uoUzWOhAhG0de703Srj85W/OeRL+zy7/yvTdqRTx2MGZDwcJ5OQaw9Wrd7gz1aeKI4RmqcszKOwgAO+OaCNk4QD7RstNTA+9akF+zkUIDhDIaYyDEr8nnoCvTmjfabF10gi87MPnC4N6DxPwGmh48JryRL5bJsbtBarCnTLYsghn1xio7EDXbUZ3jF2nFqfbLWt/XK15m7G4ojcq3Y308v9cr3CMdZpQcNTKKHpsYyRzRKMrFah3LHhzVpMMH6iXYBHJ8at7iy3jihjQQTSYPOIuVhYJE79KKu9UEos2lWSx6yrJB/jSBVzrQcliNjYdnQ5FUAzjWIiTJW5fcYN1NLsSCMyXNGsK83aGpE7aLR0MYTiEbu18trd5GoLJD3tbDHXNplmLdZkMhkKIyGLJ1lMw/MYhHbIBaApIw1vy7XKF/kc8QM5ilQxVaTJRxUsA8q6OG0o1zq58F4Pk21N2LAFwqyqyYtVwa8Kv+nJGTCiOapE62A5X5zkOihrkWzgGo/nabD5apZj50ACKRcXY8K9LHclqnMBcksrX2XgvKdephQqws29bYGZuBsCpOGXa6K1o2XLMpJ0DUtzdBRRqgghrQUiRsjX9n1uvDgpErWCQDRKyxV3lTXElP9YKFswaZzCjATmWsgOUyJIqUsS0iaq7o2ka8dko2xOX+qT11HpnCqtZYsi7Tx5djUv21IZnm6VagVdZLpBGEP6Zge59bqZtN3Te6bgngyp+lOvctAjRG4kBTHHCUhz3hjKG0CQQoczV2+jhCJgorr1Mq5FMs2LAIPwJLniwl46mvahpIgADgihNGnhtmOp3YczbOizKg5lYsFSqvhKkyaS1NantbpmK9zfaDwvPSmQCIRIxCmh24tuA3LNgUhHRLf008EWRNKBSRYUU8rilaEFY036Ziot5BUJOXK1vQBbxztoFyOvDiNdzUl4RiWYyLnzlOtn8qEb7TQ8M9XHXBDhpHQu4c7ZECEdkc7LQVGJYWhc9I1/olHUqBHlnRiiGszaa7m9hlfbhYV0NFv22aevVy+WKL+s9i/eAnu9TwR9ua/8+eqqPjz7KIHGjyrosmxtyezCD5iqWVUkL+wS1oMxYX35kr0+3hS7MnlKp116Bju9W+5Q405luiK5Qr6+d2UtXpwR5YdH+0ity+mRMlQ/iuvkDDxCQVg0csn0EpLNtD1gHKV2sIfcKXg17xp5JqK1h9WpnUr91mjJUjig+quVuKXNaJW0uSMIWO4yqT0lYKURtYzrKvGWmgyTFhkDvXuZqQIYNrIB4tDEPUfCW0BrAHxEMpFHH+LMloaFxy1eVRBWm9vUSMO5Y4RaiXZjqSlqAD7dueqkz/pmHMrKBcINprUxNAijA8ee5WjnQClOubm1XCHHRuHZ65dSUiALoGiUSVd43eR2TkbAs3RRyqclKbCpsqt4KtINVfZMylrIb3HvkpvyMudTaUmJbCFWJZYdto0vLa/MBir+SchW4ilhSNr8kfN6sihzX6vBJvINm8ixjWDxzNoq3ZR8rsRV5Qs02ZU5raw1Rb6FyJGm2h0S5D3/sv++/yH+InKjuIvWeZA1EpvP/McNRWmEFyhOTdBBoa8y8ZgexeG6r5yzstjFJuV+NNi1TeMSlR6KZMaitERxCTxx8VWSzJqrGUkrAx4F0qkYTHaaXi7csM4JX5sqmVkQRP0MGGcYMwmrM9QTBbP8Pk2ZsxxTKIWuyjJYg0AHNP/9zLerbu57J3SBlu7obqtKrFSeTiPXz3P/zEXnIYxaD5QDD8ejWDwOOhP3c/FVOmcZLH1IyyVUoTdTkyS0t+uQAUnWMQ+z29IN4rnoqE0emzZZVFfTowl2fhtChckD+XVgV5cqw0rh7elrtW234ZntdjLz2qwcZYapfQYmuwM3Yta8KpJN+WoBm3rtfYlRgGSh+wuVPWQJoZdIua5n0rH1hoWeVnqJKQ0vIrs5X6g084MjcJ5obXZ2CpHfa0wTMcfe0HmZygyfSMmydM/Ug2DEbdpzBlkCElFyUkOUsPfG2DnROfRN9ev5prHBKev3ZI1HEznlDDFhhwK6ACkLjWHAh+tUraux6PEl9DVhkDG8XP2SOlLOzuBxha6u3qVPnEKPKxMOJq6lhFmpDmnHJrrqmXPaG9R2IfIRDxE1Y8ZGiEVLWbgsj5TRxROtS1upsgLXTDJmm1YlI8coqz1OnXFCsrtWWdpmPnucauWEZEsWmKyMQcubZzo0XKylFclWlAhdDjHZQdo/J1xFsbIFm7XmUl6Kecb8ruVrrYyqwPaYpxGwDE7E+1gq37VJEDiKlPoa+LDlMxtxxU55xG1K3Q0XiMhC1EkDTcnvvQ/AdaOLoZZAG41KPJtQmGrmBZNHwtgVu9hlCOkCcfzVr8qe4qC1iCqxRsdOxZV9WrMfITVq68gosor4Dq5si4L2OcaOQ583Z/LcA9Qr0Lw+w0Mh635HyaWRtr8hK/CRHPpZ3b/b/rXPqSRsETSWuFU5rEcu7d4+iGdxtITFo8jj7TGYrfoNc384CML8FnfWd3MPZ2tB1+8IVeZHyh5E3DI1NDj0rum2N3SlQ2atufdT8AWuxCLGT8ZYd7F9n7aaULA5yrpvxa8ZiGW4Lz6zfVnyZFPhFdUoJn0BBJAXAYA3dzCzP0PP6IoUujbtyZHVfWf7LvXUyEI8uSpxlyvcJpL2XM62zCdYgiM1BWjiKDe1xBwkJEKFFqhy4oZAVticM2ors47n5M4lynu31tGZLYLcDiRA2DwQ53xslI55+rzKF4g9xzfrCFnX64zfPcP5S4b5AaZnanD+DcYy6VZmS7QfWOfDoloLmetXiHXD2xux4N4Dz+C/cPkLXRiW0t/5TJj5PtdoWx7OM2+5Xc77lpQjtDO/vlout8qTxOEnSfK1UqZ/8Yq+HZ7ZRg3LP5zhWXlbT5Slt0wdptWW7y8oDadZaa0kEf1iGEJqHfRCUaMPfDfUEcpAMriytXnBN2u0ztm3OFcui/Wye8uSluGmtIuOEI5lG9/WWIbSaz/yeam8v8J5TZvHVfSh1Na71vpt1VkmJrAziLuZrldZtp9n+8yMC4uqzlM2TBNAzyuqcJpV6ANku4FO44/LMgz6iZaC1A7FgB5NuOlX1j5P3CKnqWXFrq1rng7MDuHT4qYBDRcxUyVbHC1y7MQ5JgtuIbJyFwxS7hri5YDX6B02KnqdPdKBJUcdcSmgFuqO2ps3Af67H4GXTykHqc5sQXhCIk2PD9QsEuLMWVKQZtBCm2TBB6ZpXl5BkiRK6s9jC6hcmxxJwkSD1xq4iZcrrqA4xjNlFeUVbEtIp3655vuLjdPavZW0NRoLHaH3jRrNIsr+YPOxphWGOtUtX69sMs/J2TwXMQ5PRK1EjDQUBcrD7aV1dm0Lvuvn1O9Z7R/M8+/7fv7/s0iVlgf6tBp7l6efefavvozfP1undI1FPkvqbtOM4MjgOThy4Kszvj/+uv835ocNdLPATN3gu0DJmdOBwiYvZ23H/9T/w3lmb7tq2T+7yWfNmgsubH0cBGsZ61V7JkbWrJgoaTGyCL3ld9WnixdUU4jazbaIuP7nH7aYnLJCmnLTOc6pbU9i/ufZ8oVOgWKCTeNwGpevr9+v6mRm+J3BcZ4D32IHfX5/yJqh6c8KsPwEMsMTTv7gHzz6kx7Pxj+HDp/QgMoNX1/nFv0B/T2xLdKk9KGr1/jnVh+1S4AZ57ogCeVLn8vpn2vs13f26pMtZc8G9TRf4na8B/XN+nax9pe9+httqLlwXfQ0FvDcq54YgFT3p9WZLcdGR4v2qUS2nIPv2RsAgUKlxemvJyTxCniK1/00zagWEgErExwPd0GLWX8r8/8VafKXk58Yu+V0ZFna+3R06O+/I+0+L+y/fD241LpbiW08FYnzCLXSTEaa3lorVJSZfYGL2WHacQM+F5IwACdeA2Y13uH32b+ZtOrlb3Ey1KDla2/0EPRTu4YfsarxL0U22FVRwF8+WncrtT3/d3e3L18eXm9urnf7/WUOZ8tshMubQ/rJm3z0GdLf3Gzgklxe9d7f3e6uopq6Dkf98bbt8Kcrxj5S+tr1ZZW7pqSnYXZ4sZ/1OK6fGPtOlXsdQvooaA1P9F/uMDX2mLas9Wjy97yU690/QDIS/CVYopNRFlvRpajfX6fYeMuO0CdlxmGuZCXeUxYRgABg/B+cSxG+u/WdlH8RHgQs5r5y8eMaOPZbB/349f9VqzMbei861TcAFGCcASyNKxuwoaBlzmqIpLAjDqkXJ5gita9nY6mLml8ixUjUxPIaRZVuSu3FteoN0KynSou8c1ypB5Puu6A8erXiDy+UGmUNpZlpIRXRClriydN0Yd5jnkg6LdV6/w+I/fCGhOUpB5LCmZ6YcxinqkJJc9JnFnqMjyB3XXumoCaFxaFVrmRPKjDL7WPKWfToVI8gx+jWBDCa4HAyjp8CZEXq7Vi5M3qjpUAQy/s23sSMakejSWwxjh42k9Y39QcEy4R3ktg8GetW72TtClmYowPioUbytC23GCbtiFG0IUTEn94mCqgGSBngSw7EILLwJlwGG/LXhEsqWsAy1TF7lHzaM/WehIJJp0xYNnT+gkSZoHOhBRD7CMH0RCxlaeUB0bMOr6kvBdgylZWyr9oJoF5XBQdStZ5X7aTMdN5MfOxPEshvCvGh6LuWKO2gqIAa49yS+mwTknIICXIhriY2+dASBhUwvsKZKmzym6I0+ArmaYVOT+5ehFaAyKF+qWg7WMQ6AwctYRyA0jpSM5Tl+iAB8MgIQavQepChKzmgl2mUrT7oGrSVEYLmoS5ZAizJQUea4MmdP5cYYVvwEvqRfCERdltScWmxWgX0CpYAzUpqBNtAKvFzi8O+12yugYsW9V3JOHDhh/M2WAjaCtJynTSapfE9lAc7EBTL1E/tCRbJCMG6YB8ugm6gdT8qCLYH6wEAeOwPFuT6E3gfNrVwIRWtB+2D/jy1UY6eWHUxsi5fsWAPDO8JPWqzUmvFO78Et2w45aPo4FHK+9J+bZrq49XMm8TtHq6W/pgD3Vw6v+aTfafn+aIVu5EAcIsKtRMgyHctUYB+V1ctCgxyNJfpYjsKqHO1DqKNfpxpZ2llIrMnxJtJgF5yVH2sc5L2xMEaK6aIbaJh4q60UritRG5DOmpnrqVvU7BmAcVs9Vg2wne25yleFbNBQO0XO5tr7M0q6rSw3CfxiGOui8eg1nqJENReTBkgMdQLnEG724YDgjWQmsvgmA+nZpSMQF62x7MHzpghBPQIdRUGrDG36oJwBE1JBtYFAGoblCezdizLSn9pzpTtDkL46c+cGOlSXY2G7Eu9wxh0E1vsn+t6QTCcWMtBcRFUGrEBSjf7GwQzL0oJFtPb7dESk4h1WSxBwFjwU9pvcsEylrs/6rPZNsiqNjXYJRykdVOj2rMk9APkEPPQ61KMJH1s964ahWDxweFwAdmU2GeX44wLeVukIG2TNYArl/WHhbxvzQK/7kLSsRTh8MF2xPKkWsKfEihsqkbqNngKbS+2D6tuh+b0PBmoM/bU00KGQU1ItwFhtfX709PLjF5zyVsk2IqPj2Z36BX6aueDoOE0r1yoDgd7yNSGIzmQ2M+P0yHMDkwhgZl7RVuJiZI+daLNYIJdQ20cHNcVQExM6B/zB4goj1JUTBOp5BKRAFjyvumd1E0r9GXOaDSx3s435mGlr3ZQ7lRrgJdG+OoBhoZgHeIWQYyTebVhfu2dfW6lfmFD8PpMZcScIvTVSnd20Qasx2zkqb/2xslYsGJjVIWjA3R2hkVHTQ2AMnSwGeGwWeaeFHJMHgQ0AM7qzp1v5d3qn1zgLRLIgz79QrRE+PQItKmr7HHPyxJAKwG/uKGgNpfBYrVhptpc1LlyxXKM5dG6MO7AEu0VnFEb43PObs6/PR4xd3RsqTM48wqdj5BoEM6ox3Er1fkl8S1sxgayQY6sBTUCamO0kuqsyW0cXU69/2YSWeAw7BCMVMhSy8ztuuwqJ2Qul8yBv0wlYk7JaMa+WOUh1jhVCDcmTeD4CfgjuGcWsSUnsWThTO0OE49zJsCbhxD/rO0LgD7FMg2WjxgurlsqHIIROavXq0Tgl8FJM1ISLoA0LkjVARTM1oIZM12qwt5LohIsdbDUJyWpPEnqQiqKuXoALXh8OoLzKAH6F/QQ6Hdgcb5PuErSQF+BvnfSqm6TsmPYLm/wNQHaq2KAx7Y6xaHw3Mj8YBnRZUeWStYfp1y4QLCEEES8SqOiLU7sFqlfkHWI5UY7ZlrlCcGQj0pUFnNfhE8STbZbMWHmiGamkS1VoMoFv5A7ExhRYBqxeXbI3cKQvpAd0h7OA9iODgLWuoB1JTbV3HU6R7U+nrUjsEnIErEJibpZGiuJFb0pn+M+z+14J2QfwSWQZ4sGGuGunb4SZ3P/CeGI0CkgvjaEv2itBUVKkm7iDF4P57WBoDxqt5MuEIDo5DNmsRAGoMBDQhFgGNwDAwSAzeBEoBn3OO5xP9CAQAiGBhS4sK0BgzisQ/HUBgbMpr+BCUE8bGBBu7EbCAi17xveggS71cCBIuf63kY0NnBB5ZcbeHjGnLFmwItjtrUXbBbcR/xS4jmQG8/DavqYZIFHvX/Wb1br4TrzTkpuOQbq22UxM3kKmU/EernILh9N1nsVGpqFKB2bvu2xfiAlxzEYoma9vPR69bv2fbuEkNGbpudCe39jywc9zJLAnKkmPBBEc3VpJBdY+vp41OpPBiBAREn2fYb2iGqmQiBVHU8ityaeVRwB2cmTvQMlx0cihKX02cPnG+RBKcjOSHZomM9d0Z/lZHnJtzSFAvhGWwQeL+MyXx7CR6Np0ALESDu0BfXG4xALsoCVYQjUI6NxwaL+pAAOcnLRjN9mYTOiFf90Sm3B3cdauQO1zxZjxcHukFYWmCNfF3zltJIxCouvo/Xogc6xKRgKKGBzBOwPBHgAG6ZK4+Nev3EeM/DEC3J6c7MjwA9/AggkiGBCEPIuIkKZy3uIDYNw5iEx49+PoogmhljiiCeBRJJIJoVU0kjnfT5ASgYyPiST+VBkIScbBTkoySWPfGgKULGAQhai5h9oWEQRxZTwEaWU8S2H2Ewdp2nmIVv4HAtfcph2w+EzY8AmdvECXkIjX7CVC/zOc2ijk//CKxiHg3xHD1f4Hi0fswMdLj7hKteMibAQAvGAR+gZpJ8BfmAxY9DELW5wEwNP+ItbgFT+fgFKSn1cKigkWAn/M+RJ+NOIb55FRUbHxnQrg3weJ96JMz1tjHVcrzGVS6HSmgaRwfTZZ3O4PL5AKBJLpL32v0CuUKrUGq3O0sraxtaO1He7GIeOHDtx6sy5C5euXLsBAPtqtwwwBAqDI5AoNAaLwxNq0ZpIIlOoNDqDyeqvSffsV8Th8vgCoUgskcrkCqVKrdHq9IZ6G01mSwcOq6y6rZ29g6OTs0uf7VizbsOmbau+VOvm7uHp5e3j6+dvJAAQBIZAYXAEEoXGYHF4AnGCcF+Mzvh/3fP4AqFILJHK5AqlSq1xTHRydnF1c/fw9CIQSWQKlUZnMFlsDpfHFwhFYolUJlcoVWqNVgeoNxhNZovVZnc4XW6PP0SYUMaFVNozvp12giBKsqJqumFatuNWqrV6o9lqd7q9/mA4Gk+ms/liuVpvtjtt2reu4HUZ848OncZJMEKyBu/8e2vA4L6+A7/Og4ePHj/x0pvDZ89fvIRVq1CBc6qgFamRJUeut2ev3wAgBCMohhMkRTMsxwuiJCuqphumZTuu5wdhFCdplhdlVTdt1w/jNC/rdrc/HE/ny/W2WK7Wm+1ufwBACEZQDCdIimZYjhdESVZUTTdMy3Zczw/CKE7SLC/Kqm6Op/Plers/ni8CkUSmUGl0BpPF5nB5fIFQJJZIZXKFUqXWaHWAeoPRZLZYbXaH0+X2+COxqHlk9Zv/mBc5ImIS8qQUyChSokyFKjXqiqrphmnZjuv5QRjFSZrlRVnVTdstV0Zcb7Y72R8cHh2fnJ6dX1xeXd8AIAQjKIYTJEUzLMcLoiQrqqYbpmU7rucHYRQnaZYXZVU3bdcP4zQv63a3PxxP58v1dvwJo06cfGAs48ypA6ZNZLDrvPMn7zebhb++6OJLfiPs5+3ju3bZ5VeYsK66+pprr5tsX7xxa9K0CSyWg2AhP1YCWT09ndY7d6j8oXT0gM3yFr7K/bwPSrZVyu3X4E96lP/TacPTj7uAVNkf4A7JSv747wYSEfKb6D/m7BkierBaWk16kTTcjdgd5YjFoa3RjgxrhF/quMfyfg55Zqn1WJOVDt/MoNlDhvrmsG7V8OfkKdBjbFWcQpZacGKHHo7a0wl9NJs4UbbIp7+REX/XUWZHR+2Z4qP5lvND5oi/dJnAVW0qYojiWGSDCvDSZUVggvMQyXCMwTlz4KHLl+DcMCMIwsMfDURnw9P5qO42js5guD+KO7oo/l89qgnGjQ5xONT3Esfpw8HNEPkw3I1imczQY0rDspJWJKeQ5/a7ejcZZ8lvxjILD7II+aWgrMlWcQb10GV8687u+J+ta2VbnQr2WHJhUykrhkuYHqts2LGtLudJW6nXHCnXcHejuvnGOWw6hMMFNdVZ4AbwFjSmugvgXXZNpRVYUKNmhZ+kk5fhK3TyduLT471mfEfmbpjtL04a9vExXozPiX/9AjpWI//gn0d0u81sK0sZMhS04GMqw2IR4qwvCq53YNAx+6mNd4FG+xPZ3maV8GHqyfaB9zV1cLjkAhWAH/+QsxnVi9RRbt4oTSPKiufcXZUumwEd7Ut3F5xQ10kPXHtNeS/tR/dPVEgFap5o9Qhlf1OI0DgKxqiLUwkSCFIIroLY/AZBLsKSD1CJopOwRhsxK4c5b6jdLPP3/FvGJ2/UqQ7qXd+UjxGTvzjCl5heEkv3tFg8bu+xerZA9CAWYYKYC6I39DWU3G4kOujPE4Oc8BtuBRKCtyBV6+3QnDna5TPYeo2zYCaDs+kuqO/EGrfg966ne4lVEytwqdQfjf6qSpFnAOJAQxHhunyb6OUdBiPL9NJRZNkwgCk1UbS1D8kgRCm1hQhlwwIIYZxyGzwshTHGmBBCCCGEkKVlMsbY0QERJpRxkWUjACJMKONiiFJKKaWUP042gPYxDECECWVcDBHecG8oXAOut8svkDt8Hc4uoyd+HfTlY+CgX26HtzfUx/kcIh38HG3GN5DplrSRxq3bUU8xqc8oxbl+uri3nUSvJq107UUtZynKXfMrBd/+0Euvgx1Wx2j88Uy65IHpyCdY6dPufza0ktnefoYl75+/uC0udqGwg8P7Biqu4WNz7f/76dm//vINf+a/QbyTjPm6Imrdic+wvv2ykNpEJkMffafnD/kg490HzZ+Z5rl+2kfhhPJwzVpxXjmOnf2Mp2m/+REpemAMMy0V/j/Efy4iTCjjQjrdnXZ250cVdD0BLmzp1siNqLMdDk9sAvT4XqH90vInMr3S++//86kVxe5XXjP++9+vV3XTNx0cC67EBwfv/PLR8/f3791h90MPj2dJXv3o0jBt7AgQYUIZF9JxlTax6QARJpRxIR1XaRObARBhQpmQjqu0ic0kTCjjQjqu0iY2D0CECWVcSMdV2sTmBYgwoYwL6bhKm81+eRV8Kj2GChFhQhkX0nGVHubL33L4NABEmFDGhXRcpU1sDkCECWVcSMdV2sTmAkSYUMaFdFylTb+jAkCECWVcSMdV2sQmAkQXHhOfAwARJpRxIR1XaRObFyDChDIupOMqbWKzACJMKBv8S4Xl1lBlbP+c+CillNZaa62r5dBaa621NsYYY0wND2OMMcYY2+zRJ5RxQZjYVonx8C0M23ytZ9I5+Jj+/5dNngMIjdFHWQDO7JYfSCmMHLODyku0021ljikS5YhctooCAXFBDv5lZxdIpzfYNzl3qFJ/7OzXdUcO7078M8hqLbSViaQ0DfgdnDyTqXVgGz60FuxiMtx3Wu7iiX1n0b2FFAS2kJP9rxai6tSePe8yHV9Lt7xz9659Bw9u151MCdUTjuWACXd9WT+hXLWO5IWUBYLbtSlWTuBlO5GmQ2VnO09MdboYJ6VVGUNGZmcBIyPsv/nV0EjlZ6yRe4uq9Bbmk6ECEUrxyvLvQSE4VaHWKGzQ2uidm532zrbc3tAMoQQDsneD9f9IVcH0aPHWmzN7rXFW3qKoEVRR8egrBD0bWkHXHPRF98fub130RQeRWxZvO/6bdlqIX6aVjfy04uDtCongCsHjyhscvDiC47/ii2umEKAgPFCOgCtgjfw1AiBXjyBq5BP6NFNJLNv7RPv/IrWOzY/XkbsoLHhqvi+M1jX0y4MyGq88R5DH+v2oEWqe3jpXsLIOBcNEd51KCJ47oDY8cYJlt5w4eZL9xjZ5AixIRZgeuLYA") font_name = "Open Sans" style_name = "SemiBold" font_weight = 600 cache/0/16/0/ascent = 18.0 cache/0/16/0/descent = 5.0 cache/0/16/0/underline_position = 1.60938 cache/0/16/0/underline_thickness = 0.796875 cache/0/16/0/scale = 1.0 cache/0/16/0/textures/0/offsets = PackedInt32Array(16, 0, 240, 4, 252, 4, 4, 16, 253, 20, 3, 13, 256, 33, 0, 16, 256, 49, 0, 17, 255, 66, 1, 18, 254, 84, 2, 20, 24, 104, 232, 6, 246, 110, 10, 13, 251, 123, 5, 16, 256, 139, 0, 16, 252, 155, 4, 17, 66, 172, 190, 17) cache/0/16/0/textures/0/image = SubResource("Image_bboio") cache/0/16/0/glyphs/3/advance = Vector2(4.15625, 16.0938) cache/0/16/0/glyphs/3/offset = Vector2(-1, -1) cache/0/16/0/glyphs/3/size = Vector2(2, 2) cache/0/16/0/glyphs/3/uv_rect = Rect2(1, 1, 2, 2) cache/0/16/0/glyphs/3/texture_idx = 0 cache/0/16/0/glyphs/48/advance = Vector2(14.7656, 16.0938) cache/0/16/0/glyphs/48/offset = Vector2(0, -13) cache/0/16/0/glyphs/48/size = Vector2(15, 14) cache/0/16/0/glyphs/48/uv_rect = Rect2(1, 5, 15, 14) cache/0/16/0/glyphs/48/texture_idx = 0 cache/0/16/0/glyphs/19/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/19/offset = Vector2(-1, -13) cache/0/16/0/glyphs/19/size = Vector2(11, 14) cache/0/16/0/glyphs/19/uv_rect = Rect2(18, 5, 11, 14) cache/0/16/0/glyphs/19/texture_idx = 0 cache/0/16/0/glyphs/43/advance = Vector2(12.0156, 16.0938) cache/0/16/0/glyphs/43/offset = Vector2(0, -13) cache/0/16/0/glyphs/43/size = Vector2(12, 14) cache/0/16/0/glyphs/43/uv_rect = Rect2(31, 5, 12, 14) cache/0/16/0/glyphs/43/texture_idx = 0 cache/0/16/0/glyphs/72/advance = Vector2(9.21875, 16.0938) cache/0/16/0/glyphs/72/offset = Vector2(-1, -10) cache/0/16/0/glyphs/72/size = Vector2(11, 11) cache/0/16/0/glyphs/72/uv_rect = Rect2(45, 5, 11, 11) cache/0/16/0/glyphs/72/texture_idx = 0 cache/0/16/0/glyphs/91/advance = Vector2(8.8125, 16.0938) cache/0/16/0/glyphs/91/offset = Vector2(-1, -10) cache/0/16/0/glyphs/91/size = Vector2(11, 11) cache/0/16/0/glyphs/91/uv_rect = Rect2(58, 5, 11, 11) cache/0/16/0/glyphs/91/texture_idx = 0 cache/0/16/0/glyphs/70/advance = Vector2(7.92188, 16.0938) cache/0/16/0/glyphs/70/offset = Vector2(-1, -10) cache/0/16/0/glyphs/70/size = Vector2(10, 11) cache/0/16/0/glyphs/70/uv_rect = Rect2(71, 5, 10, 11) cache/0/16/0/glyphs/70/texture_idx = 0 cache/0/16/0/glyphs/82/advance = Vector2(9.78125, 16.0938) cache/0/16/0/glyphs/82/offset = Vector2(-1, -10) cache/0/16/0/glyphs/82/size = Vector2(11, 11) cache/0/16/0/glyphs/82/uv_rect = Rect2(83, 5, 11, 11) cache/0/16/0/glyphs/82/texture_idx = 0 cache/0/16/0/glyphs/71/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/71/offset = Vector2(-1, -13) cache/0/16/0/glyphs/71/size = Vector2(11, 14) cache/0/16/0/glyphs/71/uv_rect = Rect2(96, 5, 11, 14) cache/0/16/0/glyphs/71/texture_idx = 0 cache/0/16/0/glyphs/85/advance = Vector2(6.90625, 16.0938) cache/0/16/0/glyphs/85/offset = Vector2(0, -10) cache/0/16/0/glyphs/85/size = Vector2(8, 11) cache/0/16/0/glyphs/85/uv_rect = Rect2(109, 5, 8, 11) cache/0/16/0/glyphs/85/texture_idx = 0 cache/0/16/0/glyphs/81/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/81/offset = Vector2(0, -10) cache/0/16/0/glyphs/81/size = Vector2(10, 11) cache/0/16/0/glyphs/81/uv_rect = Rect2(119, 5, 10, 11) cache/0/16/0/glyphs/81/texture_idx = 0 cache/0/16/0/glyphs/68/advance = Vector2(9.28125, 16.0938) cache/0/16/0/glyphs/68/offset = Vector2(-1, -10) cache/0/16/0/glyphs/68/size = Vector2(11, 11) cache/0/16/0/glyphs/68/uv_rect = Rect2(131, 5, 11, 11) cache/0/16/0/glyphs/68/texture_idx = 0 cache/0/16/0/glyphs/80/advance = Vector2(15.2969, 16.0938) cache/0/16/0/glyphs/80/offset = Vector2(0, -10) cache/0/16/0/glyphs/80/size = Vector2(16, 11) cache/0/16/0/glyphs/80/uv_rect = Rect2(144, 5, 16, 11) cache/0/16/0/glyphs/80/texture_idx = 0 cache/0/16/0/glyphs/79/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/79/offset = Vector2(0, -13) cache/0/16/0/glyphs/79/size = Vector2(5, 14) cache/0/16/0/glyphs/79/uv_rect = Rect2(162, 5, 5, 14) cache/0/16/0/glyphs/79/texture_idx = 0 cache/0/16/0/glyphs/50/advance = Vector2(12.5938, 16.0938) cache/0/16/0/glyphs/50/offset = Vector2(-1, -13) cache/0/16/0/glyphs/50/size = Vector2(14, 14) cache/0/16/0/glyphs/50/uv_rect = Rect2(169, 5, 14, 14) cache/0/16/0/glyphs/50/texture_idx = 0 cache/0/16/0/glyphs/46/advance = Vector2(10.2344, 16.0938) cache/0/16/0/glyphs/46/offset = Vector2(0, -13) cache/0/16/0/glyphs/46/size = Vector2(12, 14) cache/0/16/0/glyphs/46/uv_rect = Rect2(185, 5, 12, 14) cache/0/16/0/glyphs/46/texture_idx = 0 cache/0/16/0/glyphs/41/advance = Vector2(8.51563, 16.0938) cache/0/16/0/glyphs/41/offset = Vector2(0, -13) cache/0/16/0/glyphs/41/size = Vector2(9, 14) cache/0/16/0/glyphs/41/uv_rect = Rect2(199, 5, 9, 14) cache/0/16/0/glyphs/41/texture_idx = 0 cache/0/16/0/glyphs/76/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/76/offset = Vector2(0, -13) cache/0/16/0/glyphs/76/size = Vector2(5, 14) cache/0/16/0/glyphs/76/uv_rect = Rect2(210, 5, 5, 14) cache/0/16/0/glyphs/76/texture_idx = 0 cache/0/16/0/glyphs/87/advance = Vector2(6.29688, 16.0938) cache/0/16/0/glyphs/87/offset = Vector2(-1, -12) cache/0/16/0/glyphs/87/size = Vector2(8, 13) cache/0/16/0/glyphs/87/uv_rect = Rect2(217, 5, 8, 13) cache/0/16/0/glyphs/87/texture_idx = 0 cache/0/16/0/glyphs/38/advance = Vector2(10.1406, 16.0938) cache/0/16/0/glyphs/38/offset = Vector2(-1, -13) cache/0/16/0/glyphs/38/size = Vector2(12, 14) cache/0/16/0/glyphs/38/uv_rect = Rect2(227, 5, 12, 14) cache/0/16/0/glyphs/38/texture_idx = 0 cache/0/16/0/glyphs/86/advance = Vector2(7.79688, 16.0938) cache/0/16/0/glyphs/86/offset = Vector2(-1, -10) cache/0/16/0/glyphs/86/size = Vector2(10, 11) cache/0/16/0/glyphs/86/uv_rect = Rect2(241, 5, 10, 11) cache/0/16/0/glyphs/86/texture_idx = 0 cache/0/16/0/glyphs/29/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/29/offset = Vector2(0, -10) cache/0/16/0/glyphs/29/size = Vector2(5, 11) cache/0/16/0/glyphs/29/uv_rect = Rect2(1, 21, 5, 11) cache/0/16/0/glyphs/29/texture_idx = 0 cache/0/16/0/glyphs/18/advance = Vector2(6.25, 16.0938) cache/0/16/0/glyphs/18/offset = Vector2(-1, -13) cache/0/16/0/glyphs/18/size = Vector2(9, 14) cache/0/16/0/glyphs/18/uv_rect = Rect2(1, 34, 9, 14) cache/0/16/0/glyphs/18/texture_idx = 0 cache/0/16/0/glyphs/39/advance = Vector2(11.75, 16.0938) cache/0/16/0/glyphs/39/offset = Vector2(0, -13) cache/0/16/0/glyphs/39/size = Vector2(12, 14) cache/0/16/0/glyphs/39/uv_rect = Rect2(12, 34, 12, 14) cache/0/16/0/glyphs/39/texture_idx = 0 cache/0/16/0/glyphs/78/advance = Vector2(9.15625, 16.0938) cache/0/16/0/glyphs/78/offset = Vector2(0, -13) cache/0/16/0/glyphs/78/size = Vector2(11, 14) cache/0/16/0/glyphs/78/uv_rect = Rect2(26, 34, 11, 14) cache/0/16/0/glyphs/78/texture_idx = 0 cache/0/16/0/glyphs/51/advance = Vector2(9.84375, 16.0938) cache/0/16/0/glyphs/51/offset = Vector2(0, -13) cache/0/16/0/glyphs/51/size = Vector2(11, 14) cache/0/16/0/glyphs/51/uv_rect = Rect2(39, 34, 11, 14) cache/0/16/0/glyphs/51/texture_idx = 0 cache/0/16/0/glyphs/74/advance = Vector2(8.90625, 16.0938) cache/0/16/0/glyphs/74/offset = Vector2(-1, -10) cache/0/16/0/glyphs/74/size = Vector2(11, 15) cache/0/16/0/glyphs/74/uv_rect = Rect2(1, 50, 11, 15) cache/0/16/0/glyphs/74/texture_idx = 0 cache/0/16/0/glyphs/88/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/88/offset = Vector2(0, -10) cache/0/16/0/glyphs/88/size = Vector2(10, 11) cache/0/16/0/glyphs/88/uv_rect = Rect2(8, 21, 10, 11) cache/0/16/0/glyphs/88/texture_idx = 0 cache/0/16/0/glyphs/89/advance = Vector2(8.5625, 16.0938) cache/0/16/0/glyphs/89/offset = Vector2(-1, -10) cache/0/16/0/glyphs/89/size = Vector2(11, 11) cache/0/16/0/glyphs/89/uv_rect = Rect2(20, 21, 11, 11) cache/0/16/0/glyphs/89/texture_idx = 0 cache/0/16/0/glyphs/35/advance = Vector2(14.375, 16.0938) cache/0/16/0/glyphs/35/offset = Vector2(-1, -13) cache/0/16/0/glyphs/35/size = Vector2(16, 16) cache/0/16/0/glyphs/35/uv_rect = Rect2(1, 67, 16, 16) cache/0/16/0/glyphs/35/texture_idx = 0 cache/0/16/0/glyphs/27/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/27/offset = Vector2(-1, -13) cache/0/16/0/glyphs/27/size = Vector2(11, 14) cache/0/16/0/glyphs/27/uv_rect = Rect2(52, 34, 11, 14) cache/0/16/0/glyphs/27/texture_idx = 0 cache/0/16/0/glyphs/20/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/20/offset = Vector2(0, -13) cache/0/16/0/glyphs/20/size = Vector2(8, 14) cache/0/16/0/glyphs/20/uv_rect = Rect2(65, 34, 8, 14) cache/0/16/0/glyphs/20/texture_idx = 0 cache/0/16/0/glyphs/40/advance = Vector2(8.9375, 16.0938) cache/0/16/0/glyphs/40/offset = Vector2(0, -13) cache/0/16/0/glyphs/40/size = Vector2(9, 14) cache/0/16/0/glyphs/40/uv_rect = Rect2(75, 34, 9, 14) cache/0/16/0/glyphs/40/texture_idx = 0 cache/0/16/0/glyphs/92/advance = Vector2(8.57813, 16.0938) cache/0/16/0/glyphs/92/offset = Vector2(-1, -10) cache/0/16/0/glyphs/92/size = Vector2(11, 15) cache/0/16/0/glyphs/92/uv_rect = Rect2(14, 50, 11, 15) cache/0/16/0/glyphs/92/texture_idx = 0 cache/0/16/0/glyphs/83/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/83/offset = Vector2(0, -10) cache/0/16/0/glyphs/83/size = Vector2(11, 15) cache/0/16/0/glyphs/83/uv_rect = Rect2(27, 50, 11, 15) cache/0/16/0/glyphs/83/texture_idx = 0 cache/0/16/0/glyphs/77/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/77/offset = Vector2(-2, -13) cache/0/16/0/glyphs/77/size = Vector2(7, 18) cache/0/16/0/glyphs/77/uv_rect = Rect2(1, 85, 7, 18) cache/0/16/0/glyphs/77/texture_idx = 0 cache/0/16/0/glyphs/21/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/21/offset = Vector2(-1, -13) cache/0/16/0/glyphs/21/size = Vector2(11, 14) cache/0/16/0/glyphs/21/uv_rect = Rect2(86, 34, 11, 14) cache/0/16/0/glyphs/21/texture_idx = 0 cache/0/16/0/glyphs/16/advance = Vector2(5.15625, 16.0938) cache/0/16/0/glyphs/16/offset = Vector2(-1, -7) cache/0/16/0/glyphs/16/size = Vector2(7, 4) cache/0/16/0/glyphs/16/uv_rect = Rect2(33, 21, 7, 4) cache/0/16/0/glyphs/16/texture_idx = 0 cache/0/16/0/glyphs/47/advance = Vector2(8.67188, 16.0938) cache/0/16/0/glyphs/47/offset = Vector2(0, -13) cache/0/16/0/glyphs/47/size = Vector2(10, 14) cache/0/16/0/glyphs/47/uv_rect = Rect2(99, 34, 10, 14) cache/0/16/0/glyphs/47/texture_idx = 0 cache/0/16/0/glyphs/73/advance = Vector2(5.8125, 16.0938) cache/0/16/0/glyphs/73/offset = Vector2(-1, -13) cache/0/16/0/glyphs/73/size = Vector2(9, 14) cache/0/16/0/glyphs/73/uv_rect = Rect2(111, 34, 9, 14) cache/0/16/0/glyphs/73/texture_idx = 0 cache/0/16/0/glyphs/22/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/22/offset = Vector2(-1, -13) cache/0/16/0/glyphs/22/size = Vector2(11, 14) cache/0/16/0/glyphs/22/uv_rect = Rect2(122, 34, 11, 14) cache/0/16/0/glyphs/22/texture_idx = 0 cache/0/16/0/glyphs/26/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/26/offset = Vector2(-1, -13) cache/0/16/0/glyphs/26/size = Vector2(11, 14) cache/0/16/0/glyphs/26/uv_rect = Rect2(135, 34, 11, 14) cache/0/16/0/glyphs/26/texture_idx = 0 cache/0/16/0/glyphs/25/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/25/offset = Vector2(-1, -13) cache/0/16/0/glyphs/25/size = Vector2(11, 14) cache/0/16/0/glyphs/25/uv_rect = Rect2(148, 34, 11, 14) cache/0/16/0/glyphs/25/texture_idx = 0 cache/0/16/0/glyphs/54/advance = Vector2(8.79688, 16.0938) cache/0/16/0/glyphs/54/offset = Vector2(-1, -13) cache/0/16/0/glyphs/54/size = Vector2(11, 14) cache/0/16/0/glyphs/54/uv_rect = Rect2(161, 34, 11, 14) cache/0/16/0/glyphs/54/texture_idx = 0 cache/0/16/0/glyphs/23/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/23/offset = Vector2(-1, -13) cache/0/16/0/glyphs/23/size = Vector2(11, 14) cache/0/16/0/glyphs/23/uv_rect = Rect2(174, 34, 11, 14) cache/0/16/0/glyphs/23/texture_idx = 0 cache/0/16/0/glyphs/69/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/69/offset = Vector2(0, -13) cache/0/16/0/glyphs/69/size = Vector2(11, 14) cache/0/16/0/glyphs/69/uv_rect = Rect2(187, 34, 11, 14) cache/0/16/0/glyphs/69/texture_idx = 0 cache/0/16/0/glyphs/17/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/17/offset = Vector2(0, -4) cache/0/16/0/glyphs/17/size = Vector2(5, 5) cache/0/16/0/glyphs/17/uv_rect = Rect2(42, 21, 5, 5) cache/0/16/0/glyphs/17/texture_idx = 0 cache/0/16/0/glyphs/918/advance = Vector2(4.89063, 16.0938) cache/0/16/0/glyphs/918/offset = Vector2(0, -13) cache/0/16/0/glyphs/918/size = Vector2(5, 14) cache/0/16/0/glyphs/918/uv_rect = Rect2(200, 34, 5, 14) cache/0/16/0/glyphs/918/texture_idx = 0 cache/0/16/0/glyphs/42/advance = Vector2(11.625, 16.0938) cache/0/16/0/glyphs/42/offset = Vector2(-1, -13) cache/0/16/0/glyphs/42/size = Vector2(13, 14) cache/0/16/0/glyphs/42/uv_rect = Rect2(207, 34, 13, 14) cache/0/16/0/glyphs/42/texture_idx = 0 cache/0/16/0/glyphs/55/advance = Vector2(9.0625, 16.0938) cache/0/16/0/glyphs/55/offset = Vector2(-1, -13) cache/0/16/0/glyphs/55/size = Vector2(11, 14) cache/0/16/0/glyphs/55/uv_rect = Rect2(222, 34, 11, 14) cache/0/16/0/glyphs/55/texture_idx = 0 cache/0/16/0/glyphs/49/advance = Vector2(12.5313, 16.0938) cache/0/16/0/glyphs/49/offset = Vector2(0, -13) cache/0/16/0/glyphs/49/size = Vector2(13, 14) cache/0/16/0/glyphs/49/uv_rect = Rect2(235, 34, 13, 14) cache/0/16/0/glyphs/49/texture_idx = 0 cache/0/16/0/glyphs/36/advance = Vector2(10.5781, 16.0938) cache/0/16/0/glyphs/36/offset = Vector2(-1, -13) cache/0/16/0/glyphs/36/size = Vector2(13, 14) cache/0/16/0/glyphs/36/uv_rect = Rect2(40, 50, 13, 14) cache/0/16/0/glyphs/36/texture_idx = 0 cache/0/16/0/glyphs/90/advance = Vector2(13.0781, 16.0938) cache/0/16/0/glyphs/90/offset = Vector2(-1, -10) cache/0/16/0/glyphs/90/size = Vector2(15, 11) cache/0/16/0/glyphs/90/uv_rect = Rect2(49, 21, 15, 11) cache/0/16/0/glyphs/90/texture_idx = 0 cache/0/16/0/glyphs/57/advance = Vector2(9.95313, 16.0938) cache/0/16/0/glyphs/57/offset = Vector2(-1, -13) cache/0/16/0/glyphs/57/size = Vector2(12, 14) cache/0/16/0/glyphs/57/uv_rect = Rect2(55, 50, 12, 14) cache/0/16/0/glyphs/57/texture_idx = 0 cache/0/16/0/glyphs/53/advance = Vector2(10.2344, 16.0938) cache/0/16/0/glyphs/53/offset = Vector2(0, -13) cache/0/16/0/glyphs/53/size = Vector2(12, 14) cache/0/16/0/glyphs/53/uv_rect = Rect2(69, 50, 12, 14) cache/0/16/0/glyphs/53/texture_idx = 0 cache/0/16/0/glyphs/93/advance = Vector2(7.65625, 16.0938) cache/0/16/0/glyphs/93/offset = Vector2(-1, -10) cache/0/16/0/glyphs/93/size = Vector2(10, 11) cache/0/16/0/glyphs/93/uv_rect = Rect2(66, 21, 10, 11) cache/0/16/0/glyphs/93/texture_idx = 0 cache/0/16/0/glyphs/75/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/75/offset = Vector2(0, -13) cache/0/16/0/glyphs/75/size = Vector2(10, 14) cache/0/16/0/glyphs/75/uv_rect = Rect2(83, 50, 10, 14) cache/0/16/0/glyphs/75/texture_idx = 0 cache/0/16/0/glyphs/5/advance = Vector2(6.98438, 16.0938) cache/0/16/0/glyphs/5/offset = Vector2(0, -13) cache/0/16/0/glyphs/5/size = Vector2(7, 7) cache/0/16/0/glyphs/5/uv_rect = Rect2(78, 21, 7, 7) cache/0/16/0/glyphs/5/texture_idx = 0 cache/0/16/0/glyphs/66/advance = Vector2(6.875, 16.0938) cache/0/16/0/glyphs/66/offset = Vector2(-2, 0) cache/0/16/0/glyphs/66/size = Vector2(10, 4) cache/0/16/0/glyphs/66/uv_rect = Rect2(87, 21, 10, 4) cache/0/16/0/glyphs/66/texture_idx = 0 cache/0/16/0/glyphs/33/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/33/offset = Vector2(-1, -12) cache/0/16/0/glyphs/33/size = Vector2(11, 12) cache/0/16/0/glyphs/33/uv_rect = Rect2(95, 50, 11, 12) cache/0/16/0/glyphs/33/texture_idx = 0 cache/0/16/0/glyphs/564/advance = Vector2(10.2813, 16.0938) cache/0/16/0/glyphs/564/offset = Vector2(-1, -13) cache/0/16/0/glyphs/564/size = Vector2(12, 14) cache/0/16/0/glyphs/564/uv_rect = Rect2(108, 50, 12, 14) cache/0/16/0/glyphs/564/texture_idx = 0 cache/0/16/0/glyphs/56/advance = Vector2(11.875, 16.0938) cache/0/16/0/glyphs/56/offset = Vector2(0, -13) cache/0/16/0/glyphs/56/size = Vector2(12, 14) cache/0/16/0/glyphs/56/uv_rect = Rect2(122, 50, 12, 14) cache/0/16/0/glyphs/56/texture_idx = 0 cache/0/16/0/glyphs/11/advance = Vector2(5.07813, 16.0938) cache/0/16/0/glyphs/11/offset = Vector2(-1, -13) cache/0/16/0/glyphs/11/size = Vector2(7, 17) cache/0/16/0/glyphs/11/uv_rect = Rect2(10, 85, 7, 17) cache/0/16/0/glyphs/11/texture_idx = 0 cache/0/16/0/glyphs/12/advance = Vector2(5.07813, 16.0938) cache/0/16/0/glyphs/12/offset = Vector2(-1, -13) cache/0/16/0/glyphs/12/size = Vector2(7, 17) cache/0/16/0/glyphs/12/uv_rect = Rect2(19, 85, 7, 17) cache/0/16/0/glyphs/12/texture_idx = 0 cache/0/16/0/glyphs/15/advance = Vector2(4.28125, 16.0938) cache/0/16/0/glyphs/15/offset = Vector2(-1, -4) cache/0/16/0/glyphs/15/size = Vector2(6, 7) cache/0/16/0/glyphs/15/uv_rect = Rect2(99, 21, 6, 7) cache/0/16/0/glyphs/15/texture_idx = 0 cache/0/16/0/glyphs/605/advance = Vector2(16.0781, 16.0938) cache/0/16/0/glyphs/605/offset = Vector2(-1, -13) cache/0/16/0/glyphs/605/size = Vector2(17, 14) cache/0/16/0/glyphs/605/uv_rect = Rect2(136, 50, 17, 14) cache/0/16/0/glyphs/605/texture_idx = 0 cache/0/16/0/glyphs/60/advance = Vector2(9.46875, 16.0938) cache/0/16/0/glyphs/60/offset = Vector2(-1, -13) cache/0/16/0/glyphs/60/size = Vector2(12, 14) cache/0/16/0/glyphs/60/uv_rect = Rect2(155, 50, 12, 14) cache/0/16/0/glyphs/60/texture_idx = 0 cache/0/16/0/glyphs/8/advance = Vector2(13.7969, 16.0938) cache/0/16/0/glyphs/8/offset = Vector2(-1, -13) cache/0/16/0/glyphs/8/size = Vector2(16, 14) cache/0/16/0/glyphs/8/uv_rect = Rect2(169, 50, 16, 14) cache/0/16/0/glyphs/8/texture_idx = 0 cache/0/16/0/glyphs/37/advance = Vector2(10.5625, 16.0938) cache/0/16/0/glyphs/37/offset = Vector2(0, -13) cache/0/16/0/glyphs/37/size = Vector2(11, 14) cache/0/16/0/glyphs/37/uv_rect = Rect2(187, 50, 11, 14) cache/0/16/0/glyphs/37/texture_idx = 0 cache/0/16/0/glyphs/58/advance = Vector2(15.1406, 16.0938) cache/0/16/0/glyphs/58/offset = Vector2(-1, -13) cache/0/16/0/glyphs/58/size = Vector2(18, 14) cache/0/16/0/glyphs/58/uv_rect = Rect2(200, 50, 18, 14) cache/0/16/0/glyphs/58/texture_idx = 0 cache/0/16/0/glyphs/6/advance = Vector2(10.3438, 16.0938) cache/0/16/0/glyphs/6/offset = Vector2(-1, -13) cache/0/16/0/glyphs/6/size = Vector2(12, 14) cache/0/16/0/glyphs/6/uv_rect = Rect2(220, 50, 12, 14) cache/0/16/0/glyphs/6/texture_idx = 0 cache/0/16/0/glyphs/24/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/24/offset = Vector2(-1, -13) cache/0/16/0/glyphs/24/size = Vector2(11, 14) cache/0/16/0/glyphs/24/uv_rect = Rect2(234, 50, 11, 14) cache/0/16/0/glyphs/24/texture_idx = 0 cache/0/16/0/glyphs/153/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/153/offset = Vector2(0, -11) cache/0/16/0/glyphs/153/size = Vector2(10, 10) cache/0/16/0/glyphs/153/uv_rect = Rect2(107, 21, 10, 10) cache/0/16/0/glyphs/153/texture_idx = 0 cache/0/16/0/glyphs/28/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/28/offset = Vector2(-1, -13) cache/0/16/0/glyphs/28/size = Vector2(11, 14) cache/0/16/0/glyphs/28/uv_rect = Rect2(19, 67, 11, 14) cache/0/16/0/glyphs/28/texture_idx = 0 cache/0/16/0/glyphs/31/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/31/offset = Vector2(-1, -12) cache/0/16/0/glyphs/31/size = Vector2(11, 12) cache/0/16/0/glyphs/31/uv_rect = Rect2(32, 67, 11, 12) cache/0/16/0/glyphs/31/texture_idx = 0 cache/0/16/0/glyphs/62/advance = Vector2(5.28125, 16.0938) cache/0/16/0/glyphs/62/offset = Vector2(0, -13) cache/0/16/0/glyphs/62/size = Vector2(6, 17) cache/0/16/0/glyphs/62/uv_rect = Rect2(28, 85, 6, 17) cache/0/16/0/glyphs/62/texture_idx = 0 cache/0/16/0/glyphs/64/advance = Vector2(5.28125, 16.0938) cache/0/16/0/glyphs/64/offset = Vector2(-1, -13) cache/0/16/0/glyphs/64/size = Vector2(7, 17) cache/0/16/0/glyphs/64/uv_rect = Rect2(36, 85, 7, 17) cache/0/16/0/glyphs/64/texture_idx = 0 cache/0/16/0/glyphs/909/advance = Vector2(11.625, 16.0938) cache/0/16/0/glyphs/909/offset = Vector2(-1, -13) cache/0/16/0/glyphs/909/size = Vector2(15, 14) cache/0/16/0/glyphs/909/uv_rect = Rect2(45, 67, 15, 14) cache/0/16/0/glyphs/909/texture_idx = 0 cache/0/16/0/glyphs/13/advance = Vector2(8.76563, 16.0938) cache/0/16/0/glyphs/13/offset = Vector2(-1, -13) cache/0/16/0/glyphs/13/size = Vector2(11, 10) cache/0/16/0/glyphs/13/uv_rect = Rect2(119, 21, 11, 10) cache/0/16/0/glyphs/13/texture_idx = 0 cache/0/16/0/glyphs/10/advance = Vector2(3.89063, 16.0938) cache/0/16/0/glyphs/10/offset = Vector2(0, -13) cache/0/16/0/glyphs/10/size = Vector2(4, 7) cache/0/16/0/glyphs/10/uv_rect = Rect2(132, 21, 4, 7) cache/0/16/0/glyphs/10/texture_idx = 0 cache/0/16/0/glyphs/32/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/32/offset = Vector2(-1, -10) cache/0/16/0/glyphs/32/size = Vector2(11, 8) cache/0/16/0/glyphs/32/uv_rect = Rect2(138, 21, 11, 8) cache/0/16/0/glyphs/32/texture_idx = 0 cache/0/16/0/glyphs/52/advance = Vector2(12.5938, 16.0938) cache/0/16/0/glyphs/52/offset = Vector2(-1, -13) cache/0/16/0/glyphs/52/size = Vector2(14, 17) cache/0/16/0/glyphs/52/uv_rect = Rect2(45, 85, 14, 17) cache/0/16/0/glyphs/52/texture_idx = 0 cache/0/16/0/glyphs/84/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/84/offset = Vector2(-1, -10) cache/0/16/0/glyphs/84/size = Vector2(11, 15) cache/0/16/0/glyphs/84/uv_rect = Rect2(62, 67, 11, 15) cache/0/16/0/glyphs/84/texture_idx = 0 cache/0/16/0/glyphs/94/advance = Vector2(6.1875, 16.0938) cache/0/16/0/glyphs/94/offset = Vector2(-1, -13) cache/0/16/0/glyphs/94/size = Vector2(8, 17) cache/0/16/0/glyphs/94/uv_rect = Rect2(61, 85, 8, 17) cache/0/16/0/glyphs/94/texture_idx = 0 cache/0/16/0/glyphs/96/advance = Vector2(5.9375, 16.0938) cache/0/16/0/glyphs/96/offset = Vector2(-1, -13) cache/0/16/0/glyphs/96/size = Vector2(8, 17) cache/0/16/0/glyphs/96/uv_rect = Rect2(71, 85, 8, 17) cache/0/16/0/glyphs/96/texture_idx = 0 cache/0/16/0/glyphs/565/advance = Vector2(10.2813, 16.0938) cache/0/16/0/glyphs/565/offset = Vector2(-1, -13) cache/0/16/0/glyphs/565/size = Vector2(11, 14) cache/0/16/0/glyphs/565/uv_rect = Rect2(75, 67, 11, 14) cache/0/16/0/glyphs/565/texture_idx = 0 cache/0/16/0/glyphs/606/advance = Vector2(16.0781, 16.0938) cache/0/16/0/glyphs/606/offset = Vector2(-1, -13) cache/0/16/0/glyphs/606/size = Vector2(17, 14) cache/0/16/0/glyphs/606/uv_rect = Rect2(88, 67, 17, 14) cache/0/16/0/glyphs/606/texture_idx = 0 cache/0/16/0/glyphs/45/advance = Vector2(4.78125, 16.0938) cache/0/16/0/glyphs/45/offset = Vector2(-3, -13) cache/0/16/0/glyphs/45/size = Vector2(8, 17) cache/0/16/0/glyphs/45/uv_rect = Rect2(81, 85, 8, 17) cache/0/16/0/glyphs/45/texture_idx = 0 cache/0/16/0/glyphs/97/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/97/offset = Vector2(-1, -9) cache/0/16/0/glyphs/97/size = Vector2(11, 6) cache/0/16/0/glyphs/97/uv_rect = Rect2(151, 21, 11, 6) cache/0/16/0/glyphs/97/texture_idx = 0 cache/0/16/0/glyphs/95/advance = Vector2(8.8125, 16.0938) cache/0/16/0/glyphs/95/offset = Vector2(2, -13) cache/0/16/0/glyphs/95/size = Vector2(5, 18) cache/0/16/0/glyphs/95/uv_rect = Rect2(91, 85, 5, 18) cache/0/16/0/glyphs/95/texture_idx = 0 cache/0/16/0/glyphs/59/advance = Vector2(9.95313, 16.0938) cache/0/16/0/glyphs/59/offset = Vector2(-1, -13) cache/0/16/0/glyphs/59/size = Vector2(12, 14) cache/0/16/0/glyphs/59/uv_rect = Rect2(107, 67, 12, 14) cache/0/16/0/glyphs/59/texture_idx = 0 cache/0/16/0/glyphs/61/advance = Vector2(9.20313, 16.0938) cache/0/16/0/glyphs/61/offset = Vector2(-1, -13) cache/0/16/0/glyphs/61/size = Vector2(11, 14) cache/0/16/0/glyphs/61/uv_rect = Rect2(121, 67, 11, 14) cache/0/16/0/glyphs/61/texture_idx = 0 cache/0/16/0/glyphs/4/advance = Vector2(4.42188, 16.0938) cache/0/16/0/glyphs/4/offset = Vector2(0, -13) cache/0/16/0/glyphs/4/size = Vector2(5, 14) cache/0/16/0/glyphs/4/uv_rect = Rect2(250, 34, 5, 14) cache/0/16/0/glyphs/4/texture_idx = 0 cache/0/16/0/glyphs/67/advance = Vector2(9.46875, 16.0938) cache/0/16/0/glyphs/67/offset = Vector2(1, -14) cache/0/16/0/glyphs/67/size = Vector2(7, 5) cache/0/16/0/glyphs/67/uv_rect = Rect2(164, 21, 7, 5) cache/0/16/0/glyphs/67/texture_idx = 0 cache/0/16/0/glyphs/63/advance = Vector2(6.25, 16.0938) cache/0/16/0/glyphs/63/offset = Vector2(-1, -13) cache/0/16/0/glyphs/63/size = Vector2(9, 14) cache/0/16/0/glyphs/63/uv_rect = Rect2(134, 67, 9, 14) cache/0/16/0/glyphs/63/texture_idx = 0 cache/0/16/0/glyphs/7/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/7/offset = Vector2(-1, -13) cache/0/16/0/glyphs/7/size = Vector2(11, 16) cache/0/16/0/glyphs/7/uv_rect = Rect2(145, 67, 11, 16) cache/0/16/0/glyphs/7/texture_idx = 0 cache/0/16/0/glyphs/14/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/14/offset = Vector2(-1, -11) cache/0/16/0/glyphs/14/size = Vector2(11, 10) cache/0/16/0/glyphs/14/uv_rect = Rect2(173, 21, 11, 10) cache/0/16/0/glyphs/14/texture_idx = 0 cache/0/16/0/glyphs/268435528/advance = Vector2(9.21875, 16.0938) cache/0/16/0/glyphs/268435528/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435528/size = Vector2(10, 11) cache/0/16/0/glyphs/268435528/uv_rect = Rect2(186, 21, 10, 11) cache/0/16/0/glyphs/268435528/texture_idx = 0 cache/0/16/0/glyphs/402653274/advance = Vector2(13.0781, 16.0938) cache/0/16/0/glyphs/402653274/offset = Vector2(-1, -10) cache/0/16/0/glyphs/402653274/size = Vector2(16, 11) cache/0/16/0/glyphs/402653274/uv_rect = Rect2(198, 21, 16, 11) cache/0/16/0/glyphs/402653274/texture_idx = 0 cache/0/16/0/glyphs/134217796/advance = Vector2(9.28125, 16.0938) cache/0/16/0/glyphs/134217796/offset = Vector2(-1, -10) cache/0/16/0/glyphs/134217796/size = Vector2(11, 11) cache/0/16/0/glyphs/134217796/uv_rect = Rect2(216, 21, 11, 11) cache/0/16/0/glyphs/134217796/texture_idx = 0 cache/0/16/0/glyphs/268435537/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/268435537/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435537/size = Vector2(11, 11) cache/0/16/0/glyphs/268435537/uv_rect = Rect2(229, 21, 11, 11) cache/0/16/0/glyphs/268435537/texture_idx = 0 cache/0/16/0/glyphs/402653262/advance = Vector2(9.15625, 16.0938) cache/0/16/0/glyphs/402653262/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653262/size = Vector2(10, 14) cache/0/16/0/glyphs/402653262/uv_rect = Rect2(158, 67, 10, 14) cache/0/16/0/glyphs/402653262/texture_idx = 0 cache/0/16/0/glyphs/402653238/advance = Vector2(8.79688, 16.0938) cache/0/16/0/glyphs/402653238/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653238/size = Vector2(10, 14) cache/0/16/0/glyphs/402653238/uv_rect = Rect2(170, 67, 10, 14) cache/0/16/0/glyphs/402653238/texture_idx = 0 cache/0/16/0/glyphs/402653254/advance = Vector2(7.92188, 16.0938) cache/0/16/0/glyphs/402653254/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653254/size = Vector2(10, 11) cache/0/16/0/glyphs/402653254/uv_rect = Rect2(242, 21, 10, 11) cache/0/16/0/glyphs/402653254/texture_idx = 0 cache/0/16/0/glyphs/268435538/advance = Vector2(9.78125, 16.0938) cache/0/16/0/glyphs/268435538/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435538/size = Vector2(11, 11) cache/0/16/0/glyphs/268435538/uv_rect = Rect2(182, 67, 11, 11) cache/0/16/0/glyphs/268435538/texture_idx = 0 cache/0/16/0/glyphs/134217813/advance = Vector2(6.90625, 16.0938) cache/0/16/0/glyphs/134217813/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217813/size = Vector2(8, 11) cache/0/16/0/glyphs/134217813/uv_rect = Rect2(247, 50, 8, 11) cache/0/16/0/glyphs/134217813/texture_idx = 0 cache/0/16/0/glyphs/134217800/advance = Vector2(9.21875, 16.0938) cache/0/16/0/glyphs/134217800/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217800/size = Vector2(10, 11) cache/0/16/0/glyphs/134217800/uv_rect = Rect2(195, 67, 10, 11) cache/0/16/0/glyphs/134217800/texture_idx = 0 cache/0/16/0/glyphs/268435512/advance = Vector2(11.875, 16.0938) cache/0/16/0/glyphs/268435512/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435512/size = Vector2(12, 14) cache/0/16/0/glyphs/268435512/uv_rect = Rect2(207, 67, 12, 14) cache/0/16/0/glyphs/268435512/texture_idx = 0 cache/0/16/0/glyphs/134217814/advance = Vector2(7.79688, 16.0938) cache/0/16/0/glyphs/134217814/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217814/size = Vector2(9, 11) cache/0/16/0/glyphs/134217814/uv_rect = Rect2(221, 67, 9, 11) cache/0/16/0/glyphs/134217814/texture_idx = 0 cache/0/16/0/glyphs/268435543/advance = Vector2(6.29688, 16.0938) cache/0/16/0/glyphs/268435543/offset = Vector2(-1, -12) cache/0/16/0/glyphs/268435543/size = Vector2(9, 13) cache/0/16/0/glyphs/268435543/uv_rect = Rect2(232, 67, 9, 13) cache/0/16/0/glyphs/268435543/texture_idx = 0 cache/0/16/0/glyphs/402653256/advance = Vector2(9.21875, 16.0938) cache/0/16/0/glyphs/402653256/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653256/size = Vector2(11, 11) cache/0/16/0/glyphs/402653256/uv_rect = Rect2(243, 67, 11, 11) cache/0/16/0/glyphs/402653256/texture_idx = 0 cache/0/16/0/glyphs/134217807/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/134217807/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217807/size = Vector2(5, 14) cache/0/16/0/glyphs/134217807/uv_rect = Rect2(98, 85, 5, 14) cache/0/16/0/glyphs/134217807/texture_idx = 0 cache/0/16/0/glyphs/402653266/advance = Vector2(9.78125, 16.0938) cache/0/16/0/glyphs/402653266/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653266/size = Vector2(11, 11) cache/0/16/0/glyphs/402653266/uv_rect = Rect2(105, 85, 11, 11) cache/0/16/0/glyphs/402653266/texture_idx = 0 cache/0/16/0/glyphs/268435525/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/268435525/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435525/size = Vector2(11, 14) cache/0/16/0/glyphs/268435525/uv_rect = Rect2(118, 85, 11, 14) cache/0/16/0/glyphs/268435525/texture_idx = 0 cache/0/16/0/glyphs/268435548/advance = Vector2(8.57813, 16.0938) cache/0/16/0/glyphs/268435548/offset = Vector2(-1, -10) cache/0/16/0/glyphs/268435548/size = Vector2(12, 15) cache/0/16/0/glyphs/268435548/uv_rect = Rect2(131, 85, 12, 15) cache/0/16/0/glyphs/268435548/texture_idx = 0 cache/0/16/0/glyphs/268435529/advance = Vector2(5.8125, 16.0938) cache/0/16/0/glyphs/268435529/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435529/size = Vector2(10, 14) cache/0/16/0/glyphs/268435529/uv_rect = Rect2(145, 85, 10, 14) cache/0/16/0/glyphs/268435529/texture_idx = 0 cache/0/16/0/glyphs/268435542/advance = Vector2(7.79688, 16.0938) cache/0/16/0/glyphs/268435542/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435542/size = Vector2(9, 11) cache/0/16/0/glyphs/268435542/uv_rect = Rect2(157, 85, 9, 11) cache/0/16/0/glyphs/268435542/texture_idx = 0 cache/0/16/0/glyphs/134217803/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/134217803/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217803/size = Vector2(11, 14) cache/0/16/0/glyphs/134217803/uv_rect = Rect2(168, 85, 11, 14) cache/0/16/0/glyphs/134217803/texture_idx = 0 cache/0/16/0/glyphs/402653213/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/402653213/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653213/size = Vector2(6, 11) cache/0/16/0/glyphs/402653213/uv_rect = Rect2(181, 85, 6, 11) cache/0/16/0/glyphs/402653213/texture_idx = 0 cache/0/16/0/glyphs/268435526/advance = Vector2(7.92188, 16.0938) cache/0/16/0/glyphs/268435526/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435526/size = Vector2(9, 11) cache/0/16/0/glyphs/268435526/uv_rect = Rect2(189, 85, 9, 11) cache/0/16/0/glyphs/268435526/texture_idx = 0 cache/0/16/0/glyphs/268435531/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/268435531/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435531/size = Vector2(11, 14) cache/0/16/0/glyphs/268435531/uv_rect = Rect2(200, 85, 11, 14) cache/0/16/0/glyphs/268435531/texture_idx = 0 cache/0/16/0/glyphs/268435532/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/268435532/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435532/size = Vector2(5, 14) cache/0/16/0/glyphs/268435532/uv_rect = Rect2(213, 85, 5, 14) cache/0/16/0/glyphs/268435532/texture_idx = 0 cache/0/16/0/glyphs/134217817/advance = Vector2(8.5625, 16.0938) cache/0/16/0/glyphs/134217817/offset = Vector2(-1, -10) cache/0/16/0/glyphs/134217817/size = Vector2(11, 11) cache/0/16/0/glyphs/134217817/uv_rect = Rect2(220, 85, 11, 11) cache/0/16/0/glyphs/134217817/texture_idx = 0 cache/0/16/0/glyphs/402653271/advance = Vector2(6.29688, 16.0938) cache/0/16/0/glyphs/402653271/offset = Vector2(0, -12) cache/0/16/0/glyphs/402653271/size = Vector2(8, 13) cache/0/16/0/glyphs/402653271/uv_rect = Rect2(233, 85, 8, 13) cache/0/16/0/glyphs/402653271/texture_idx = 0 cache/0/16/0/glyphs/134217799/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/134217799/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217799/size = Vector2(10, 14) cache/0/16/0/glyphs/134217799/uv_rect = Rect2(243, 85, 10, 14) cache/0/16/0/glyphs/134217799/texture_idx = 0 cache/0/16/0/glyphs/134217794/advance = Vector2(6.875, 16.0938) cache/0/16/0/glyphs/134217794/offset = Vector2(-1, 0) cache/0/16/0/glyphs/134217794/size = Vector2(10, 4) cache/0/16/0/glyphs/134217794/uv_rect = Rect2(1, 105, 10, 4) cache/0/16/0/glyphs/134217794/texture_idx = 0 cache/0/16/0/glyphs/268435541/advance = Vector2(6.90625, 16.0938) cache/0/16/0/glyphs/268435541/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435541/size = Vector2(9, 11) cache/0/16/0/glyphs/268435541/uv_rect = Rect2(1, 111, 9, 11) cache/0/16/0/glyphs/268435541/texture_idx = 0 cache/0/16/0/glyphs/402653250/advance = Vector2(6.875, 16.0938) cache/0/16/0/glyphs/402653250/offset = Vector2(-1, 0) cache/0/16/0/glyphs/402653250/size = Vector2(10, 4) cache/0/16/0/glyphs/402653250/uv_rect = Rect2(13, 105, 10, 4) cache/0/16/0/glyphs/402653250/texture_idx = 0 cache/0/16/0/glyphs/268435476/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/268435476/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435476/size = Vector2(8, 14) cache/0/16/0/glyphs/268435476/uv_rect = Rect2(1, 124, 8, 14) cache/0/16/0/glyphs/268435476/texture_idx = 0 cache/0/16/0/glyphs/402653205/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/402653205/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653205/size = Vector2(11, 14) cache/0/16/0/glyphs/402653205/uv_rect = Rect2(11, 124, 11, 14) cache/0/16/0/glyphs/402653205/texture_idx = 0 cache/0/16/0/glyphs/402653206/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/402653206/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653206/size = Vector2(11, 14) cache/0/16/0/glyphs/402653206/uv_rect = Rect2(24, 124, 11, 14) cache/0/16/0/glyphs/402653206/texture_idx = 0 cache/0/16/0/glyphs/134217752/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/134217752/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217752/size = Vector2(10, 14) cache/0/16/0/glyphs/134217752/uv_rect = Rect2(37, 124, 10, 14) cache/0/16/0/glyphs/134217752/texture_idx = 0 cache/0/16/0/glyphs/134217753/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/134217753/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217753/size = Vector2(11, 14) cache/0/16/0/glyphs/134217753/uv_rect = Rect2(49, 124, 11, 14) cache/0/16/0/glyphs/134217753/texture_idx = 0 cache/0/16/0/glyphs/268435482/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/268435482/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435482/size = Vector2(10, 14) cache/0/16/0/glyphs/268435482/uv_rect = Rect2(62, 124, 10, 14) cache/0/16/0/glyphs/268435482/texture_idx = 0 cache/0/16/0/glyphs/268435483/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/268435483/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435483/size = Vector2(10, 14) cache/0/16/0/glyphs/268435483/uv_rect = Rect2(74, 124, 10, 14) cache/0/16/0/glyphs/268435483/texture_idx = 0 cache/0/16/0/glyphs/402653212/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/402653212/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653212/size = Vector2(11, 14) cache/0/16/0/glyphs/402653212/uv_rect = Rect2(86, 124, 11, 14) cache/0/16/0/glyphs/402653212/texture_idx = 0 cache/0/16/0/glyphs/402653260/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/402653260/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653260/size = Vector2(6, 14) cache/0/16/0/glyphs/402653260/uv_rect = Rect2(99, 124, 6, 14) cache/0/16/0/glyphs/402653260/texture_idx = 0 cache/0/16/0/glyphs/134217808/advance = Vector2(15.2969, 16.0938) cache/0/16/0/glyphs/134217808/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217808/size = Vector2(16, 11) cache/0/16/0/glyphs/134217808/uv_rect = Rect2(12, 111, 16, 11) cache/0/16/0/glyphs/134217808/texture_idx = 0 cache/0/16/0/glyphs/402653265/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/402653265/offset = Vector2(1, -10) cache/0/16/0/glyphs/402653265/size = Vector2(10, 11) cache/0/16/0/glyphs/402653265/uv_rect = Rect2(30, 111, 10, 11) cache/0/16/0/glyphs/402653265/texture_idx = 0 cache/0/16/0/glyphs/134217731/advance = Vector2(4.15625, 16.0938) cache/0/16/0/glyphs/134217731/offset = Vector2(-1, -1) cache/0/16/0/glyphs/134217731/size = Vector2(2, 2) cache/0/16/0/glyphs/134217731/uv_rect = Rect2(5, 1, 2, 2) cache/0/16/0/glyphs/134217731/texture_idx = 0 cache/0/16/0/glyphs/268435535/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/268435535/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435535/size = Vector2(5, 14) cache/0/16/0/glyphs/268435535/uv_rect = Rect2(107, 124, 5, 14) cache/0/16/0/glyphs/268435535/texture_idx = 0 cache/0/16/0/glyphs/268435459/advance = Vector2(4.15625, 16.0938) cache/0/16/0/glyphs/268435459/offset = Vector2(-1, -1) cache/0/16/0/glyphs/268435459/size = Vector2(2, 2) cache/0/16/0/glyphs/268435459/uv_rect = Rect2(9, 1, 2, 2) cache/0/16/0/glyphs/268435459/texture_idx = 0 cache/0/16/0/glyphs/402653239/advance = Vector2(9.0625, 16.0938) cache/0/16/0/glyphs/402653239/offset = Vector2(-1, -13) cache/0/16/0/glyphs/402653239/size = Vector2(12, 14) cache/0/16/0/glyphs/402653239/uv_rect = Rect2(114, 124, 12, 14) cache/0/16/0/glyphs/402653239/texture_idx = 0 cache/0/16/0/glyphs/402653187/advance = Vector2(4.15625, 16.0938) cache/0/16/0/glyphs/402653187/offset = Vector2(-1, -1) cache/0/16/0/glyphs/402653187/size = Vector2(2, 2) cache/0/16/0/glyphs/402653187/uv_rect = Rect2(13, 1, 2, 2) cache/0/16/0/glyphs/402653187/texture_idx = 0 cache/0/16/0/glyphs/402653241/advance = Vector2(9.95313, 16.0938) cache/0/16/0/glyphs/402653241/offset = Vector2(-1, -13) cache/0/16/0/glyphs/402653241/size = Vector2(13, 14) cache/0/16/0/glyphs/402653241/uv_rect = Rect2(128, 124, 13, 14) cache/0/16/0/glyphs/402653241/texture_idx = 0 cache/0/16/0/glyphs/134217757/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/134217757/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217757/size = Vector2(5, 11) cache/0/16/0/glyphs/134217757/uv_rect = Rect2(42, 111, 5, 11) cache/0/16/0/glyphs/134217757/texture_idx = 0 cache/0/16/0/glyphs/402653244/advance = Vector2(9.46875, 16.0938) cache/0/16/0/glyphs/402653244/offset = Vector2(-1, -13) cache/0/16/0/glyphs/402653244/size = Vector2(13, 14) cache/0/16/0/glyphs/402653244/uv_rect = Rect2(143, 124, 13, 14) cache/0/16/0/glyphs/402653244/texture_idx = 0 cache/0/16/0/glyphs/402653240/advance = Vector2(11.875, 16.0938) cache/0/16/0/glyphs/402653240/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653240/size = Vector2(12, 14) cache/0/16/0/glyphs/402653240/uv_rect = Rect2(158, 124, 12, 14) cache/0/16/0/glyphs/402653240/texture_idx = 0 cache/0/16/0/glyphs/134217810/advance = Vector2(9.78125, 16.0938) cache/0/16/0/glyphs/134217810/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217810/size = Vector2(11, 11) cache/0/16/0/glyphs/134217810/uv_rect = Rect2(49, 111, 11, 11) cache/0/16/0/glyphs/134217810/texture_idx = 0 cache/0/16/0/glyphs/134217798/advance = Vector2(7.92188, 16.0938) cache/0/16/0/glyphs/134217798/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217798/size = Vector2(9, 11) cache/0/16/0/glyphs/134217798/uv_rect = Rect2(62, 111, 9, 11) cache/0/16/0/glyphs/134217798/texture_idx = 0 cache/0/16/0/glyphs/268435527/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/268435527/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435527/size = Vector2(11, 14) cache/0/16/0/glyphs/268435527/uv_rect = Rect2(172, 124, 11, 14) cache/0/16/0/glyphs/268435527/texture_idx = 0 cache/0/16/0/glyphs/268435485/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/268435485/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435485/size = Vector2(5, 11) cache/0/16/0/glyphs/268435485/uv_rect = Rect2(73, 111, 5, 11) cache/0/16/0/glyphs/268435485/texture_idx = 0 cache/0/16/0/glyphs/402653270/advance = Vector2(7.79688, 16.0938) cache/0/16/0/glyphs/402653270/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653270/size = Vector2(9, 11) cache/0/16/0/glyphs/402653270/uv_rect = Rect2(80, 111, 9, 11) cache/0/16/0/glyphs/402653270/texture_idx = 0 cache/0/16/0/glyphs/268435534/advance = Vector2(9.15625, 16.0938) cache/0/16/0/glyphs/268435534/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435534/size = Vector2(11, 14) cache/0/16/0/glyphs/268435534/uv_rect = Rect2(185, 124, 11, 14) cache/0/16/0/glyphs/268435534/texture_idx = 0 cache/0/16/0/glyphs/402653255/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/402653255/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653255/size = Vector2(11, 14) cache/0/16/0/glyphs/402653255/uv_rect = Rect2(198, 124, 11, 14) cache/0/16/0/glyphs/402653255/texture_idx = 0 cache/0/16/0/glyphs/268435524/advance = Vector2(9.28125, 16.0938) cache/0/16/0/glyphs/268435524/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435524/size = Vector2(10, 11) cache/0/16/0/glyphs/268435524/uv_rect = Rect2(91, 111, 10, 11) cache/0/16/0/glyphs/268435524/texture_idx = 0 cache/0/16/0/glyphs/402653264/advance = Vector2(15.2969, 16.0938) cache/0/16/0/glyphs/402653264/offset = Vector2(1, -10) cache/0/16/0/glyphs/402653264/size = Vector2(15, 11) cache/0/16/0/glyphs/402653264/uv_rect = Rect2(103, 111, 15, 11) cache/0/16/0/glyphs/402653264/texture_idx = 0 cache/0/16/0/glyphs/134217806/advance = Vector2(9.15625, 16.0938) cache/0/16/0/glyphs/134217806/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217806/size = Vector2(11, 14) cache/0/16/0/glyphs/134217806/uv_rect = Rect2(211, 124, 11, 14) cache/0/16/0/glyphs/134217806/texture_idx = 0 cache/0/16/0/glyphs/402653233/advance = Vector2(12.5313, 16.0938) cache/0/16/0/glyphs/402653233/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653233/size = Vector2(12, 14) cache/0/16/0/glyphs/402653233/uv_rect = Rect2(224, 124, 12, 14) cache/0/16/0/glyphs/402653233/texture_idx = 0 cache/0/16/0/glyphs/268435536/advance = Vector2(15.2969, 16.0938) cache/0/16/0/glyphs/268435536/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435536/size = Vector2(16, 11) cache/0/16/0/glyphs/268435536/uv_rect = Rect2(120, 111, 16, 11) cache/0/16/0/glyphs/268435536/texture_idx = 0 cache/0/16/0/glyphs/134217728/advance = Vector2(0, 0) cache/0/16/0/glyphs/134217728/offset = Vector2(0, 0) cache/0/16/0/glyphs/134217728/size = Vector2(0, 0) cache/0/16/0/glyphs/134217728/uv_rect = Rect2(0, 0, 0, 0) cache/0/16/0/glyphs/134217728/texture_idx = -1 cache/0/16/0/glyphs/134217781/advance = Vector2(10.2344, 16.0938) cache/0/16/0/glyphs/134217781/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217781/size = Vector2(12, 14) cache/0/16/0/glyphs/134217781/uv_rect = Rect2(238, 124, 12, 14) cache/0/16/0/glyphs/134217781/texture_idx = 0 cache/0/16/0/glyphs/268435492/advance = Vector2(10.5781, 16.0938) cache/0/16/0/glyphs/268435492/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435492/size = Vector2(14, 14) cache/0/16/0/glyphs/268435492/uv_rect = Rect2(1, 140, 14, 14) cache/0/16/0/glyphs/268435492/texture_idx = 0 cache/0/16/0/glyphs/402653224/advance = Vector2(8.9375, 16.0938) cache/0/16/0/glyphs/402653224/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653224/size = Vector2(9, 14) cache/0/16/0/glyphs/402653224/uv_rect = Rect2(17, 140, 9, 14) cache/0/16/0/glyphs/402653224/texture_idx = 0 cache/0/16/0/glyphs/268435514/advance = Vector2(15.1406, 16.0938) cache/0/16/0/glyphs/268435514/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435514/size = Vector2(18, 14) cache/0/16/0/glyphs/268435514/uv_rect = Rect2(28, 140, 18, 14) cache/0/16/0/glyphs/268435514/texture_idx = 0 cache/0/16/0/glyphs/268435506/advance = Vector2(12.5938, 16.0938) cache/0/16/0/glyphs/268435506/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435506/size = Vector2(14, 14) cache/0/16/0/glyphs/268435506/uv_rect = Rect2(48, 140, 14, 14) cache/0/16/0/glyphs/268435506/texture_idx = 0 cache/0/16/0/glyphs/134217765/advance = Vector2(10.5625, 16.0938) cache/0/16/0/glyphs/134217765/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217765/size = Vector2(11, 14) cache/0/16/0/glyphs/134217765/uv_rect = Rect2(64, 140, 11, 14) cache/0/16/0/glyphs/134217765/texture_idx = 0 cache/0/16/0/glyphs/402653221/advance = Vector2(10.5625, 16.0938) cache/0/16/0/glyphs/402653221/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653221/size = Vector2(11, 14) cache/0/16/0/glyphs/402653221/uv_rect = Rect2(77, 140, 11, 14) cache/0/16/0/glyphs/402653221/texture_idx = 0 cache/0/16/0/glyphs/134217788/advance = Vector2(9.46875, 16.0938) cache/0/16/0/glyphs/134217788/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217788/size = Vector2(12, 14) cache/0/16/0/glyphs/134217788/uv_rect = Rect2(90, 140, 12, 14) cache/0/16/0/glyphs/134217788/texture_idx = 0 cache/0/16/0/glyphs/268435496/advance = Vector2(8.9375, 16.0938) cache/0/16/0/glyphs/268435496/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435496/size = Vector2(9, 14) cache/0/16/0/glyphs/268435496/uv_rect = Rect2(104, 140, 9, 14) cache/0/16/0/glyphs/268435496/texture_idx = 0 cache/0/16/0/glyphs/134217764/advance = Vector2(10.5781, 16.0938) cache/0/16/0/glyphs/134217764/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217764/size = Vector2(13, 14) cache/0/16/0/glyphs/134217764/uv_rect = Rect2(115, 140, 13, 14) cache/0/16/0/glyphs/134217764/texture_idx = 0 cache/0/16/0/glyphs/402653231/advance = Vector2(8.67188, 16.0938) cache/0/16/0/glyphs/402653231/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653231/size = Vector2(9, 14) cache/0/16/0/glyphs/402653231/uv_rect = Rect2(130, 140, 9, 14) cache/0/16/0/glyphs/402653231/texture_idx = 0 cache/0/16/0/glyphs/402653243/advance = Vector2(9.95313, 16.0938) cache/0/16/0/glyphs/402653243/offset = Vector2(-1, -13) cache/0/16/0/glyphs/402653243/size = Vector2(13, 14) cache/0/16/0/glyphs/402653243/uv_rect = Rect2(141, 140, 13, 14) cache/0/16/0/glyphs/402653243/texture_idx = 0 cache/0/16/0/glyphs/268435515/advance = Vector2(9.95313, 16.0938) cache/0/16/0/glyphs/268435515/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435515/size = Vector2(13, 14) cache/0/16/0/glyphs/268435515/uv_rect = Rect2(156, 140, 13, 14) cache/0/16/0/glyphs/268435515/texture_idx = 0 cache/0/16/0/glyphs/402653269/advance = Vector2(6.90625, 16.0938) cache/0/16/0/glyphs/402653269/offset = Vector2(1, -10) cache/0/16/0/glyphs/402653269/size = Vector2(8, 11) cache/0/16/0/glyphs/402653269/uv_rect = Rect2(138, 111, 8, 11) cache/0/16/0/glyphs/402653269/texture_idx = 0 cache/0/16/0/glyphs/402653276/advance = Vector2(8.57813, 16.0938) cache/0/16/0/glyphs/402653276/offset = Vector2(-1, -10) cache/0/16/0/glyphs/402653276/size = Vector2(12, 15) cache/0/16/0/glyphs/402653276/uv_rect = Rect2(1, 156, 12, 15) cache/0/16/0/glyphs/402653276/texture_idx = 0 cache/0/16/0/glyphs/402653272/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/402653272/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653272/size = Vector2(11, 11) cache/0/16/0/glyphs/402653272/uv_rect = Rect2(148, 111, 11, 11) cache/0/16/0/glyphs/402653272/texture_idx = 0 cache/0/16/0/glyphs/134217815/advance = Vector2(6.29688, 16.0938) cache/0/16/0/glyphs/134217815/offset = Vector2(-1, -12) cache/0/16/0/glyphs/134217815/size = Vector2(9, 13) cache/0/16/0/glyphs/134217815/uv_rect = Rect2(171, 140, 9, 13) cache/0/16/0/glyphs/134217815/texture_idx = 0 cache/0/16/0/glyphs/402653234/advance = Vector2(12.5938, 16.0938) cache/0/16/0/glyphs/402653234/offset = Vector2(0, -13) cache/0/16/0/glyphs/402653234/size = Vector2(14, 14) cache/0/16/0/glyphs/402653234/uv_rect = Rect2(182, 140, 14, 14) cache/0/16/0/glyphs/402653234/texture_idx = 0 cache/0/16/0/glyphs/268435507/advance = Vector2(9.84375, 16.0938) cache/0/16/0/glyphs/268435507/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435507/size = Vector2(10, 14) cache/0/16/0/glyphs/268435507/uv_rect = Rect2(198, 140, 10, 14) cache/0/16/0/glyphs/268435507/texture_idx = 0 cache/0/16/0/glyphs/134217784/advance = Vector2(11.875, 16.0938) cache/0/16/0/glyphs/134217784/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217784/size = Vector2(12, 14) cache/0/16/0/glyphs/134217784/uv_rect = Rect2(210, 140, 12, 14) cache/0/16/0/glyphs/134217784/texture_idx = 0 cache/0/16/0/glyphs/134217779/advance = Vector2(9.84375, 16.0938) cache/0/16/0/glyphs/134217779/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217779/size = Vector2(11, 14) cache/0/16/0/glyphs/134217779/uv_rect = Rect2(224, 140, 11, 14) cache/0/16/0/glyphs/134217779/texture_idx = 0 cache/0/16/0/glyphs/134217786/advance = Vector2(15.1406, 16.0938) cache/0/16/0/glyphs/134217786/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217786/size = Vector2(18, 14) cache/0/16/0/glyphs/134217786/uv_rect = Rect2(237, 140, 18, 14) cache/0/16/0/glyphs/134217786/texture_idx = 0 cache/0/16/0/glyphs/134218646/advance = Vector2(4.89063, 16.0938) cache/0/16/0/glyphs/134218646/offset = Vector2(0, -13) cache/0/16/0/glyphs/134218646/size = Vector2(5, 14) cache/0/16/0/glyphs/134218646/uv_rect = Rect2(15, 156, 5, 14) cache/0/16/0/glyphs/134218646/texture_idx = 0 cache/0/16/0/glyphs/134217777/advance = Vector2(12.5313, 16.0938) cache/0/16/0/glyphs/134217777/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217777/size = Vector2(13, 14) cache/0/16/0/glyphs/134217777/uv_rect = Rect2(22, 156, 13, 14) cache/0/16/0/glyphs/134217777/texture_idx = 0 cache/0/16/0/glyphs/402653223/advance = Vector2(11.75, 16.0938) cache/0/16/0/glyphs/402653223/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653223/size = Vector2(12, 14) cache/0/16/0/glyphs/402653223/uv_rect = Rect2(37, 156, 12, 14) cache/0/16/0/glyphs/402653223/texture_idx = 0 cache/0/16/0/glyphs/134217783/advance = Vector2(9.0625, 16.0938) cache/0/16/0/glyphs/134217783/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217783/size = Vector2(12, 14) cache/0/16/0/glyphs/134217783/uv_rect = Rect2(51, 156, 12, 14) cache/0/16/0/glyphs/134217783/texture_idx = 0 cache/0/16/0/glyphs/268435516/advance = Vector2(9.46875, 16.0938) cache/0/16/0/glyphs/268435516/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435516/size = Vector2(12, 14) cache/0/16/0/glyphs/268435516/uv_rect = Rect2(65, 156, 12, 14) cache/0/16/0/glyphs/268435516/texture_idx = 0 cache/0/16/0/glyphs/268436374/advance = Vector2(4.89063, 16.0938) cache/0/16/0/glyphs/268436374/offset = Vector2(1, -13) cache/0/16/0/glyphs/268436374/size = Vector2(4, 14) cache/0/16/0/glyphs/268436374/uv_rect = Rect2(79, 156, 4, 14) cache/0/16/0/glyphs/268436374/texture_idx = 0 cache/0/16/0/glyphs/268435503/advance = Vector2(8.67188, 16.0938) cache/0/16/0/glyphs/268435503/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435503/size = Vector2(9, 14) cache/0/16/0/glyphs/268435503/uv_rect = Rect2(85, 156, 9, 14) cache/0/16/0/glyphs/268435503/texture_idx = 0 cache/0/16/0/glyphs/402653252/advance = Vector2(9.28125, 16.0938) cache/0/16/0/glyphs/402653252/offset = Vector2(0, -10) cache/0/16/0/glyphs/402653252/size = Vector2(10, 11) cache/0/16/0/glyphs/402653252/uv_rect = Rect2(161, 111, 10, 11) cache/0/16/0/glyphs/402653252/texture_idx = 0 cache/0/16/0/glyphs/268435493/advance = Vector2(10.5625, 16.0938) cache/0/16/0/glyphs/268435493/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435493/size = Vector2(11, 14) cache/0/16/0/glyphs/268435493/uv_rect = Rect2(96, 156, 11, 14) cache/0/16/0/glyphs/268435493/texture_idx = 0 cache/0/16/0/glyphs/268435511/advance = Vector2(9.0625, 16.0938) cache/0/16/0/glyphs/268435511/offset = Vector2(-1, -13) cache/0/16/0/glyphs/268435511/size = Vector2(12, 14) cache/0/16/0/glyphs/268435511/uv_rect = Rect2(109, 156, 12, 14) cache/0/16/0/glyphs/268435511/texture_idx = 0 cache/0/16/0/glyphs/134217802/advance = Vector2(8.90625, 16.0938) cache/0/16/0/glyphs/134217802/offset = Vector2(-1, -10) cache/0/16/0/glyphs/134217802/size = Vector2(11, 15) cache/0/16/0/glyphs/134217802/uv_rect = Rect2(123, 156, 11, 15) cache/0/16/0/glyphs/134217802/texture_idx = 0 cache/0/16/0/glyphs/268435544/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/268435544/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435544/size = Vector2(11, 11) cache/0/16/0/glyphs/268435544/uv_rect = Rect2(173, 111, 11, 11) cache/0/16/0/glyphs/268435544/texture_idx = 0 cache/0/16/0/glyphs/134217797/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/134217797/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217797/size = Vector2(11, 14) cache/0/16/0/glyphs/134217797/uv_rect = Rect2(136, 156, 11, 14) cache/0/16/0/glyphs/134217797/texture_idx = 0 cache/0/16/0/glyphs/134217809/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/134217809/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217809/size = Vector2(11, 11) cache/0/16/0/glyphs/134217809/uv_rect = Rect2(186, 111, 11, 11) cache/0/16/0/glyphs/134217809/texture_idx = 0 cache/0/16/0/glyphs/134217816/advance = Vector2(10.1563, 16.0938) cache/0/16/0/glyphs/134217816/offset = Vector2(0, -10) cache/0/16/0/glyphs/134217816/size = Vector2(11, 11) cache/0/16/0/glyphs/134217816/uv_rect = Rect2(199, 111, 11, 11) cache/0/16/0/glyphs/134217816/texture_idx = 0 cache/0/16/0/glyphs/268435504/advance = Vector2(14.7656, 16.0938) cache/0/16/0/glyphs/268435504/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435504/size = Vector2(14, 14) cache/0/16/0/glyphs/268435504/uv_rect = Rect2(149, 156, 14, 14) cache/0/16/0/glyphs/268435504/texture_idx = 0 cache/0/16/0/glyphs/268435539/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/268435539/offset = Vector2(0, -10) cache/0/16/0/glyphs/268435539/size = Vector2(11, 15) cache/0/16/0/glyphs/268435539/uv_rect = Rect2(165, 156, 11, 15) cache/0/16/0/glyphs/268435539/texture_idx = 0 cache/0/16/0/glyphs/134217819/advance = Vector2(8.8125, 16.0938) cache/0/16/0/glyphs/134217819/offset = Vector2(-1, -10) cache/0/16/0/glyphs/134217819/size = Vector2(11, 11) cache/0/16/0/glyphs/134217819/uv_rect = Rect2(212, 111, 11, 11) cache/0/16/0/glyphs/134217819/texture_idx = 0 cache/0/16/0/glyphs/134217804/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/134217804/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217804/size = Vector2(5, 14) cache/0/16/0/glyphs/134217804/uv_rect = Rect2(178, 156, 5, 14) cache/0/16/0/glyphs/134217804/texture_idx = 0 cache/0/16/0/glyphs/134217747/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/134217747/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217747/size = Vector2(11, 14) cache/0/16/0/glyphs/134217747/uv_rect = Rect2(185, 156, 11, 14) cache/0/16/0/glyphs/134217747/texture_idx = 0 cache/0/16/0/glyphs/134217736/advance = Vector2(13.7969, 16.0938) cache/0/16/0/glyphs/134217736/offset = Vector2(-1, -13) cache/0/16/0/glyphs/134217736/size = Vector2(16, 14) cache/0/16/0/glyphs/134217736/uv_rect = Rect2(198, 156, 16, 14) cache/0/16/0/glyphs/134217736/texture_idx = 0 cache/0/16/0/glyphs/268435477/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/268435477/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435477/size = Vector2(10, 14) cache/0/16/0/glyphs/268435477/uv_rect = Rect2(216, 156, 10, 14) cache/0/16/0/glyphs/268435477/texture_idx = 0 cache/0/16/0/glyphs/268435478/advance = Vector2(9.14063, 16.0938) cache/0/16/0/glyphs/268435478/offset = Vector2(0, -13) cache/0/16/0/glyphs/268435478/size = Vector2(10, 14) cache/0/16/0/glyphs/268435478/uv_rect = Rect2(228, 156, 10, 14) cache/0/16/0/glyphs/268435478/texture_idx = 0 cache/0/16/0/glyphs/402654102/advance = Vector2(4.89063, 16.0938) cache/0/16/0/glyphs/402654102/offset = Vector2(1, -13) cache/0/16/0/glyphs/402654102/size = Vector2(5, 14) cache/0/16/0/glyphs/402654102/uv_rect = Rect2(240, 156, 5, 14) cache/0/16/0/glyphs/402654102/texture_idx = 0 cache/0/16/0/glyphs/402653258/advance = Vector2(8.90625, 16.0938) cache/0/16/0/glyphs/402653258/offset = Vector2(-1, -10) cache/0/16/0/glyphs/402653258/size = Vector2(12, 15) cache/0/16/0/glyphs/402653258/uv_rect = Rect2(1, 173, 12, 15) cache/0/16/0/glyphs/402653258/texture_idx = 0 cache/0/16/0/glyphs/402653263/advance = Vector2(4.46875, 16.0938) cache/0/16/0/glyphs/402653263/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653263/size = Vector2(4, 14) cache/0/16/0/glyphs/402653263/uv_rect = Rect2(247, 156, 4, 14) cache/0/16/0/glyphs/402653263/texture_idx = 0 cache/0/16/0/glyphs/268435495/advance = Vector2(11.75, 16.0938) cache/0/16/0/glyphs/268435495/offset = Vector2(1, -13) cache/0/16/0/glyphs/268435495/size = Vector2(12, 14) cache/0/16/0/glyphs/268435495/uv_rect = Rect2(15, 173, 12, 14) cache/0/16/0/glyphs/268435495/texture_idx = 0 cache/0/16/0/glyphs/402653253/advance = Vector2(9.96875, 16.0938) cache/0/16/0/glyphs/402653253/offset = Vector2(1, -13) cache/0/16/0/glyphs/402653253/size = Vector2(10, 14) cache/0/16/0/glyphs/402653253/uv_rect = Rect2(29, 173, 10, 14) cache/0/16/0/glyphs/402653253/texture_idx = 0 cache/0/16/0/glyphs/134217767/advance = Vector2(11.75, 16.0938) cache/0/16/0/glyphs/134217767/offset = Vector2(0, -13) cache/0/16/0/glyphs/134217767/size = Vector2(13, 14) cache/0/16/0/glyphs/134217767/uv_rect = Rect2(41, 173, 13, 14) cache/0/16/0/glyphs/134217767/texture_idx = 0 cache/0/16/0/glyphs/134217745/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/134217745/offset = Vector2(0, -4) cache/0/16/0/glyphs/134217745/size = Vector2(5, 5) cache/0/16/0/glyphs/134217745/uv_rect = Rect2(225, 111, 5, 5) cache/0/16/0/glyphs/134217745/texture_idx = 0 cache/0/16/0/glyphs/268435473/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/268435473/offset = Vector2(0, -4) cache/0/16/0/glyphs/268435473/size = Vector2(5, 5) cache/0/16/0/glyphs/268435473/uv_rect = Rect2(232, 111, 5, 5) cache/0/16/0/glyphs/268435473/texture_idx = 0 cache/0/16/0/glyphs/402653201/advance = Vector2(4.40625, 16.0938) cache/0/16/0/glyphs/402653201/offset = Vector2(0, -4) cache/0/16/0/glyphs/402653201/size = Vector2(6, 5) cache/0/16/0/glyphs/402653201/uv_rect = Rect2(239, 111, 6, 5) cache/0/16/0/glyphs/402653201/texture_idx = 0 cache/0/16/0/glyphs/34/advance = Vector2(7.25, 16.0938) cache/0/16/0/glyphs/34/offset = Vector2(-1, -13) cache/0/16/0/glyphs/34/size = Vector2(9, 14) cache/0/16/0/glyphs/34/uv_rect = Rect2(56, 173, 9, 14) cache/0/16/0/glyphs/34/texture_idx = 0 cache/0/16/0/kerning_overrides/16/0 = Vector2(0, 0) cache/0/16/0/kerning_overrides/20/0 = Vector2(0, 0) cache/0/20/0/ascent = 22.0 cache/0/20/0/descent = 6.0 cache/0/20/0/underline_position = 2.0 cache/0/20/0/underline_thickness = 1.0 cache/0/20/0/scale = 1.0 cache/0/20/0/textures/0/offsets = PackedInt32Array(193, 0, 63, 18, 15, 18, 241, 19) cache/0/20/0/textures/0/image = SubResource("Image_vwwu5") cache/0/20/0/glyphs/39/advance = Vector2(14.6719, 20.1094) cache/0/20/0/glyphs/39/offset = Vector2(0, -15) cache/0/20/0/glyphs/39/size = Vector2(15, 16) cache/0/20/0/glyphs/39/uv_rect = Rect2(1, 1, 15, 16) cache/0/20/0/glyphs/39/texture_idx = 0 cache/0/20/0/glyphs/76/advance = Vector2(5.57813, 20.1094) cache/0/20/0/glyphs/76/offset = Vector2(0, -16) cache/0/20/0/glyphs/76/size = Vector2(6, 17) cache/0/20/0/glyphs/76/uv_rect = Rect2(1, 19, 6, 17) cache/0/20/0/glyphs/76/texture_idx = 0 cache/0/20/0/glyphs/85/advance = Vector2(8.625, 20.1094) cache/0/20/0/glyphs/85/offset = Vector2(0, -12) cache/0/20/0/glyphs/85/size = Vector2(10, 13) cache/0/20/0/glyphs/85/uv_rect = Rect2(18, 1, 10, 13) cache/0/20/0/glyphs/85/texture_idx = 0 cache/0/20/0/glyphs/72/advance = Vector2(11.5313, 20.1094) cache/0/20/0/glyphs/72/offset = Vector2(0, -12) cache/0/20/0/glyphs/72/size = Vector2(12, 13) cache/0/20/0/glyphs/72/uv_rect = Rect2(30, 1, 12, 13) cache/0/20/0/glyphs/72/texture_idx = 0 cache/0/20/0/glyphs/70/advance = Vector2(9.90625, 20.1094) cache/0/20/0/glyphs/70/offset = Vector2(0, -12) cache/0/20/0/glyphs/70/size = Vector2(11, 13) cache/0/20/0/glyphs/70/uv_rect = Rect2(44, 1, 11, 13) cache/0/20/0/glyphs/70/texture_idx = 0 cache/0/20/0/glyphs/87/advance = Vector2(7.85938, 20.1094) cache/0/20/0/glyphs/87/offset = Vector2(-1, -15) cache/0/20/0/glyphs/87/size = Vector2(10, 16) cache/0/20/0/glyphs/87/uv_rect = Rect2(57, 1, 10, 16) cache/0/20/0/glyphs/87/texture_idx = 0 cache/0/20/0/glyphs/82/advance = Vector2(12.2188, 20.1094) cache/0/20/0/glyphs/82/offset = Vector2(0, -12) cache/0/20/0/glyphs/82/size = Vector2(13, 13) cache/0/20/0/glyphs/82/uv_rect = Rect2(69, 1, 13, 13) cache/0/20/0/glyphs/82/texture_idx = 0 cache/0/20/0/glyphs/86/advance = Vector2(9.73438, 20.1094) cache/0/20/0/glyphs/86/offset = Vector2(-1, -12) cache/0/20/0/glyphs/86/size = Vector2(11, 13) cache/0/20/0/glyphs/86/uv_rect = Rect2(84, 1, 11, 13) cache/0/20/0/glyphs/86/texture_idx = 0 cache/0/20/0/glyphs/3/advance = Vector2(5.20313, 20.1094) cache/0/20/0/glyphs/3/offset = Vector2(-1, -1) cache/0/20/0/glyphs/3/size = Vector2(2, 2) cache/0/20/0/glyphs/3/uv_rect = Rect2(97, 1, 2, 2) cache/0/20/0/glyphs/3/texture_idx = 0 cache/0/20/0/glyphs/9/advance = Vector2(14.8125, 20.1094) cache/0/20/0/glyphs/9/offset = Vector2(-1, -15) cache/0/20/0/glyphs/9/size = Vector2(17, 16) cache/0/20/0/glyphs/9/uv_rect = Rect2(101, 1, 17, 16) cache/0/20/0/glyphs/9/texture_idx = 0 cache/0/20/0/glyphs/41/advance = Vector2(10.6406, 20.1094) cache/0/20/0/glyphs/41/offset = Vector2(0, -15) cache/0/20/0/glyphs/41/size = Vector2(11, 16) cache/0/20/0/glyphs/41/uv_rect = Rect2(120, 1, 11, 16) cache/0/20/0/glyphs/41/texture_idx = 0 cache/0/20/0/glyphs/79/advance = Vector2(5.57813, 20.1094) cache/0/20/0/glyphs/79/offset = Vector2(0, -16) cache/0/20/0/glyphs/79/size = Vector2(5, 17) cache/0/20/0/glyphs/79/uv_rect = Rect2(9, 19, 5, 17) cache/0/20/0/glyphs/79/texture_idx = 0 cache/0/20/0/glyphs/29/advance = Vector2(5.5, 20.1094) cache/0/20/0/glyphs/29/offset = Vector2(0, -12) cache/0/20/0/glyphs/29/size = Vector2(6, 13) cache/0/20/0/glyphs/29/uv_rect = Rect2(133, 1, 6, 13) cache/0/20/0/glyphs/29/texture_idx = 0 cache/0/20/0/glyphs/49/advance = Vector2(15.6719, 20.1094) cache/0/20/0/glyphs/49/offset = Vector2(0, -15) cache/0/20/0/glyphs/49/size = Vector2(15, 16) cache/0/20/0/glyphs/49/uv_rect = Rect2(141, 1, 15, 16) cache/0/20/0/glyphs/49/texture_idx = 0 cache/0/20/0/glyphs/68/advance = Vector2(11.6094, 20.1094) cache/0/20/0/glyphs/68/offset = Vector2(-1, -12) cache/0/20/0/glyphs/68/size = Vector2(13, 13) cache/0/20/0/glyphs/68/uv_rect = Rect2(158, 1, 13, 13) cache/0/20/0/glyphs/68/texture_idx = 0 cache/0/20/0/glyphs/80/advance = Vector2(19.125, 20.1094) cache/0/20/0/glyphs/80/offset = Vector2(0, -12) cache/0/20/0/glyphs/80/size = Vector2(19, 13) cache/0/20/0/glyphs/80/uv_rect = Rect2(173, 1, 19, 13) cache/0/20/0/glyphs/80/texture_idx = 0 cache/0/20/0/kerning_overrides/16/0 = Vector2(0, 0) cache/0/20/0/kerning_overrides/20/0 = Vector2(0, 0) [sub_resource type="Image" id="Image_l4aks"] data = { "data": PackedByteArray("////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////YP///2H///8A////AP///wD///8A////AP///wD///8A////AP///2D///9h////AP///wD///8A////YP///7////+/////Yf///wD///8A////AP///wD///8A////AP///2D///+/////v////2H///8A////AP///2H///+/////v////7////9h////AP///wD///8A////AP///2D///+/////v////7////9g////AP///wD///8A////Yf///7////+/////v////2H///8A////AP///2D///+/////v////7////9g////AP///wD///8A////AP///wD///9h////v////7////+/////Yf///2D///+/////v////7////9g////AP///wD///8A////AP///wD///8A////AP///2H///+/////v////7////+/////v////7////9g////AP///wD///8A////AP///wD///8A////AP///wD///8A////Yf///7////+/////v////7////9g////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///2D///+/////v////7////+/////Yf///wD///8A////AP///wD///8A////AP///wD///8A////AP///2D///+/////v////7////+/////v////7////9h////AP///wD///8A////AP///wD///8A////AP///2D///+/////v////7r///85////jv///7////+/////v////2H///8A////AP///wD///8A////AP///2D///+/////v////73///86////AP///wb///+F////v////7////+/////Yf///wD///8A////AP///2D///+/////v////77///9E////AP///wD///8A////BP///3z///+/////v////7////9h////AP///wD///9h////v////7////9O////AP///wD///8A////AP///wD///8C////cf///7////+/////YP///wD///8A////AP///2H///9a////AP///wD///8A////AP///wD///8A////AP///wH///9m////YP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AA=="), "format": "RGBA8", "height": 16, "mipmaps": false, "width": 16 } [sub_resource type="ImageTexture" id="ImageTexture_3k56q"] image = SubResource("Image_l4aks") [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ea04u"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 content_margin_bottom = 4.0 bg_color = Color(1, 1, 1, 0.75) draw_center = false border_width_left = 2 border_width_top = 2 border_width_right = 2 border_width_bottom = 2 corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 expand_margin_left = 2.0 expand_margin_top = 2.0 expand_margin_right = 2.0 expand_margin_bottom = 2.0 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v4mmb"] content_margin_left = 8.0 content_margin_top = 6.0 content_margin_right = 6.0 content_margin_bottom = 6.0 bg_color = Color(0.101961, 0.101961, 0.101961, 1) border_width_left = 2 border_width_top = 2 border_width_right = 2 border_width_bottom = 2 border_color = Color(0.188235, 0.188235, 0.188235, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ncfsv"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 content_margin_bottom = 4.0 bg_color = Color(0.1, 0.1, 0.1, 0.3) border_width_bottom = 2 border_color = Color(0, 0, 0, 0.3) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="Image" id="Image_flcqm"] data = { "data": PackedByteArray("////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALOzs6OysrKmuLi4Ev///wD///8A////AP///wC4uLgSsrKyprKysqb///8A////ALGxsa2ysrLZsbGxs7i4uBL///8A////ALi4uBKysrKzsrKy2bKysqz///8A////AK+vrxOysrK1srKy2bGxsbO4uLgSuLi4ErKysrOysrLZs7OztK+vrxP///8A////AP///wCvr68TsrKytbKystmxsbGzsrKys7Kystmzs7O0r6+vE////wD///8A////AP///wD///8Ar6+vE7KysrWysrLZsrKy2bOzs7Svr68T////AP///wD///8A////AP///wD///8A////AK+vrxOysrKpsrKyqK+vrxP///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A"), "format": "RGBA8", "height": 12, "mipmaps": false, "width": 12 } [sub_resource type="ImageTexture" id="ImageTexture_oehfs"] image = SubResource("Image_flcqm") [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qts61"] content_margin_left = 8.0 content_margin_top = 4.0 content_margin_right = 21.0 content_margin_bottom = 4.0 bg_color = Color(0.1, 0.1, 0.1, 0.3) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hv865"] content_margin_left = 21.0 content_margin_top = 4.0 content_margin_right = 8.0 content_margin_bottom = 4.0 bg_color = Color(0.1, 0.1, 0.1, 0.3) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1scso"] content_margin_left = 8.0 content_margin_top = 4.0 content_margin_right = 21.0 content_margin_bottom = 4.0 bg_color = Color(0.225, 0.225, 0.225, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kx431"] content_margin_left = 21.0 content_margin_top = 4.0 content_margin_right = 8.0 content_margin_bottom = 4.0 bg_color = Color(0.225, 0.225, 0.225, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4fm5o"] content_margin_left = 8.0 content_margin_top = 4.0 content_margin_right = 21.0 content_margin_bottom = 4.0 bg_color = Color(0.1, 0.1, 0.1, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lo50j"] content_margin_left = 21.0 content_margin_top = 4.0 content_margin_right = 8.0 content_margin_bottom = 4.0 bg_color = Color(0.1, 0.1, 0.1, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ss0gg"] content_margin_left = 8.0 content_margin_top = 4.0 content_margin_right = 21.0 content_margin_bottom = 4.0 bg_color = Color(0, 0, 0, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6fha2"] content_margin_left = 21.0 content_margin_top = 4.0 content_margin_right = 8.0 content_margin_bottom = 4.0 bg_color = Color(0, 0, 0, 0.6) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6tiuh"] content_margin_left = 2.0 content_margin_top = 2.0 content_margin_right = 2.0 content_margin_bottom = 6.0 bg_color = Color(0.054902, 0.054902, 0.054902, 1) border_width_bottom = 4 border_color = Color(0.192157, 0.192157, 0.192157, 1) [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dgqpj"] bg_color = Color(0, 0, 0, 1) border_width_left = 4 border_width_top = 4 border_width_right = 4 border_width_bottom = 4 border_color = Color(0.192157, 0.192157, 0.192157, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 corner_detail = 5 [resource] default_font = ExtResource("5_kvsd8") default_font_size = 16 CheckBox/icons/checked = ExtResource("1_q2c85") CheckBox/icons/checked_disabled = ExtResource("2_s6mpo") CheckBox/icons/unchecked = ExtResource("3_gg3vm") CheckBox/icons/unchecked_disabled = ExtResource("4_s07s3") LineEdit/colors/caret_color = Color(0.95, 0.95, 0.95, 1) LineEdit/colors/clear_button_color = Color(0.875, 0.875, 0.875, 1) LineEdit/colors/clear_button_color_pressed = Color(1, 1, 1, 1) LineEdit/colors/font_color = Color(0.875, 0.875, 0.875, 1) LineEdit/colors/font_outline_color = Color(1, 1, 1, 1) LineEdit/colors/font_placeholder_color = Color(0.875, 0.875, 0.875, 0.6) LineEdit/colors/font_selected_color = Color(1, 1, 1, 1) LineEdit/colors/font_uneditable_color = Color(0.875, 0.875, 0.875, 0.5) LineEdit/colors/selection_color = Color(0.5, 0.5, 0.5, 1) LineEdit/constants/caret_width = 1 LineEdit/constants/minimum_character_width = 4 LineEdit/constants/outline_size = 0 LineEdit/font_sizes/font_size = 16 LineEdit/fonts/font = SubResource("FontFile_5froe") LineEdit/icons/clear = SubResource("ImageTexture_3k56q") LineEdit/styles/focus = SubResource("StyleBoxFlat_ea04u") LineEdit/styles/normal = SubResource("StyleBoxFlat_v4mmb") LineEdit/styles/read_only = SubResource("StyleBoxFlat_ncfsv") OptionButton/colors/font_color = Color(0.875, 0.875, 0.875, 1) OptionButton/colors/font_disabled_color = Color(0.875, 0.875, 0.875, 0.5) OptionButton/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1) OptionButton/colors/font_hover_color = Color(0.95, 0.95, 0.95, 1) OptionButton/colors/font_hover_pressed_color = Color(1, 1, 1, 1) OptionButton/colors/font_outline_color = Color(1, 1, 1, 1) OptionButton/colors/font_pressed_color = Color(1, 1, 1, 1) OptionButton/constants/arrow_margin = 4 OptionButton/constants/h_separation = 2 OptionButton/constants/modulate_arrow = 0 OptionButton/constants/outline_size = 0 OptionButton/font_sizes/font_size = 16 OptionButton/fonts/font = SubResource("FontFile_5froe") OptionButton/icons/arrow = SubResource("ImageTexture_oehfs") OptionButton/styles/disabled = SubResource("StyleBoxFlat_qts61") OptionButton/styles/disabled_mirrored = SubResource("StyleBoxFlat_hv865") OptionButton/styles/focus = SubResource("StyleBoxFlat_ea04u") OptionButton/styles/hover = SubResource("StyleBoxFlat_1scso") OptionButton/styles/hover_mirrored = SubResource("StyleBoxFlat_kx431") OptionButton/styles/normal = SubResource("StyleBoxFlat_4fm5o") OptionButton/styles/normal_mirrored = SubResource("StyleBoxFlat_lo50j") OptionButton/styles/pressed = SubResource("StyleBoxFlat_ss0gg") OptionButton/styles/pressed_mirrored = SubResource("StyleBoxFlat_6fha2") Panel_TitleBar/styles/panel = SubResource("StyleBoxFlat_6tiuh") PopupPanel/styles/panel = SubResource("StyleBoxFlat_dgqpj") ================================================ FILE: sample/dedicated_server_example/client_main.gd ================================================ extends Control @onready var PRODUCT_NAME: String = Env.get_var("PRODUCT_NAME") @onready var PRODUCT_VERSION: String = Env.get_var("PRODUCT_VERSION") @onready var PRODUCT_ID: String = Env.get_var("PRODUCT_ID") @onready var SANDBOX_ID: String = Env.get_var("SANDBOX_ID") @onready var DEPLOYMENT_ID: String = Env.get_var("DEPLOYMENT_ID") @onready var ENCRYPTION_KEY: String = Env.get_var("ENCRYPTION_KEY") @onready var CLIENT_ID: String = Env.get_var("CLIENT_ID") @onready var CLIENT_SECRET: String = Env.get_var("CLIENT_SECRET") func _log(msg: String): print_rich("[color=WEB_GREEN]Client " + HAuth.product_user_id + ": " + msg + "[/color]") func _ready() -> void: var credentials = HCredentials.new() credentials.product_name = PRODUCT_NAME credentials.product_version = PRODUCT_VERSION credentials.product_id = PRODUCT_ID credentials.sandbox_id = SANDBOX_ID credentials.deployment_id = DEPLOYMENT_ID credentials.client_id = CLIENT_ID credentials.client_secret = CLIENT_SECRET credentials.encryption_key = ENCRYPTION_KEY _log("Setting up EOS for dedicated client...") var setup_success := await HPlatform.setup_eos_async(credentials) if not setup_success: _log("Failed to setup EOS. See logs for error details") return var args = OS.get_cmdline_user_args() for arg in args: if arg.begins_with("--devuser="): var username = arg.replace("--devuser=", "") _login_as_client(username) func _login_as_client(devusername: String): _log("Logging in as client... %s" % devusername) var success := await HAuth.login_devtool_async("localhost:4545", devusername) if not success: return _log("Client logged in") # Wait so that the server has time to create the session # This is just for the sample project! # In the real game this timer isnt needed await get_tree().create_timer(5).timeout var sessions = await HSessions.search_by_attribute_async({key = "level", value = "margao"}) if not sessions: return var session_details = sessions[-1] var session_info = session_details.copy_info().info _log("Joining session: %s" % session_info) #HLog.log_level = HLog.LogLevel.DEBUG # Join the session var join_success := await HSessions.join_async(session_details, "Party") if not join_success: return var peer = ENetMultiplayerPeer.new() var addr_parts = session_info.host_address.split(":") var ip = addr_parts[0] var port = int(addr_parts[1]) peer.create_client(ip, port) multiplayer.multiplayer_peer = peer await multiplayer.connected_to_server c_s_register_player.rpc_id(0, HAuth.product_user_id) @rpc("any_peer", "reliable") func c_s_register_player(puid: String): pass @rpc("reliable") func s_c_register_player_completed(is_success: bool): _log("Got register player completed response from server: register_success=%s" % str(is_success)) ================================================ FILE: sample/dedicated_server_example/client_main.gd.uid ================================================ uid://3nyw0hwkkgmg ================================================ FILE: sample/dedicated_server_example/client_main.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://dve4hhbpclrm5"] [ext_resource type="Script" uid="uid://3nyw0hwkkgmg" path="res://dedicated_server_example/client_main.gd" id="1_t6poi"] [node name="Root" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_t6poi") ================================================ FILE: sample/dedicated_server_example/server_main.gd ================================================ extends Control @onready var PRODUCT_NAME: String = Env.get_var("PRODUCT_NAME") @onready var PRODUCT_VERSION: String = Env.get_var("PRODUCT_VERSION") @onready var PRODUCT_ID: String = Env.get_var("PRODUCT_ID") @onready var SANDBOX_ID: String = Env.get_var("SANDBOX_ID") @onready var DEPLOYMENT_ID: String = Env.get_var("DEPLOYMENT_ID") @onready var ENCRYPTION_KEY: String = Env.get_var("ENCRYPTION_KEY") @onready var SERVER_CLIENT_ID: String = Env.get_var("SERVER_CLIENT_ID") @onready var SERVER_CLIENT_SECRET: String = Env.get_var("SERVER_CLIENT_SECRET") const PORT = 5412 var peer = ENetMultiplayerPeer.new() func _log(msg: String): print_rich("[color=cyan]Server: " + msg + "[/color]") func _ready() -> void: var err = peer.create_server(PORT) if err != OK: _log("Failed to create ENet server %s (%d)" % [error_string(err), err]) multiplayer.multiplayer_peer = peer var credentials = HCredentials.new() credentials.product_name = PRODUCT_NAME credentials.product_version = PRODUCT_VERSION credentials.product_id = PRODUCT_ID credentials.sandbox_id = SANDBOX_ID credentials.deployment_id = DEPLOYMENT_ID credentials.client_id = SERVER_CLIENT_ID credentials.client_secret = SERVER_CLIENT_SECRET credentials.encryption_key = ENCRYPTION_KEY print("Setting up EOS for dedicated server...") HPlatform.cache_directory = ProjectSettings.globalize_path("user://eosg-server-cache") HPlatform.is_server = true HPlatform.flags = EOS.Platform.PlatformFlags.DisableOverlay var setup_success := await HPlatform.setup_eos_async(credentials) if not setup_success: _log("Failed to setup EOS. See logs for error details") return var opts = EOS.Sessions.CreateSessionModificationOptions.new() opts.bucket_id = "party-bucket" opts.max_players = 4 opts.session_name = "Party" var session := HSessions.create_session(opts) if not session: return _log("Created session") session.add_attribute("level", "margao", EOS.Sessions.SessionAttributeAdvertisementType.Advertise) session.set_permission_level(EOS.Sessions.OnlineSessionPermissionLevel.PublicAdvertised) session.set_host_address("localhost:" + str(PORT)) var update_success := await HSessions.update_session_async(session) if not update_success: return _log("Updated session") var active_sess := HSessions.get_active_session("Party") if not active_sess: return _log("sess is %s" % active_sess.copy_info()) # This is just added as a sample in your game you can register a player in # a different way also @rpc("any_peer", "reliable") func c_s_register_player(puid: String): var peerid = multiplayer.get_remote_sender_id() var opts = EOS.Sessions.RegisterPlayersOptions.new() opts.session_name = "Party" opts.players_to_register = [puid] EOS.Sessions.SessionsInterface.register_players(opts) var ret: Dictionary = await IEOS.sessions_interface_register_players_callback if not EOS.is_success(ret): _log("Failed to register player: puid=%s, result_code=%s" % [puid, EOS.result_str(ret)]) s_c_register_player_completed.rpc_id(peerid, false) return _log("Registered player: puid") s_c_register_player_completed.rpc_id(peerid, true) @rpc("reliable") func s_c_register_player_completed(is_success: bool): pass ================================================ FILE: sample/dedicated_server_example/server_main.gd.uid ================================================ uid://bneij5syrcl1f ================================================ FILE: sample/dedicated_server_example/server_main.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://dyuj4qlamg7b5"] [ext_resource type="Script" uid="uid://bneij5syrcl1f" path="res://dedicated_server_example/server_main.gd" id="1_74ooc"] [node name="Root" type="Control"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 script = ExtResource("1_74ooc") ================================================ FILE: sample/default_bus_layout.tres ================================================ [gd_resource type="AudioBusLayout" load_steps=2 format=3 uid="uid://dfiyvl4a8dbhr"] [sub_resource type="AudioEffectCapture" id="AudioEffectCapture_j3pel"] resource_name = "Capture" [resource] bus/1/name = &"Microphone" bus/1/solo = false bus/1/mute = false bus/1/bypass_fx = false bus/1/volume_db = 0.0 bus/1/send = &"Master" bus/1/effect/0/effect = SubResource("AudioEffectCapture_j3pel") bus/1/effect/0/enabled = true ================================================ FILE: sample/default_env.tres ================================================ [gd_resource type="Environment" load_steps=2 format=3 uid="uid://cdoy56rtiqovw"] [sub_resource type="Sky" id="1"] [resource] background_mode = 2 sky = SubResource("1") ================================================ FILE: sample/fonts/roboto-13r.tres ================================================ [gd_resource type="FontFile" load_steps=2 format=3 uid="uid://da6bpapjl4a5k"] [ext_resource type="FontFile" uid="uid://ij5t8dy1m6e6" path="res://fonts/Roboto-Regular.ttf" id="1"] [resource] fallbacks = Array[Font]([ExtResource("1")]) cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 cache/0/16/0/underline_thickness = 0.0 cache/0/16/0/scale = 1.0 ================================================ FILE: sample/fonts/roboto-16b.tres ================================================ [gd_resource type="FontFile" load_steps=2 format=3 uid="uid://bghbav6kh0lsl"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="1"] [resource] fallbacks = Array[Font]([ExtResource("1")]) cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 cache/0/16/0/underline_thickness = 0.0 cache/0/16/0/scale = 1.0 cache/0/50/0/ascent = 0.0 cache/0/50/0/descent = 0.0 cache/0/50/0/underline_position = 0.0 cache/0/50/0/underline_thickness = 0.0 cache/0/50/0/scale = 1.0 ================================================ FILE: sample/fonts/roboto-16r.tres ================================================ [gd_resource type="FontFile" load_steps=2 format=3 uid="uid://bxcl265jykrk0"] [ext_resource type="FontFile" uid="uid://ij5t8dy1m6e6" path="res://fonts/Roboto-Regular.ttf" id="1"] [resource] fallbacks = Array[Font]([ExtResource("1")]) cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 cache/0/16/0/underline_thickness = 0.0 cache/0/16/0/scale = 1.0 cache/0/50/0/ascent = 0.0 cache/0/50/0/descent = 0.0 cache/0/50/0/underline_position = 0.0 cache/0/50/0/underline_thickness = 0.0 cache/0/50/0/scale = 1.0 ================================================ FILE: sample/fonts/roboto-32b.tres ================================================ [gd_resource type="FontFile" load_steps=2 format=3 uid="uid://njn2rf7rj4ph"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="1"] [resource] fallbacks = Array[Font]([ExtResource("1")]) cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 cache/0/16/0/underline_thickness = 0.0 cache/0/16/0/scale = 1.0 cache/0/50/0/ascent = 0.0 cache/0/50/0/descent = 0.0 cache/0/50/0/underline_position = 0.0 cache/0/50/0/underline_thickness = 0.0 cache/0/50/0/scale = 1.0 ================================================ FILE: sample/game/entities/bullet/bullet.gd ================================================ class_name Bullet extends Area2D const SPEED = 2000.0 var dmg_amt = 25 var owner_peer_id: int var owner_puid: String func _physics_process(delta: float) -> void: position += transform.x * SPEED * delta func _on_visible_on_screen_notifier_2d_screen_exited() -> void: if is_multiplayer_authority(): remove_bullet.rpc() @rpc("call_local", "reliable") func remove_bullet(): queue_free() func _on_body_entered(body: Node2D) -> void: if not is_multiplayer_authority(): return if body is Player: body.take_damage.rpc_id(body.peer_id, dmg_amt, owner_peer_id) remove_bullet.rpc() ================================================ FILE: sample/game/entities/bullet/bullet.gd.uid ================================================ uid://83g60od7sqbn ================================================ FILE: sample/game/entities/bullet/bullet.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://b8clmnksfigve"] [ext_resource type="Script" uid="uid://83g60od7sqbn" path="res://game/entities/bullet/bullet.gd" id="1_dsf6p"] [ext_resource type="Texture2D" uid="uid://bcxe48xa5eniy" path="res://game/entities/bullet/bullet.jpg" id="2_y3kyh"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_3mh06"] size = Vector2(40, 16) [node name="Bullet" type="Area2D"] collision_mask = 3 script = ExtResource("1_dsf6p") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] visible = false position = Vector2(20, 0) shape = SubResource("RectangleShape2D_3mh06") [node name="VisibleOnScreenNotifier2D" type="VisibleOnScreenNotifier2D" parent="."] position = Vector2(10, 1.19209e-07) scale = Vector2(1, 0.4) [node name="Sprite2D" type="Sprite2D" parent="."] position = Vector2(10, 1.19209e-07) scale = Vector2(0.3125, 0.125) texture = ExtResource("2_y3kyh") [connection signal="body_entered" from="." to="." method="_on_body_entered"] [connection signal="screen_exited" from="VisibleOnScreenNotifier2D" to="." method="_on_visible_on_screen_notifier_2d_screen_exited"] ================================================ FILE: sample/game/entities/player/player.gd ================================================ class_name Player extends CharacterBody2D signal health_changed signal died(player, killing_peer_id: int) const SPEED = 180.0 const SHOOT_COOLDOWN = 0.3 const BULLET = preload("res://game/entities/bullet/bullet.tscn") var peer_id: int ## Remote product user id var puid: String var score = 0 var health = 100 var shoot_cooldown = SHOOT_COOLDOWN @onready var _gun_container: Node2D = %GunContainer @onready var _muzzle: Node2D = %Muzzle @onready var _panel: Panel = $Panel #region Built-in methods func _enter_tree() -> void: set_multiplayer_authority(peer_id) func _ready() -> void: Store.player_score_changed.emit() if not is_multiplayer_authority(): _panel.self_modulate = Color("#ff0000") func _physics_process(delta: float) -> void: if not is_multiplayer_authority(): return _handle_move() _handle_gun_rotation() if shoot_cooldown > -0.1: shoot_cooldown -= delta if not Store.is_mobile: if Input.is_action_pressed(&"shoot") && shoot_cooldown <= 0: _handle_shoot() else: if Store.shoot_joystick.get_deadzoned_vector().length_squared() > 0.1 && shoot_cooldown <= 0: _handle_shoot() #endregion #region Public methods @rpc("any_peer", "call_local", "reliable") func take_damage(amt: int, owner_peer_id: int): health = max(0, health - amt) health_changed.emit() if health == 0: died.emit(self, owner_peer_id) Store.network.add_score.rpc(owner_peer_id, 10) @rpc("call_local", "reliable") func shoot(owner_peer_id: int, owner_puid: String, bname: String): var bullet: Bullet = BULLET.instantiate() bullet.owner_peer_id = owner_peer_id bullet.owner_puid = owner_puid bullet.name = bname bullet.set_multiplayer_authority(owner_peer_id) get_parent().add_child(bullet) bullet.transform = _muzzle.global_transform #endregion #region Private methods func _handle_move(): var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down") velocity = direction * SPEED move_and_slide() func _handle_gun_rotation(): if not Store.is_mobile: _gun_container.look_at(get_global_mouse_position()) else: _gun_container.look_at(global_position + Store.shoot_joystick.get_deadzoned_vector()) func _handle_shoot(): shoot_cooldown = SHOOT_COOLDOWN var bname = str(peer_id) + "_" + str(randi_range(1000, 9999)) shoot.rpc(peer_id, puid, bname) #endregion ================================================ FILE: sample/game/entities/player/player.gd.uid ================================================ uid://cwybnfn6dv66t ================================================ FILE: sample/game/entities/player/player.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://d25l4fa1sffqk"] [ext_resource type="Script" uid="uid://cwybnfn6dv66t" path="res://game/entities/player/player.gd" id="1_gheds"] [sub_resource type="CircleShape2D" id="CircleShape2D_yh8w5"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0phbn"] bg_color = Color(0.594875, 0.0860397, 0.180462, 1) [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2p5sl"] bg_color = Color(1, 1, 1, 1) corner_radius_top_left = 10 corner_radius_top_right = 10 corner_radius_bottom_right = 10 corner_radius_bottom_left = 10 corner_detail = 20 [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_gepvl"] properties/0/path = NodePath(".:position") properties/0/spawn = true properties/0/replication_mode = 2 properties/1/path = NodePath("GunContainer:rotation") properties/1/spawn = true properties/1/replication_mode = 2 [node name="Player" type="CharacterBody2D" groups=["GRP_PLAYER"]] collision_layer = 2 script = ExtResource("1_gheds") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] visible = false shape = SubResource("CircleShape2D_yh8w5") [node name="GunContainer" type="Node2D" parent="."] unique_name_in_owner = true [node name="Gun" type="Node2D" parent="GunContainer"] position = Vector2(10, 0) [node name="Panel" type="Panel" parent="GunContainer/Gun"] offset_top = -2.0 offset_right = 6.0 offset_bottom = 2.0 theme_override_styles/panel = SubResource("StyleBoxFlat_0phbn") [node name="Muzzle" type="Node2D" parent="GunContainer/Gun"] unique_name_in_owner = true position = Vector2(5, 0) [node name="Panel" type="Panel" parent="."] offset_left = -10.0 offset_top = -10.0 offset_right = 10.0 offset_bottom = 10.0 theme_override_styles/panel = SubResource("StyleBoxFlat_2p5sl") [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] replication_config = SubResource("SceneReplicationConfig_gepvl") ================================================ FILE: sample/game/entities/wall/wall.tscn ================================================ [gd_scene load_steps=3 format=3 uid="uid://dnt3dorvwuw5y"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_0hwfu"] size = Vector2(32, 32) [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kvwfc"] bg_color = Color(0.0889726, 0.111937, 0.180319, 1) border_width_left = 1 border_width_top = 1 border_width_right = 1 border_width_bottom = 1 border_color = Color(0.0498713, 0.0669275, 0.115281, 1) border_blend = true [node name="Wall" type="StaticBody2D"] [node name="CollisionShape2D" type="CollisionShape2D" parent="."] visible = false shape = SubResource("RectangleShape2D_0hwfu") [node name="Panel" type="Panel" parent="."] offset_left = -16.0 offset_top = -16.0 offset_right = 16.0 offset_bottom = 16.0 theme_override_styles/panel = SubResource("StyleBoxFlat_kvwfc") ================================================ FILE: sample/game/maps/map_bellandur.tscn ================================================ [gd_scene load_steps=4 format=4 uid="uid://cq20ci5f675b8"] [ext_resource type="PackedScene" uid="uid://dnt3dorvwuw5y" path="res://game/entities/wall/wall.tscn" id="1_r6dfo"] [sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_hd8jp"] scenes/1/scene = ExtResource("1_r6dfo") [sub_resource type="TileSet" id="TileSet_1qt5j"] tile_size = Vector2i(32, 32) sources/0 = SubResource("TileSetScenesCollectionSource_hd8jp") [node name="MapBellandur" type="Node2D" groups=["GRP_MAP"]] [node name="TileMapLayer" type="TileMapLayer" parent="."] tile_map_data = PackedByteArray("AAAAAAAAAAAAAAAAAQABAAAAAAAAAAAAAQACAAAAAAAAAAAAAQADAAAAAAAAAAAAAQAAAAEAAAAAAAAAAQAAAAIAAAAAAAAAAQAAAAMAAAAAAAAAAQAAAAQAAAAAAAAAAQAEAAAAAAAAAAAAAQAFAAAAAAAAAAAAAQAGAAAAAAAAAAAAAQAHAAAAAAAAAAAAAQAIAAAAAAAAAAAAAQAJAAAAAAAAAAAAAQAAAAUAAAAAAAAAAQAAAAYAAAAAAAAAAQAAAAcAAAAAAAAAAQAAAAgAAAAAAAAAAQAAAAkAAAAAAAAAAQAAAAoAAAAAAAAAAQAAAAsAAAAAAAAAAQAAAAwAAAAAAAAAAQAAAA0AAAAAAAAAAQAAAA4AAAAAAAAAAQAAAA8AAAAAAAAAAQAAABAAAAAAAAAAAQAAABEAAAAAAAAAAQAAABIAAAAAAAAAAQAAABMAAAAAAAAAAQAiAAAAAAAAAAAAAQAhAAAAAAAAAAAAAQAgAAAAAAAAAAAAAQAfAAAAAAAAAAAAAQAeAAAAAAAAAAAAAQAdAAAAAAAAAAAAAQAcAAAAAAAAAAAAAQAbAAAAAAAAAAAAAQAaAAAAAAAAAAAAAQAZAAAAAAAAAAAAAQAYAAAAAAAAAAAAAQAXAAAAAAAAAAAAAQAWAAAAAAAAAAAAAQAVAAAAAAAAAAAAAQAUAAAAAAAAAAAAAQATAAAAAAAAAAAAAQASAAAAAAAAAAAAAQARAAAAAAAAAAAAAQAQAAAAAAAAAAAAAQAPAAAAAAAAAAAAAQAOAAAAAAAAAAAAAQANAAAAAAAAAAAAAQAMAAAAAAAAAAAAAQALAAAAAAAAAAAAAQAKAAAAAAAAAAAAAQAjAAAAAAAAAAAAAQAjAAEAAAAAAAAAAQAjAAIAAAAAAAAAAQAjAAMAAAAAAAAAAQAjAAQAAAAAAAAAAQAjAAUAAAAAAAAAAQAjAAYAAAAAAAAAAQAjAAcAAAAAAAAAAQAjAAgAAAAAAAAAAQAjAAkAAAAAAAAAAQAjAAoAAAAAAAAAAQAjAAsAAAAAAAAAAQAjAAwAAAAAAAAAAQAjAA0AAAAAAAAAAQAjAA4AAAAAAAAAAQAjAA8AAAAAAAAAAQAjABAAAAAAAAAAAQAjABEAAAAAAAAAAQAjABIAAAAAAAAAAQAjABMAAAAAAAAAAQAiABMAAAAAAAAAAQAhABMAAAAAAAAAAQAgABMAAAAAAAAAAQAfABMAAAAAAAAAAQAeABMAAAAAAAAAAQAdABMAAAAAAAAAAQAcABMAAAAAAAAAAQAbABMAAAAAAAAAAQAaABMAAAAAAAAAAQAZABMAAAAAAAAAAQAYABMAAAAAAAAAAQAXABMAAAAAAAAAAQAWABMAAAAAAAAAAQAVABMAAAAAAAAAAQAUABMAAAAAAAAAAQATABMAAAAAAAAAAQASABMAAAAAAAAAAQARABMAAAAAAAAAAQAQABMAAAAAAAAAAQAPABMAAAAAAAAAAQAOABMAAAAAAAAAAQANABMAAAAAAAAAAQAMABMAAAAAAAAAAQALABMAAAAAAAAAAQAKABMAAAAAAAAAAQAJABMAAAAAAAAAAQAIABMAAAAAAAAAAQAHABMAAAAAAAAAAQAGABMAAAAAAAAAAQAFABMAAAAAAAAAAQAEABMAAAAAAAAAAQADABMAAAAAAAAAAQACABMAAAAAAAAAAQABABMAAAAAAAAAAQAJAAoAAAAAAAAAAQAKAAoAAAAAAAAAAQALAAoAAAAAAAAAAQAMAAoAAAAAAAAAAQANAAoAAAAAAAAAAQAOAAoAAAAAAAAAAQAPAAoAAAAAAAAAAQAQAAoAAAAAAAAAAQARAAoAAAAAAAAAAQASAAoAAAAAAAAAAQATAAoAAAAAAAAAAQAUAAoAAAAAAAAAAQAVAAoAAAAAAAAAAQAWAAoAAAAAAAAAAQAXAAoAAAAAAAAAAQAYAAoAAAAAAAAAAQAZAAoAAAAAAAAAAQAaAAoAAAAAAAAAAQARAAUAAAAAAAAAAQARAAYAAAAAAAAAAQARAAcAAAAAAAAAAQARAAgAAAAAAAAAAQARAAkAAAAAAAAAAQARAAsAAAAAAAAAAQARAAwAAAAAAAAAAQARAA0AAAAAAAAAAQARAA4AAAAAAAAAAQARAA8AAAAAAAAAAQA=") tile_set = SubResource("TileSet_1qt5j") [node name="SpawnPoints" type="Node2D" parent="."] [node name="Node2D" type="Node2D" parent="SpawnPoints"] position = Vector2(390, 210) [node name="Node2D2" type="Node2D" parent="SpawnPoints"] position = Vector2(732, 437) [node name="Node2D3" type="Node2D" parent="SpawnPoints"] position = Vector2(714, 216) [node name="Node2D4" type="Node2D" parent="SpawnPoints"] position = Vector2(397, 439) [node name="MapName" type="Label" parent="."] offset_left = 8.0 offset_top = 3.0 offset_right = 67.0 offset_bottom = 26.0 theme_override_colors/font_color = Color(0.52312, 0.52312, 0.52312, 1) text = "Bellandur" ================================================ FILE: sample/game/maps/map_margao.tscn ================================================ [gd_scene load_steps=4 format=4 uid="uid://d25abb5oqolnx"] [ext_resource type="PackedScene" uid="uid://dnt3dorvwuw5y" path="res://game/entities/wall/wall.tscn" id="1_ccu0p"] [sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_hd8jp"] scenes/1/scene = ExtResource("1_ccu0p") [sub_resource type="TileSet" id="TileSet_1qt5j"] tile_size = Vector2i(32, 32) sources/0 = SubResource("TileSetScenesCollectionSource_hd8jp") [node name="MapMargao" type="Node2D" groups=["GRP_MAP"]] [node name="TileMapLayer" type="TileMapLayer" parent="."] tile_map_data = PackedByteArray("AAAAAAAAAAAAAAAAAQABAAAAAAAAAAAAAQACAAAAAAAAAAAAAQADAAAAAAAAAAAAAQAAAAEAAAAAAAAAAQAAAAIAAAAAAAAAAQAAAAMAAAAAAAAAAQAAAAQAAAAAAAAAAQAEAAAAAAAAAAAAAQAFAAAAAAAAAAAAAQAGAAAAAAAAAAAAAQAHAAAAAAAAAAAAAQAIAAAAAAAAAAAAAQAJAAAAAAAAAAAAAQAAAAUAAAAAAAAAAQAAAAYAAAAAAAAAAQAAAAcAAAAAAAAAAQAAAAgAAAAAAAAAAQAAAAkAAAAAAAAAAQAAAAoAAAAAAAAAAQAAAAsAAAAAAAAAAQAAAAwAAAAAAAAAAQAAAA0AAAAAAAAAAQAAAA4AAAAAAAAAAQAAAA8AAAAAAAAAAQAAABAAAAAAAAAAAQAAABEAAAAAAAAAAQAAABIAAAAAAAAAAQAAABMAAAAAAAAAAQAiAAAAAAAAAAAAAQAhAAAAAAAAAAAAAQAgAAAAAAAAAAAAAQAfAAAAAAAAAAAAAQAeAAAAAAAAAAAAAQAdAAAAAAAAAAAAAQAcAAAAAAAAAAAAAQAbAAAAAAAAAAAAAQAaAAAAAAAAAAAAAQAZAAAAAAAAAAAAAQAYAAAAAAAAAAAAAQAXAAAAAAAAAAAAAQAWAAAAAAAAAAAAAQAVAAAAAAAAAAAAAQAUAAAAAAAAAAAAAQATAAAAAAAAAAAAAQASAAAAAAAAAAAAAQARAAAAAAAAAAAAAQAQAAAAAAAAAAAAAQAPAAAAAAAAAAAAAQAOAAAAAAAAAAAAAQANAAAAAAAAAAAAAQAMAAAAAAAAAAAAAQALAAAAAAAAAAAAAQAKAAAAAAAAAAAAAQAjAAAAAAAAAAAAAQAjAAEAAAAAAAAAAQAjAAIAAAAAAAAAAQAjAAMAAAAAAAAAAQAjAAQAAAAAAAAAAQAjAAUAAAAAAAAAAQAjAAYAAAAAAAAAAQAjAAcAAAAAAAAAAQAjAAgAAAAAAAAAAQAjAAkAAAAAAAAAAQAjAAoAAAAAAAAAAQAjAAsAAAAAAAAAAQAjAAwAAAAAAAAAAQAjAA0AAAAAAAAAAQAjAA4AAAAAAAAAAQAjAA8AAAAAAAAAAQAjABAAAAAAAAAAAQAjABEAAAAAAAAAAQAjABIAAAAAAAAAAQAjABMAAAAAAAAAAQAiABMAAAAAAAAAAQAhABMAAAAAAAAAAQAgABMAAAAAAAAAAQAfABMAAAAAAAAAAQAeABMAAAAAAAAAAQAdABMAAAAAAAAAAQAcABMAAAAAAAAAAQAbABMAAAAAAAAAAQAaABMAAAAAAAAAAQAZABMAAAAAAAAAAQAYABMAAAAAAAAAAQAXABMAAAAAAAAAAQAWABMAAAAAAAAAAQAVABMAAAAAAAAAAQAUABMAAAAAAAAAAQATABMAAAAAAAAAAQASABMAAAAAAAAAAQARABMAAAAAAAAAAQAQABMAAAAAAAAAAQAPABMAAAAAAAAAAQAOABMAAAAAAAAAAQANABMAAAAAAAAAAQAMABMAAAAAAAAAAQALABMAAAAAAAAAAQAKABMAAAAAAAAAAQAJABMAAAAAAAAAAQAIABMAAAAAAAAAAQAHABMAAAAAAAAAAQAGABMAAAAAAAAAAQAFABMAAAAAAAAAAQAEABMAAAAAAAAAAQADABMAAAAAAAAAAQACABMAAAAAAAAAAQABABMAAAAAAAAAAQAJAAIAAAAAAAAAAQAJAAMAAAAAAAAAAQAJAAQAAAAAAAAAAQAdAA4AAAAAAAAAAQAdAA0AAAAAAAAAAQAdAAwAAAAAAAAAAQA=") tile_set = SubResource("TileSet_1qt5j") [node name="SpawnPoints" type="Node2D" parent="."] [node name="Node2D" type="Node2D" parent="SpawnPoints"] position = Vector2(117, 97) [node name="Node2D2" type="Node2D" parent="SpawnPoints"] position = Vector2(1032, 542) [node name="Node2D3" type="Node2D" parent="SpawnPoints"] position = Vector2(1039, 99) [node name="Node2D4" type="Node2D" parent="SpawnPoints"] position = Vector2(112, 546) [node name="MapName" type="Label" parent="."] offset_left = 8.0 offset_top = 3.0 offset_right = 67.0 offset_bottom = 26.0 theme_override_colors/font_color = Color(0.52312, 0.52312, 0.52312, 1) text = "Margao" ================================================ FILE: sample/game/maps/map_new_york.tscn ================================================ [gd_scene load_steps=4 format=4 uid="uid://cutu8g5suwurd"] [ext_resource type="PackedScene" uid="uid://dnt3dorvwuw5y" path="res://game/entities/wall/wall.tscn" id="1_74dyf"] [sub_resource type="TileSetScenesCollectionSource" id="TileSetScenesCollectionSource_hd8jp"] scenes/1/scene = ExtResource("1_74dyf") [sub_resource type="TileSet" id="TileSet_1qt5j"] tile_size = Vector2i(32, 32) sources/0 = SubResource("TileSetScenesCollectionSource_hd8jp") [node name="MapNewYork" type="Node2D" groups=["GRP_MAP"]] [node name="TileMapLayer" type="TileMapLayer" parent="."] tile_map_data = PackedByteArray("AAAAAAAAAAAAAAAAAQABAAAAAAAAAAAAAQACAAAAAAAAAAAAAQADAAAAAAAAAAAAAQAAAAEAAAAAAAAAAQAAAAIAAAAAAAAAAQAAAAMAAAAAAAAAAQAAAAQAAAAAAAAAAQAEAAAAAAAAAAAAAQAFAAAAAAAAAAAAAQAGAAAAAAAAAAAAAQAHAAAAAAAAAAAAAQAIAAAAAAAAAAAAAQAJAAAAAAAAAAAAAQAAAAUAAAAAAAAAAQAAAAYAAAAAAAAAAQAAAAcAAAAAAAAAAQAAAAgAAAAAAAAAAQAAAAkAAAAAAAAAAQAAAAoAAAAAAAAAAQAAAAsAAAAAAAAAAQAAAAwAAAAAAAAAAQAAAA0AAAAAAAAAAQAAAA4AAAAAAAAAAQAAAA8AAAAAAAAAAQAAABAAAAAAAAAAAQAAABEAAAAAAAAAAQAAABIAAAAAAAAAAQAAABMAAAAAAAAAAQAiAAAAAAAAAAAAAQAhAAAAAAAAAAAAAQAgAAAAAAAAAAAAAQAfAAAAAAAAAAAAAQAeAAAAAAAAAAAAAQAdAAAAAAAAAAAAAQAcAAAAAAAAAAAAAQAbAAAAAAAAAAAAAQAaAAAAAAAAAAAAAQAZAAAAAAAAAAAAAQAYAAAAAAAAAAAAAQAXAAAAAAAAAAAAAQAWAAAAAAAAAAAAAQAVAAAAAAAAAAAAAQAUAAAAAAAAAAAAAQATAAAAAAAAAAAAAQASAAAAAAAAAAAAAQARAAAAAAAAAAAAAQAQAAAAAAAAAAAAAQAPAAAAAAAAAAAAAQAOAAAAAAAAAAAAAQANAAAAAAAAAAAAAQAMAAAAAAAAAAAAAQALAAAAAAAAAAAAAQAKAAAAAAAAAAAAAQAjAAAAAAAAAAAAAQAjAAEAAAAAAAAAAQAjAAIAAAAAAAAAAQAjAAMAAAAAAAAAAQAjAAQAAAAAAAAAAQAjAAUAAAAAAAAAAQAjAAYAAAAAAAAAAQAjAAcAAAAAAAAAAQAjAAgAAAAAAAAAAQAjAAkAAAAAAAAAAQAjAAoAAAAAAAAAAQAjAAsAAAAAAAAAAQAjAAwAAAAAAAAAAQAjAA0AAAAAAAAAAQAjAA4AAAAAAAAAAQAjAA8AAAAAAAAAAQAjABAAAAAAAAAAAQAjABEAAAAAAAAAAQAjABIAAAAAAAAAAQAjABMAAAAAAAAAAQAiABMAAAAAAAAAAQAhABMAAAAAAAAAAQAgABMAAAAAAAAAAQAfABMAAAAAAAAAAQAeABMAAAAAAAAAAQAdABMAAAAAAAAAAQAcABMAAAAAAAAAAQAbABMAAAAAAAAAAQAaABMAAAAAAAAAAQAZABMAAAAAAAAAAQAYABMAAAAAAAAAAQAXABMAAAAAAAAAAQAWABMAAAAAAAAAAQAVABMAAAAAAAAAAQAUABMAAAAAAAAAAQATABMAAAAAAAAAAQASABMAAAAAAAAAAQARABMAAAAAAAAAAQAQABMAAAAAAAAAAQAPABMAAAAAAAAAAQAOABMAAAAAAAAAAQANABMAAAAAAAAAAQAMABMAAAAAAAAAAQALABMAAAAAAAAAAQAKABMAAAAAAAAAAQAJABMAAAAAAAAAAQAIABMAAAAAAAAAAQAHABMAAAAAAAAAAQAGABMAAAAAAAAAAQAFABMAAAAAAAAAAQAEABMAAAAAAAAAAQADABMAAAAAAAAAAQACABMAAAAAAAAAAQABABMAAAAAAAAAAQAMAAcAAAAAAAAAAQANAAcAAAAAAAAAAQAWAAwAAAAAAAAAAQAXAAwAAAAAAAAAAQAXAAcAAAAAAAAAAQAWAAcAAAAAAAAAAQANAAwAAAAAAAAAAQAMAAwAAAAAAAAAAQAEAAcAAAAAAAAAAQAFAAcAAAAAAAAAAQAGAAcAAAAAAAAAAQAHAAcAAAAAAAAAAQAIAAcAAAAAAAAAAQAJAAcAAAAAAAAAAQAKAAcAAAAAAAAAAQALAAcAAAAAAAAAAQANAAMAAAAAAAAAAQANAAQAAAAAAAAAAQANAAUAAAAAAAAAAQANAAYAAAAAAAAAAQAEAAwAAAAAAAAAAQAFAAwAAAAAAAAAAQAGAAwAAAAAAAAAAQAHAAwAAAAAAAAAAQAIAAwAAAAAAAAAAQAJAAwAAAAAAAAAAQAKAAwAAAAAAAAAAQALAAwAAAAAAAAAAQANAA0AAAAAAAAAAQANAA4AAAAAAAAAAQANAA8AAAAAAAAAAQANABAAAAAAAAAAAQAWAAMAAAAAAAAAAQAWAAQAAAAAAAAAAQAWAAUAAAAAAAAAAQAWAAYAAAAAAAAAAQAYAAcAAAAAAAAAAQAZAAcAAAAAAAAAAQAaAAcAAAAAAAAAAQAbAAcAAAAAAAAAAQAcAAcAAAAAAAAAAQAdAAcAAAAAAAAAAQAeAAcAAAAAAAAAAQAfAAcAAAAAAAAAAQAWAA8AAAAAAAAAAQAWABAAAAAAAAAAAQAYAAwAAAAAAAAAAQAZAAwAAAAAAAAAAQAaAAwAAAAAAAAAAQAbAAwAAAAAAAAAAQAcAAwAAAAAAAAAAQAdAAwAAAAAAAAAAQAeAAwAAAAAAAAAAQAfAAwAAAAAAAAAAQAWAA0AAAAAAAAAAQAWAA4AAAAAAAAAAQA=") tile_set = SubResource("TileSet_1qt5j") [node name="SpawnPoints" type="Node2D" parent="."] [node name="Node2D" type="Node2D" parent="SpawnPoints"] position = Vector2(361, 176) [node name="Node2D2" type="Node2D" parent="SpawnPoints"] position = Vector2(777, 474) [node name="Node2D3" type="Node2D" parent="SpawnPoints"] position = Vector2(787, 184) [node name="Node2D4" type="Node2D" parent="SpawnPoints"] position = Vector2(358, 464) [node name="MapName" type="Label" parent="."] offset_left = 8.0 offset_top = 3.0 offset_right = 67.0 offset_bottom = 26.0 theme_override_colors/font_color = Color(0.52312, 0.52312, 0.52312, 1) text = "New York" ================================================ FILE: sample/project.godot ================================================ ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. ; ; Format: ; [section] ; section goes between [] ; param=value ; assign values to parameters config_version=5 [animation] compatibility/default_parent_skeleton_in_mesh_instance_3d=true [application] config/name="Epic Online Services Godot 4.2+ (EOSG)" run/main_scene="res://Main.tscn" config/features=PackedStringArray("4.5") config/icon="res://logo.png" [audio] driver/enable_input=true driver/mix_rate=48000 [autoload] Env="*res://scripts/Env.gd" Store="*res://scripts/Store.gd" EOSGRuntime="*res://addons/epic-online-services-godot/runtime.gd" HPlatform="*res://addons/epic-online-services-godot/heos/hplatform.gd" HAuth="*res://addons/epic-online-services-godot/heos/hauth.gd" HAchievements="*res://addons/epic-online-services-godot/heos/hachievements.gd" HFriends="*res://addons/epic-online-services-godot/heos/hfriends.gd" HStats="*res://addons/epic-online-services-godot/heos/hstats.gd" HLeaderboards="*res://addons/epic-online-services-godot/heos/hleaderboards.gd" HLobbies="*res://addons/epic-online-services-godot/heos/hlobbies.gd" HP2P="*res://addons/epic-online-services-godot/heos/hp2p.gd" HSessions="*res://addons/epic-online-services-godot/heos/hsessions.gd" [display] window/size/viewport_height=640 window/stretch/mode="canvas_items" window/dpi/allow_hidpi=false [dotnet] project/assembly_name="Epic Online Services Godot 4.2+ (EOSG)" [editor_plugins] enabled=PackedStringArray("res://addons/epic-online-services-godot/plugin.cfg") [global_group] GRP_MAP="" GRP_PLAYER="" [input] move_up={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) ] } move_down={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) ] } move_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) ] } move_right={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) ] } shoot={ "deadzone": 0.5, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) ] } push_to_talk={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null) ] } [physics] common/enable_pause_aware_picking=true [rendering] renderer/rendering_method="gl_compatibility" renderer/rendering_method.mobile="gl_compatibility" textures/vram_compression/import_etc2_astc=true environment/defaults/default_clear_color=Color(0.121569, 0.121569, 0.121569, 1) environment/defaults/default_environment="res://default_env.tres" ================================================ FILE: sample/scenes/AchievementsView/AchievementPopup.gd ================================================ class_name AchievementPopup extends StyledPopupWindow @onready var id_label = $VB/MC/VB/GridContainer/IdLabel @onready var flavor_label = $VB/MC/VB/GridContainer/FlavorLabel @onready var is_visible_label = $VB/MC/VB/GridContainer/IsVisibleLabel @onready var is_unlocked_label = $VB/MC/VB/GridContainer/IsUnlockedLabel @onready var stat_thresholds_vb = $VB/MC/VB/MC/VB/SC/StatThresholdsVB @onready var locked_image = $VB/MC/VB/HB/VB/LockedImage @onready var locked_label = $VB/MC/VB/HB/VB/LockedLabel @onready var locked_desc_label = $VB/MC/VB/HB/VB/LockedDescLabel @onready var unlocked_image = $VB/MC/VB/HB/VB2/UnlockedImage @onready var unlocked_label = $VB/MC/VB/HB/VB2/UnlockedLabel @onready var unlocked_desc_label = $VB/MC/VB/HB/VB2/UnlockedDescLabel @onready var unlock_btn = $VB/MC/VB/UnlockBtn var achievement_node: AchievementsListAchievement func _ready() -> void: super._ready() unlock_btn.pressed.connect(_on_unlock_btn_pressed) func from_achievement_node(node: AchievementsListAchievement): achievement_node = node var data := achievement_node.get_data() id_label.text = data.achievement_id flavor_label.text = data.flavor_text if data.is_hidden != null: if data.is_hidden: is_visible_label.text = "False" else: is_visible_label.text = "True" else: is_visible_label.text = "NA" if not data.is_unlocked(): is_unlocked_label.text = "No" unlock_btn.disabled = false else: var time_str = Time.get_datetime_string_from_unix_time(data.unlock_time) is_unlocked_label.text = "Yes at " + time_str unlock_btn.disabled = true for child in stat_thresholds_vb.get_children(): child.queue_free() for stat in data.stats: var label = Label.new() label.text = "%s : %s" % [stat.name, stat.threshold_value] stat_thresholds_vb.add_child(label) locked_image.fetch_image(data.locked_icon_url) locked_label.text = "Locked Name: " + data.locked_display_name locked_desc_label.text = "Locked Desc: " + data.locked_description unlocked_image.fetch_image(data.unlocked_icon_url) unlocked_label.text = "Locked Name: " + data.unlocked_display_name unlocked_desc_label.text = "Locked Desc: " + data.unlocked_description func _on_unlock_btn_pressed(): var data = achievement_node.get_data() unlock_btn.disabled = true visible = false HAchievements.unlock_achievement_async(data.achievement_id) ================================================ FILE: sample/scenes/AchievementsView/AchievementPopup.gd.uid ================================================ uid://b8fpdhlcd5vf2 ================================================ FILE: sample/scenes/AchievementsView/AchievementUnlockNotification.gd ================================================ class_name AchievementUnlockNotification extends Control var _data: HAchievementData @onready var id_label = $MC/HB/VB/IdLabel @onready var name_label = $MC/HB/VB/NameLabel @onready var unlocked_image = $MC/HB/UnlockedImage @onready var anim = $Anim @onready var fade_out_timer = $FadeOutTimer func _ready() -> void: fade_out_timer.timeout.connect(_on_fade_out_timer_timeout) func from_achievement_data(data: HAchievementData): _data = data name_label.text = data.unlocked_display_name id_label.text = data.achievement_id if data.unlocked_icon_url != null: unlocked_image.fetch_image(data.unlocked_icon_url) else: unlocked_image.fetch_image(data.icon_url) func fade_in(): anim.play("FadeIn") fade_out_timer.start() func _on_fade_out_timer_timeout(): anim.play_backwards("FadeIn") await anim.animation_finished queue_free() ================================================ FILE: sample/scenes/AchievementsView/AchievementUnlockNotification.gd.uid ================================================ uid://dnm64cf611a0h ================================================ FILE: sample/scenes/AchievementsView/AchievementUnlockNotification.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://b7hrrwwvjqycs"] [ext_resource type="PackedScene" uid="uid://djhhnis3ksdjk" path="res://scenes/UI/NetworkImage.tscn" id="1"] [ext_resource type="Script" uid="uid://dnm64cf611a0h" path="res://scenes/AchievementsView/AchievementUnlockNotification.gd" id="2"] [sub_resource type="StyleBoxFlat" id="1"] bg_color = Color(0.0588235, 0.0588235, 0.0588235, 1) border_width_left = 2 border_width_top = 2 border_width_right = 2 border_width_bottom = 2 border_color = Color(0.878431, 0.886275, 0.207843, 1) border_blend = true corner_radius_top_left = 8 corner_radius_top_right = 8 corner_radius_bottom_right = 8 corner_radius_bottom_left = 8 [sub_resource type="Animation" id="2"] resource_name = "FadeIn" length = 0.35 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath(".:modulate") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0, 0.35), "transitions": PackedFloat32Array(1, 0.5), "update": 0, "values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] } [sub_resource type="Animation" id="3"] length = 0.001 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath(".:modulate") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 0, "values": [Color(0, 0, 0, 1)] } [node name="AchievementUnlockNotification" type="MarginContainer"] modulate = Color(0, 0, 0, 1) offset_right = 338.0 offset_bottom = 80.0 script = ExtResource("2") __meta__ = { "_edit_use_anchors_": false } [node name="BG" type="PanelContainer" parent="."] offset_right = 338.0 offset_bottom = 80.0 theme_override_styles/panel = SubResource("1") [node name="MC" type="MarginContainer" parent="."] offset_right = 338.0 offset_bottom = 80.0 mouse_filter = 2 theme_override_constants/margin_right = 8 theme_override_constants/margin_top = 8 theme_override_constants/margin_left = 8 theme_override_constants/margin_bottom = 8 [node name="HB" type="HBoxContainer" parent="MC"] offset_left = 8.0 offset_top = 8.0 offset_right = 330.0 offset_bottom = 72.0 mouse_filter = 2 theme_override_constants/separation = 8 __meta__ = { "_edit_use_anchors_": false } [node name="UnlockedImage" parent="MC/HB" instance=ExtResource("1")] size_flags_vertical = 0 [node name="VB" type="VBoxContainer" parent="MC/HB"] offset_left = 72.0 offset_right = 322.0 offset_bottom = 64.0 mouse_filter = 2 [node name="Unlocked" type="Label" parent="MC/HB/VB"] offset_right = 250.0 offset_bottom = 14.0 custom_minimum_size = Vector2(250, 0) text = "New Achievement Unlocked!" autowrap = true [node name="NameLabel" type="Label" parent="MC/HB/VB"] offset_top = 18.0 offset_right = 250.0 offset_bottom = 32.0 custom_minimum_size = Vector2(250, 0) text = "Name here" autowrap = true [node name="IdLabel" type="Label" parent="MC/HB/VB"] offset_top = 36.0 offset_right = 250.0 offset_bottom = 50.0 custom_minimum_size = Vector2(250, 0) text = "ID: 123" autowrap = true [node name="Anim" type="AnimationPlayer" parent="."] anims/FadeIn = SubResource("2") anims/RESET = SubResource("3") [node name="FadeOutTimer" type="Timer" parent="."] wait_time = 7.0 one_shot = true ================================================ FILE: sample/scenes/AchievementsView/AchievementsList.gd ================================================ class_name AchievementsList extends MarginContainer signal achievement_pressed(node) const ACHIEVEMENTS_LIST_ACHIEVEMENT = preload("res://scenes/AchievementsView/AchievementsListAchievement.tscn") func _ready() -> void: _clear() func _clear() -> void: # Clear existing achievements for child in $SC/VB.get_children(): child.queue_free() func from_achievements_array(arr: Array): _clear() # Populate new achievements for achievement_data in arr: var achievement = ACHIEVEMENTS_LIST_ACHIEVEMENT.instantiate() $SC/VB.add_child(achievement) achievement.pressed.connect(_on_achievement_pressed) achievement.from_achievement_data(achievement_data) func _on_achievement_pressed(node: AchievementsListAchievement): achievement_pressed.emit(node) ================================================ FILE: sample/scenes/AchievementsView/AchievementsList.gd.uid ================================================ uid://27jk4xa1yfq ================================================ FILE: sample/scenes/AchievementsView/AchievementsListAchievement.gd ================================================ class_name AchievementsListAchievement extends MarginContainer # warning-ignore:unused_signal signal pressed(_self) @onready var id_label = $MC/HB/VB/IdLabel @onready var unlocked_image = $MC/HB/UnlockedImage as NetworkImage @onready var button = $RR/Button var _data: HAchievementData func _ready(): button.pressed.connect(func (): pressed.emit(self) ) func from_achievement_data(data: HAchievementData): _data = data id_label.text = data.achievement_id if data.unlocked_icon_url != null: unlocked_image.fetch_image(data.unlocked_icon_url) else: unlocked_image.fetch_image(data.icon_url) func get_data() -> HAchievementData: return _data ================================================ FILE: sample/scenes/AchievementsView/AchievementsListAchievement.gd.uid ================================================ uid://cuosdhat5m7tg ================================================ FILE: sample/scenes/AchievementsView/AchievementsListAchievement.tscn ================================================ [gd_scene load_steps=5 format=3 uid="uid://blvi28uiveh0a"] [ext_resource type="Script" uid="uid://cuosdhat5m7tg" path="res://scenes/AchievementsView/AchievementsListAchievement.gd" id="1"] [ext_resource type="PackedScene" uid="uid://djhhnis3ksdjk" path="res://scenes/UI/NetworkImage.tscn" id="2"] [sub_resource type="StyleBoxFlat" id="2"] bg_color = Color(0.329412, 0.329412, 0.329412, 1) corner_radius_top_left = 4 corner_radius_top_right = 4 corner_radius_bottom_right = 4 corner_radius_bottom_left = 4 [sub_resource type="StyleBoxFlat" id="1"] bg_color = Color(0.2, 0.2, 0.2, 1) corner_radius_top_left = 4 corner_radius_top_right = 4 corner_radius_bottom_right = 4 corner_radius_bottom_left = 4 [node name="AchievementsListAchievement" type="MarginContainer"] offset_right = 326.0 offset_bottom = 72.0 script = ExtResource("1") __meta__ = { "_edit_use_anchors_": false } [node name="RR" type="ReferenceRect" parent="."] offset_right = 326.0 offset_bottom = 72.0 border_color = Color(1, 0, 0, 0) [node name="Button" type="Button" parent="RR"] anchor_right = 1.0 anchor_bottom = 1.0 mouse_default_cursor_shape = 2 theme_override_styles/hover = SubResource("2") theme_override_styles/pressed = SubResource("2") theme_override_styles/focus = SubResource("2") theme_override_styles/normal = SubResource("1") __meta__ = { "_edit_use_anchors_": false } [node name="MC" type="MarginContainer" parent="."] offset_right = 326.0 offset_bottom = 72.0 mouse_filter = 2 theme_override_constants/margin_right = 4 theme_override_constants/margin_top = 4 theme_override_constants/margin_left = 4 theme_override_constants/margin_bottom = 4 [node name="HB" type="HBoxContainer" parent="MC"] offset_left = 4.0 offset_top = 4.0 offset_right = 322.0 offset_bottom = 68.0 mouse_filter = 2 __meta__ = { "_edit_use_anchors_": false } [node name="UnlockedImage" parent="MC/HB" instance=ExtResource("2")] [node name="VB" type="VBoxContainer" parent="MC/HB"] offset_left = 68.0 offset_right = 318.0 offset_bottom = 64.0 mouse_filter = 2 [node name="IdLabel" type="Label" parent="MC/HB/VB"] offset_right = 250.0 offset_bottom = 14.0 custom_minimum_size = Vector2(250, 0) text = "ID: achievement_id_here_123456789" autowrap = true ================================================ FILE: sample/scenes/AchievementsView/AchievementsView.gd ================================================ class_name AchievementsView extends VBoxContainer # Local cache of achievements var achievements = {} @onready var _achievements_list = %AchievementsList @onready var _achievement_popup = %AchievementPopup @onready var refresh_btn = %RefreshBtn @onready var AchievementUnlockNotificationScene = preload ("res://scenes/AchievementsView/AchievementUnlockNotification.tscn") func _ready() -> void: HAchievements.achievement_unlocked.connect(_on_achievement_unlocked) visibility_changed.connect(_get_achievements) HAuth.logged_out.connect(_on_logged_out) _achievements_list.achievement_pressed.connect(_on_achievement_pressed) refresh_btn.pressed.connect(_get_achievements) func _get_achievements(): if not visible: return var achievements_arr := await HAchievements.get_all_achievements_async() for achievement in achievements_arr: achievements[achievement.achievement_id] = achievement var player_achievements_arr := await HAchievements.get_player_achievements_async() for player_achievement in player_achievements_arr: achievements[player_achievement.achievement_id].unlock_time = player_achievement.unlock_time _rebuild_ui() func _on_logged_out(): achievements = {} _rebuild_ui() func _rebuild_ui(): _achievements_list.from_achievements_array(achievements.values()) func _on_achievement_pressed(node: AchievementsListAchievement): _achievement_popup.from_achievement_node(node) _achievement_popup.popup_centered() func _on_achievement_unlocked(data: Dictionary): var achievement_id = data.achievement_id if achievements.has(achievement_id): achievements[data.achievement_id].unlock_time = data.unlock_time _rebuild_ui() # Show achievement unlocked notification var notif = AchievementUnlockNotificationScene.instantiate() Store.get_view("Notifications").add_notification(notif) notif.from_achievement_data(achievements[data.achievement_id]) ================================================ FILE: sample/scenes/AchievementsView/AchievementsView.gd.uid ================================================ uid://cjhi71075hj1y ================================================ FILE: sample/scenes/AchievementsView/AchievementsView.tscn ================================================ [gd_scene load_steps=12 format=3 uid="uid://d3hxdkoy73pf1"] [ext_resource type="Script" uid="uid://cjhi71075hj1y" path="res://scenes/AchievementsView/AchievementsView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_1831t"] [ext_resource type="PackedScene" uid="uid://djhhnis3ksdjk" path="res://scenes/UI/NetworkImage.tscn" id="3"] [ext_resource type="Script" uid="uid://27jk4xa1yfq" path="res://scenes/AchievementsView/AchievementsList.gd" id="4"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [ext_resource type="Script" uid="uid://b8fpdhlcd5vf2" path="res://scenes/AchievementsView/AchievementPopup.gd" id="6"] [ext_resource type="PackedScene" uid="uid://blvi28uiveh0a" path="res://scenes/AchievementsView/AchievementsListAchievement.tscn" id="7"] [ext_resource type="PackedScene" uid="uid://dkprjbptror6n" path="res://components/StyledPopupWindow/StyledPopupWindow.tscn" id="8_37km7"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="8_75ml2"] [sub_resource type="StyleBoxEmpty" id="1"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6nfih"] [node name="AchievementsView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Achievements" label_settings = ExtResource("2_1831t") [node name="MC" type="MarginContainer" parent="."] layout_mode = 2 theme_override_constants/margin_top = 8 theme_override_constants/margin_bottom = 8 [node name="HBoxContainer" type="HBoxContainer" parent="MC"] layout_mode = 2 [node name="RefreshBtn" parent="MC/HBoxContainer" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Refresh" [node name="RR" type="ReferenceRect" parent="."] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 border_color = Color(1, 0, 0, 0) [node name="AchievementsList" type="MarginContainer" parent="RR"] unique_name_in_owner = true layout_mode = 0 anchor_right = 1.0 anchor_bottom = 1.0 script = ExtResource("4") [node name="SC" type="ScrollContainer" parent="RR/AchievementsList"] layout_mode = 2 [node name="VB" type="VBoxContainer" parent="RR/AchievementsList/SC"] layout_mode = 2 theme_override_constants/separation = 12 [node name="AchievementsListAchievement" parent="RR/AchievementsList/SC/VB" instance=ExtResource("7")] layout_mode = 2 [node name="AchievementsListAchievement2" parent="RR/AchievementsList/SC/VB" instance=ExtResource("7")] layout_mode = 2 [node name="AchievementPopup" parent="." instance=ExtResource("8_37km7")] unique_name_in_owner = true title = "VIEW ACHIEVEMENT" size = Vector2i(572, 628) visible = false script = ExtResource("6") [node name="VB" parent="AchievementPopup" index="1"] offset_right = 568.0 offset_bottom = 624.0 [node name="GridContainer" type="GridContainer" parent="AchievementPopup/VB/MC/VB" index="0"] layout_mode = 2 theme_override_constants/h_separation = 64 columns = 2 [node name="IdLabelTitle" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Id" [node name="IdLabel" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 [node name="FlavorLabelTitle" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Flavor Text" [node name="FlavorLabel" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 [node name="IsVisibleLabelTitle" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Is Visible" [node name="IsVisibleLabel" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Yes" [node name="IsUnlockedLabelTitle" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Is Unlocked" [node name="IsUnlockedLabel" type="Label" parent="AchievementPopup/VB/MC/VB/GridContainer"] layout_mode = 2 text = "Yes" [node name="HSeparator2" type="HSeparator" parent="AchievementPopup/VB/MC/VB" index="1"] layout_mode = 2 theme_override_constants/separation = 16 [node name="MC" type="MarginContainer" parent="AchievementPopup/VB/MC/VB" index="2"] layout_mode = 2 theme_override_constants/margin_top = 8 theme_override_constants/margin_bottom = 8 [node name="VB" type="VBoxContainer" parent="AchievementPopup/VB/MC/VB/MC"] layout_mode = 2 [node name="StatThesholdsLabel" type="Label" parent="AchievementPopup/VB/MC/VB/MC/VB"] layout_mode = 2 theme_override_fonts/font = ExtResource("8_75ml2") text = "Stat Thresholds" [node name="SC" type="ScrollContainer" parent="AchievementPopup/VB/MC/VB/MC/VB"] custom_minimum_size = Vector2(0, 50) layout_mode = 2 [node name="StatThresholdsVB" type="VBoxContainer" parent="AchievementPopup/VB/MC/VB/MC/VB/SC"] layout_mode = 2 [node name="Label" type="Label" parent="AchievementPopup/VB/MC/VB/MC/VB/SC/StatThresholdsVB"] layout_mode = 2 text = "Name: Value" [node name="HB" type="HBoxContainer" parent="AchievementPopup/VB/MC/VB" index="3"] layout_mode = 2 [node name="VB" type="VBoxContainer" parent="AchievementPopup/VB/MC/VB/HB"] layout_mode = 2 theme_override_constants/separation = 8 [node name="LockedImage" parent="AchievementPopup/VB/MC/VB/HB/VB" instance=ExtResource("3")] layout_mode = 2 [node name="LockedLabel" type="Label" parent="AchievementPopup/VB/MC/VB/HB/VB"] layout_mode = 2 text = "Locked Name" [node name="LockedDescLabel" type="RichTextLabel" parent="AchievementPopup/VB/MC/VB/HB/VB"] custom_minimum_size = Vector2(250, 0) layout_mode = 2 text = "(LOCK) Desc for Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet blandit lectus, ac eleifend erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus." fit_content = true [node name="VS" type="VSeparator" parent="AchievementPopup/VB/MC/VB/HB"] custom_minimum_size = Vector2(16, 0) layout_mode = 2 [node name="VB2" type="VBoxContainer" parent="AchievementPopup/VB/MC/VB/HB"] layout_mode = 2 theme_override_constants/separation = 8 [node name="UnlockedImage" parent="AchievementPopup/VB/MC/VB/HB/VB2" instance=ExtResource("3")] layout_mode = 2 [node name="UnlockedLabel" type="Label" parent="AchievementPopup/VB/MC/VB/HB/VB2"] layout_mode = 2 text = "Unlocked Name" [node name="UnlockedDescLabel" type="RichTextLabel" parent="AchievementPopup/VB/MC/VB/HB/VB2"] custom_minimum_size = Vector2(250, 0) layout_mode = 2 text = "Desc for Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet blandit lectus, ac eleifend erat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus." fit_content = true [node name="HSeparator" type="HSeparator" parent="AchievementPopup/VB/MC/VB" index="4"] layout_mode = 2 theme_override_styles/separator = SubResource("1") [node name="UnlockBtn" parent="AchievementPopup/VB/MC/VB" index="5" instance=ExtResource("5")] layout_mode = 2 size_flags_horizontal = 8 theme_override_styles/focus = SubResource("StyleBoxEmpty_6nfih") text = "Unlock" [editable path="AchievementPopup"] ================================================ FILE: sample/scenes/CustomInvitesView/CustomInvitesView.gd ================================================ class_name CustomInvitesView extends VBoxContainer @onready var payload_textedit = %PayloadTextEdit @onready var send_invite_btn = %SendInviteBtn func _ready() -> void: IEOS.custom_invites_interface_send_custom_invite_callback.connect(_on_send_custom_invite_callback) IEOS.custom_invites_interface_custom_invite_accepted_callback.connect(_on_custom_invite_accepted_callback) IEOS.custom_invites_interface_custom_invite_received_callback.connect(_on_custom_invite_received_callback) IEOS.custom_invites_interface_custom_invite_rejected_callback.connect(_on_custom_invite_rejected_callback) IEOS.custom_invites_interface_request_to_join_received_callback.connect(_on_request_to_join_received_callback) IEOS.custom_invites_interface_request_to_join_response_received_callback.connect(_on_request_to_join_response_received_callback) IEOS.custom_invites_interface_request_to_join_accepted_callback.connect(_on_request_to_join_accepted_callback) IEOS.custom_invites_interface_request_to_join_rejected_callback.connect(_on_request_to_join_rejected_callback) send_invite_btn.pressed.connect(_on_send_invite_btn_pressed) payload_textedit.text_changed.connect(_on_payload_textedit_text_changed) send_invite_btn.disabled = true func _on_send_custom_invite_callback(data: Dictionary): print("--- Custom Invites: send_custom_invite_callback: ", EOS.result_str(data)) func _on_custom_invite_accepted_callback(data: Dictionary): print("--- Custom Invites: custom_invite_accepted_callback: ", data) var finalize_opts = EOS.CustomInvites.FinalizeInviteOptions.new() finalize_opts.target_user_id = data.target_user_id finalize_opts.custom_invite_id = data.custom_invite_id finalize_opts.processing_result = EOS.Result.Success var res = EOS.CustomInvites.CustomInvitesInterface.finalize_invite(finalize_opts) print("--- Custom Invites: finalize_invite: ", EOS.result_str(res)) func _on_custom_invite_received_callback(data: Dictionary): print("--- Custom Invites: custom_invite_received_callback: ", data) func _on_custom_invite_rejected_callback(data: Dictionary): print("--- Custom Invites: custom_invite_rejected_callback: ", data) func _on_request_to_join_received_callback(data: Dictionary): print("--- Custom Invites: request_to_join_received_callback: ", EOS.result_str(data)) func _on_request_to_join_response_received_callback(data: Dictionary): print("--- Custom Invites: request_to_join_response_received_callback: ", EOS.result_str(data)) func _on_request_to_join_accepted_callback(data: Dictionary): print("--- Custom Invites: request_to_join_accepted_callback: ", EOS.result_str(data)) func _on_request_to_join_rejected_callback(data: Dictionary): print("--- Custom Invites: request_to_join_rejected_callback: ", EOS.result_str(data)) func _on_send_invite_btn_pressed(): var set_invite_opts = EOS.CustomInvites.SetCustomInviteOptions.new() set_invite_opts.payload = payload_textedit.text var set_res: EOS.Result = EOS.CustomInvites.CustomInvitesInterface.set_custom_invite(set_invite_opts) print("--- Custom Invites: set_custom_invite: ", EOS.result_str(set_res)) var send_invite_opts = EOS.CustomInvites.SendCustomInviteOptions.new() send_invite_opts.target_user_ids = [Store.second_product_user_id] EOS.CustomInvites.CustomInvitesInterface.send_custom_invite(send_invite_opts) payload_textedit.text = "" func _on_payload_textedit_text_changed(): if payload_textedit.text.strip_edges() == "": send_invite_btn.disabled = true else: send_invite_btn.disabled = false ================================================ FILE: sample/scenes/CustomInvitesView/CustomInvitesView.gd.uid ================================================ uid://ddkxgj5m43swv ================================================ FILE: sample/scenes/CustomInvitesView/CustomInvitesView.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://d2bkej1dakv6s"] [ext_resource type="Script" uid="uid://ddkxgj5m43swv" path="res://scenes/CustomInvitesView/CustomInvitesView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_u46xl"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [node name="CustomInvitesView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Custom Invites" label_settings = ExtResource("2_u46xl") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 [node name="PayloadTextEdit" type="TextEdit" parent="VB"] unique_name_in_owner = true custom_minimum_size = Vector2(250, 75) layout_mode = 2 size_flags_horizontal = 0 placeholder_text = "Enter payload here" [node name="SendInviteBtn" parent="VB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Send Invite" ================================================ FILE: sample/scenes/FriendsView/FriendsView.gd ================================================ class_name FriendsView extends VBoxContainer # Local cache of friends data var friends = [] @onready var list_title_rich_text_label = %ListTitleRichTextLabel @onready var friend_richtextlabel = %FriendsRichTextLabel func _ready() -> void: visibility_changed.connect(_query_friends) HAuth.logged_out.connect(_on_logged_out) func _query_friends(): if not visible: return if HAuth.epic_account_id == "": print("Unable to query friends. Needs to login with Epic Games account.") return friends = await HFriends.get_friends_async() _rebuild_ui() func _on_logged_out(): friends = [] _rebuild_ui() func _rebuild_ui(): list_title_rich_text_label.text = "[b]Friends List (%s)[/b]" % str(friends.size()) var table_bbcode = """[table=4] [cell][b]Display Name[/b][/cell] [cell][b]User Id[/b][/cell] [cell][b]Nickname[/b][/cell] [cell][b]Country[/b][/cell] %s [/table] """ var rows_bbcode = "" for friend in friends: var nickname = friend.nickname if friend.nickname != "" else "-" var country = friend.country if friend.country != "" else "-" rows_bbcode += "[cell]%s[/cell][cell]%s[/cell][cell]%s[/cell][cell]%s[/cell]" % \ [friend.display_name_sanitized, friend.user_id, nickname, country] friend_richtextlabel.text = table_bbcode % rows_bbcode ================================================ FILE: sample/scenes/FriendsView/FriendsView.gd.uid ================================================ uid://dx8eth83tfuvn ================================================ FILE: sample/scenes/FriendsView/FriendsView.tscn ================================================ [gd_scene load_steps=5 format=3 uid="uid://t016k2yh382d"] [ext_resource type="Script" uid="uid://dx8eth83tfuvn" path="res://scenes/FriendsView/FriendsView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_5qe5o"] [ext_resource type="FontFile" uid="uid://bghbav6kh0lsl" path="res://fonts/roboto-16b.tres" id="3"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_igci0"] [node name="FriendsView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Friends" label_settings = ExtResource("2_5qe5o") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 8 [node name="ListTitleRichTextLabel" type="RichTextLabel" parent="VB"] unique_name_in_owner = true layout_mode = 2 focus_mode = 2 theme_override_constants/table_h_separation = 16 theme_override_constants/table_v_separation = 4 theme_override_fonts/bold_font = ExtResource("3") theme_override_styles/focus = SubResource("StyleBoxEmpty_igci0") bbcode_enabled = true text = "[b]Friends List[/b]" fit_content = true context_menu_enabled = true selection_enabled = true [node name="FriendsRichTextLabel" type="RichTextLabel" parent="VB"] unique_name_in_owner = true layout_mode = 2 focus_mode = 2 theme_override_constants/table_h_separation = 16 theme_override_constants/table_v_separation = 4 theme_override_fonts/bold_font = ExtResource("3") theme_override_styles/focus = SubResource("StyleBoxEmpty_igci0") bbcode_enabled = true fit_content = true context_menu_enabled = true selection_enabled = true ================================================ FILE: sample/scenes/LeaderboardsView/LeaderboardsView.gd ================================================ class_name LeaderboardsView extends VBoxContainer # Local cache of leaderboard data var leaderboards = [] @onready var select_leaderboard_btn = %SelectLeaderboardBtn @onready var view_leaderboard_btn = %ViewLeaderboardBtn @onready var leaderboard_id = %LeaderboardId @onready var leaderboard_data_richtextlabel = %LeaderboardDataRichTextLabel var selected_leaderboard = { id = "", records = [] } func _ready() -> void: visibility_changed.connect(_on_query_leaderboard) HAuth.logged_out.connect(_on_logged_out) view_leaderboard_btn.pressed.connect(_on_view_leaderboard_btn_pressed) _rebuild_ui() func _on_query_leaderboard(): if not visible: return leaderboards = await HLeaderboards.get_leaderboard_definitions_async() _update_select_leaderboard_button() func _on_logged_out(): leaderboards = [] _update_select_leaderboard_button() selected_leaderboard.id = "" selected_leaderboard.records = [] _rebuild_ui() func _update_select_leaderboard_button(): select_leaderboard_btn.clear() for i in leaderboards.size(): var id = leaderboards[i].leaderboard_id select_leaderboard_btn.add_item(id, i) select_leaderboard_btn.set_item_metadata(i, id) func _on_view_leaderboard_btn_pressed(): var selected_leaderboard_id = select_leaderboard_btn.get_selected_metadata() var records = await HLeaderboards.get_leaderboard_records_async(selected_leaderboard_id) selected_leaderboard.id = selected_leaderboard_id selected_leaderboard.records = records _rebuild_ui() func _rebuild_ui(): if not selected_leaderboard.id: leaderboard_id.visible = false leaderboard_data_richtextlabel.visible = false else: leaderboard_id.visible = true leaderboard_data_richtextlabel.visible = true leaderboard_id.text = selected_leaderboard.id var base_bbcode = """[table=3] [cell][b]Rank[/b][/cell] [cell][b]Score[/b][/cell] [cell][b]User[/b][/cell] %s [/table]""" var rows_bbcode = "" for record in selected_leaderboard.records: rows_bbcode += "[cell]%s[/cell]" % record.rank if record.score != null: rows_bbcode += "[cell]%s[/cell]" % record.score if record.user_display_name != null: var display_name = record.user_display_name if record.user_id == HAuth.product_user_id: display_name += "[b] (you)[/b]" rows_bbcode += "[cell]%s[/cell]" % display_name leaderboard_data_richtextlabel.text = base_bbcode % rows_bbcode ================================================ FILE: sample/scenes/LeaderboardsView/LeaderboardsView.gd.uid ================================================ uid://dkhn2a8wdwx21 ================================================ FILE: sample/scenes/LeaderboardsView/LeaderboardsView.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://5xrk7nvpwosj"] [ext_resource type="Script" uid="uid://dkhn2a8wdwx21" path="res://scenes/LeaderboardsView/LeaderboardsView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_6kpeu"] [ext_resource type="FontFile" uid="uid://bghbav6kh0lsl" path="res://fonts/roboto-16b.tres" id="3"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_tgtn6"] [node name="LeaderboardsView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Leaderboards" label_settings = ExtResource("2_6kpeu") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 8 [node name="SelectLeaderboardBtn" type="OptionButton" parent="VB"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 [node name="ViewLeaderboardBtn" parent="VB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "View" [node name="LeaderboardId" type="Label" parent="VB"] unique_name_in_owner = true layout_mode = 2 theme_override_fonts/font = ExtResource("3") text = "Leaderboard Id" [node name="LeaderboardDataRichTextLabel" type="RichTextLabel" parent="VB"] unique_name_in_owner = true layout_mode = 2 theme_override_constants/table_h_separation = 16 theme_override_constants/table_v_separation = 4 theme_override_fonts/bold_font = ExtResource("3") theme_override_styles/focus = SubResource("StyleBoxEmpty_tgtn6") bbcode_enabled = true text = "RankScoreUser" fit_content = true context_menu_enabled = true selection_enabled = true ================================================ FILE: sample/scenes/LobbiesView/CreateLobbyPopup.gd ================================================ class_name CreateLobbyPopup extends StyledPopupWindow @onready var _bucket_id: LineEdit = %BucketId @onready var _map_option_btn: OptionButton = %MapOptionBtn @onready var _max_players_options_btn: OptionButton = %MaxPlayersOptionsBtn @onready var _public_check_box: CheckBox = %PublicCheckBox @onready var _allow_invites_check_box: CheckBox = %AllowInvitesCheckBox @onready var _presence_check_box: CheckBox = %PresenceCheckBox @onready var _rtc_voice_room_check_box: CheckBox = %RTCVoiceRoomCheckBox @onready var _create_lobby_btn: Button = %CreateLobbyBtn func _ready() -> void: super._ready() _populate_options_btns() _bucket_id.text_submitted.connect(func (_text): _on_create_lobby_btn_pressed()) _create_lobby_btn.pressed.connect(_on_create_lobby_btn_pressed) _bucket_id.text_changed.connect(_on_bucket_id_text_changed) visibility_changed.connect(_on_visibility_changed) func _populate_options_btns(): var map_names = LobbiesView.Maps.keys() for i in range(map_names.size()): _map_option_btn.add_item("Map: " + map_names[i]) _map_option_btn.set_item_metadata(i, LobbiesView.Maps[map_names[i]]) for i in range(LobbiesView.MAX_PLAYERS): _max_players_options_btn.add_item("Max players: " + str(i + 1), i) _max_players_options_btn.set_item_metadata(i, i + 1) func _on_create_lobby_btn_pressed(): var create_opts := EOS.Lobby.CreateLobbyOptions.new() create_opts.bucket_id = _bucket_id.text.strip_edges() create_opts.max_lobby_members = _max_players_options_btn.get_selected_metadata() create_opts.presence_enabled = _presence_check_box.button_pressed create_opts.enable_rtc_room = _rtc_voice_room_check_box.button_pressed create_opts.permission_level = EOS.Lobby.LobbyPermissionLevel.PublicAdvertised if _public_check_box.button_pressed else EOS.Lobby.LobbyPermissionLevel.InviteOnly create_opts.allow_invites = _allow_invites_check_box.button_pressed create_opts.local_rtc_options = { flags = EOS.RTC.JoinRoomFlags.EnableDataChannel } _create_lobby_btn.disabled = true var lobby := await HLobbies.create_lobby_async(create_opts) if not lobby: print("Failed to create lobby!") _create_lobby_btn.disabled = false return lobby.add_attribute(LobbiesView.MAP_ATTRIBUTE_KEY, LobbiesView.Maps.keys()[_map_option_btn.get_selected_metadata()]) lobby.add_attribute(LobbiesView.OWNER_NAME_ATTRIBUTE_KEY, HAuth.display_name) lobby.add_current_member_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY, HAuth.display_name) lobby.add_current_member_attribute(LobbiesView.SKIN_ATTRIBUTE_KEY, Store.get_new_random(LobbiesView.Skins.values())) await lobby.update_async() var lobbies_view: LobbiesView = Store.get_view("Lobbies") lobbies_view.set_current_lobby(lobby) _create_lobby_btn.disabled = false hide() func _on_visibility_changed(): if visible: _reset() func _reset(): _bucket_id.text = "battle_royale" _create_lobby_btn.disabled = false _map_option_btn.select(0) _max_players_options_btn.select(3) func _on_bucket_id_text_changed(new_text: String): if new_text.strip_edges() != "": _create_lobby_btn.disabled = false else: _create_lobby_btn.disabled = true ================================================ FILE: sample/scenes/LobbiesView/CreateLobbyPopup.gd.uid ================================================ uid://csdajb1e07h18 ================================================ FILE: sample/scenes/LobbiesView/CreateLobbyPopup.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://bmxf7kdhjlip6"] [ext_resource type="PackedScene" uid="uid://dkprjbptror6n" path="res://components/StyledPopupWindow/StyledPopupWindow.tscn" id="1_dmoyl"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="2_ngx7o"] [ext_resource type="Script" uid="uid://csdajb1e07h18" path="res://scenes/LobbiesView/CreateLobbyPopup.gd" id="2_oyj73"] [node name="CreateLobbyPopup" instance=ExtResource("1_dmoyl")] title = "CREATE LOBBY" size = Vector2i(356, 245) visible = false script = ExtResource("2_oyj73") [node name="VB" parent="." index="1"] offset_right = 352.0 offset_bottom = 241.0 [node name="Title" parent="VB/PanelContainer/TitleBar" index="0"] text = "CREATE LOBBY" [node name="BucketId" type="LineEdit" parent="VB/MC/VB" index="0"] unique_name_in_owner = true layout_mode = 2 placeholder_text = "Bucket Id" [node name="HBoxContainer" type="HBoxContainer" parent="VB/MC/VB" index="1"] layout_mode = 2 theme_override_constants/separation = 8 [node name="MapOptionBtn" type="OptionButton" parent="VB/MC/VB/HBoxContainer" index="0"] unique_name_in_owner = true custom_minimum_size = Vector2(150, 0) layout_mode = 2 [node name="MaxPlayersOptionsBtn" type="OptionButton" parent="VB/MC/VB/HBoxContainer" index="1"] unique_name_in_owner = true custom_minimum_size = Vector2(150, 0) layout_mode = 2 [node name="HFlowContainer" type="HFlowContainer" parent="VB/MC/VB" index="2"] layout_mode = 2 theme_override_constants/h_separation = 8 theme_override_constants/v_separation = 8 [node name="PublicCheckBox" type="CheckBox" parent="VB/MC/VB/HFlowContainer" index="0"] unique_name_in_owner = true layout_mode = 2 mouse_default_cursor_shape = 2 button_pressed = true text = "Public" [node name="AllowInvitesCheckBox" type="CheckBox" parent="VB/MC/VB/HFlowContainer" index="1"] unique_name_in_owner = true layout_mode = 2 mouse_default_cursor_shape = 2 button_pressed = true text = "Allow Invites" [node name="PresenceCheckBox" type="CheckBox" parent="VB/MC/VB/HFlowContainer" index="2"] unique_name_in_owner = true layout_mode = 2 mouse_default_cursor_shape = 2 button_pressed = true text = "Presence" [node name="RTCVoiceRoomCheckBox" type="CheckBox" parent="VB/MC/VB/HFlowContainer" index="3"] unique_name_in_owner = true layout_mode = 2 mouse_default_cursor_shape = 2 button_pressed = true text = "RTC Voice Room" [node name="CreateLobbyBtn" parent="VB/MC/VB" index="3" instance=ExtResource("2_ngx7o")] unique_name_in_owner = true layout_mode = 2 text = "Create" ================================================ FILE: sample/scenes/LobbiesView/CurrentLobby.gd ================================================ class_name CurrentLobby extends VBoxContainer @onready var id_label: LineEdit = %IdLabel @onready var owner_label: LineEdit = %OwnerLabel @onready var permission_label: LineEdit = %PermissionLabel @onready var map_label: LineEdit = %MapLabel @onready var members: GridContainer = %Members @onready var start_game_btn: Button = %StartGameBtn @onready var destroy_lobby_btn: Button = %DestroyLobbyBtn @onready var random_map_btn: Button = %RandomMapBtn @onready var leave_lobby_btn: Button = %LeaveLobbyBtn @onready var random_skin_btn: Button = %RandomSkinBtn @onready var toggle_mute_btn: Button = %MuteUnmuteBtn @onready var chat_history_label: RichTextLabel = %ChatHistoryLabel @onready var chat_msg_line_edit: LineEdit = %ChatMsgLineEdit @onready var send_chat_btn: Button = %SendChatBtn const PRIMARY_BUTTON = preload("res://scenes/UI/PrimaryButton.tscn") const GRID_CONTAINER_LABELS_COUNT = 5 var cached_lobby := HLobby.new() enum RTCDataTypes { CHAT_MSG = 1, JOIN_GAME = 2, } func _ready() -> void: start_game_btn.pressed.connect(_on_start_game_pressed) destroy_lobby_btn.pressed.connect(_on_destroy_lobby_btn_pressed) random_map_btn.pressed.connect(_on_random_map_btn_pressed) leave_lobby_btn.pressed.connect(_on_leave_lobby_btn_pressed) random_skin_btn.pressed.connect(_on_random_skin_btn_pressed) toggle_mute_btn.pressed.connect(_on_toggle_mute_btn_pressed) chat_msg_line_edit.text_submitted.connect(func (_text): _on_send_chat_btn_pressed()) send_chat_btn.pressed.connect(_on_send_chat_btn_pressed) func update(lobby: HLobby): cached_lobby = lobby Store.current_lobby = cached_lobby if cached_lobby.is_valid(): if cached_lobby.is_owner(): Store.network.create_server() cached_lobby.lobby_updated.connect(_update) cached_lobby.kicked_from_lobby.connect(func(): _reset() hide() ) cached_lobby.rtc_data_received.connect(_on_rtc_data_received) cached_lobby.lobby_owner_changed.connect(func(): if not cached_lobby.is_owner(): return var mem = cached_lobby.get_owner() if not mem: return var username_attr = mem.get_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY) if not username_attr: return lobby.add_attribute(LobbiesView.OWNER_NAME_ATTRIBUTE_KEY, username_attr.value) await lobby.update_async() ) chat_history_label.text = "" _update() func _reset(): id_label.text = "Id: ?" owner_label.text = "Owner: ?" permission_label.text = "Permission: ?" map_label.text = "Map: ?" _reset_lobby_members() _reset_buttons() func _reset_lobby_members(): var idx = members.get_child_count() - 1 while idx >= GRID_CONTAINER_LABELS_COUNT: var node = members.get_child(idx) node.queue_free() idx -= 1 func _reset_buttons(): start_game_btn.hide() destroy_lobby_btn.hide() random_map_btn.hide() leave_lobby_btn.hide() random_skin_btn.hide() toggle_mute_btn.hide() func _update(): _reset() if cached_lobby.is_valid(): show() else: hide() if cached_lobby.lobby_id: id_label.text = "Id: " + cached_lobby.lobby_id if cached_lobby.owner_product_user_id: var lobby_owner := cached_lobby.get_owner() var owner_name_attr = cached_lobby.get_attribute(LobbiesView.OWNER_NAME_ATTRIBUTE_KEY) var owner_name = "" if owner_name_attr: owner_name = owner_name_attr.value elif lobby_owner and lobby_owner.display_name: owner_name = lobby_owner.display_name owner_label.text = "Owner: %s(%s)" % [owner_name, cached_lobby.owner_product_user_id] # Lobby permission level var perm = "Public" if cached_lobby.permission_level == EOS.Lobby.LobbyPermissionLevel.JoinViaPresence: perm = "JoinViaPresence" elif cached_lobby.permission_level == EOS.Lobby.LobbyPermissionLevel.InviteOnly: perm = "InviteOnly" permission_label.text = "Permission: " + perm # Lobby Attr: Map var map_attr = cached_lobby.get_attribute(LobbiesView.MAP_ATTRIBUTE_KEY) if map_attr: map_label.text = "Map: " + map_attr.value # Lobby members _update_lobby_members() # Buttons _update_buttons() func _update_lobby_members(): var connected_members = 0 for mem in cached_lobby.members: if mem.rtc_state.is_in_rtc_room: connected_members += 1 for mem in cached_lobby.members: var name_label = Label.new() var display_name_attr = mem.get_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY) var mem_name = "User %d" % randi_range(1000, 9999) if display_name_attr: mem_name = display_name_attr.value if not mem_name: mem_name = "User" name_label.text = "%s" % [mem_name] members.add_child(name_label) var is_owner_label = Label.new() is_owner_label.text = "Owner" if cached_lobby.is_owner(mem.product_user_id) else "Member" members.add_child(is_owner_label) var skin_label = Label.new() skin_label.text = "?" var skin_attr = mem.get_attribute(LobbiesView.SKIN_ATTRIBUTE_KEY) if skin_attr: skin_label.text = skin_attr.value members.add_child(skin_label) var talking_status = "Unknown" var text_color = Color.DARK_GRAY if not cached_lobby.rtc_room_enabled: talking_status = "RTC Disabled" else: if not mem.rtc_state.is_in_rtc_room: talking_status = "Disconnected" elif mem.rtc_state.is_hard_muted: talking_status = "Hard Muted" text_color = Color.RED elif mem.rtc_state.is_locally_muted: talking_status = "Muted" text_color = Color.RED elif mem.rtc_state.is_audio_output_disabled: talking_status = "Self muted" text_color = Color.DARK_RED elif mem.rtc_state.is_talking: talking_status = "Talking" text_color = Color.GREEN elif connected_members < 2: talking_status = "Connected" text_color = Color.WHITE else: talking_status = "Not Talking" text_color = Color.YELLOW var talking_node = Label.new() talking_node.text = talking_status talking_node.modulate = text_color members.add_child(talking_node) var actions_container = GridContainer.new() actions_container.columns = 2 if cached_lobby.is_owner() and not mem.is_self(): if not mem.is_owner(): # Can promote var promote_btn = PRIMARY_BUTTON.instantiate() promote_btn.text = "Promote" promote_btn.pressed.connect(_on_promote_btn_pressed.bind(mem)) actions_container.add_child(promote_btn) # Can kick var kick_btn = PRIMARY_BUTTON.instantiate() kick_btn.text = "Kick" kick_btn.pressed.connect(_on_kick_btn_pressed.bind(mem)) actions_container.add_child(kick_btn) # Can toggle hard-mute var toggle_hardmute_btn = PRIMARY_BUTTON.instantiate() if mem.is_hard_muted(): toggle_hardmute_btn.text = "Un hard-mute" else: toggle_hardmute_btn.text = "Hard-mute" toggle_hardmute_btn.pressed.connect(_on_toggle_hard_mute_btn_pressed.bind(mem)) actions_container.add_child(toggle_hardmute_btn) members.add_child(actions_container) func _update_buttons(): if not cached_lobby.is_valid(): return if cached_lobby.is_owner(): start_game_btn.show() destroy_lobby_btn.show() random_map_btn.show() leave_lobby_btn.show() random_skin_btn.show() if cached_lobby.rtc_room_enabled: toggle_mute_btn.show() if cached_lobby and cached_lobby.get_current_member(): toggle_mute_btn.text = "Unmute" if cached_lobby.get_current_member().is_muted() else "Mute" func _on_leave_lobby_btn_pressed(): var success = await cached_lobby.leave_async() if not success: print("failed to leave lobby") return update(HLobby.new()) func _on_start_game_pressed(): var map_attr = cached_lobby.get_attribute(LobbiesView.MAP_ATTRIBUTE_KEY) # Update the lobby so it doesnt show in searches cached_lobby.permission_level = EOS.Lobby.LobbyPermissionLevel.InviteOnly await cached_lobby.update_async() Store.network.start_game(LobbiesView.Maps.get(map_attr.value)) var data = _make_join_game_data() var ret := cached_lobby.rtc_send_data(data) if not ret: print("failed to send join game msg") return func _on_destroy_lobby_btn_pressed(): var success = await cached_lobby.destroy_async() if not success: print("failed to destroy lobby") return func _on_random_skin_btn_pressed(): var skin_attr = cached_lobby.get_current_member_attribute(LobbiesView.SKIN_ATTRIBUTE_KEY) var current_skin = skin_attr.get("value", null) cached_lobby.add_current_member_attribute(LobbiesView.SKIN_ATTRIBUTE_KEY, Store.get_new_random(LobbiesView.Skins.values(), current_skin)) await cached_lobby.update_async() func _on_random_map_btn_pressed(): var map_attr = cached_lobby.get_current_member_attribute(LobbiesView.MAP_ATTRIBUTE_KEY) var current_map = map_attr.get("value", null) cached_lobby.add_attribute(LobbiesView.MAP_ATTRIBUTE_KEY, Store.get_new_random(LobbiesView.Maps.keys(), current_map)) await cached_lobby.update_async() func _on_toggle_mute_btn_pressed(): var mem = cached_lobby.get_current_member() if not mem: print("failed to get member") return var success := await mem.toggle_mute_member_async() if not success: print("mute/unmute failed") func _on_promote_btn_pressed(mem: HLobbyMember): var success := await mem.promote_member_async() if not success: print("failed to promote member") return func _on_kick_btn_pressed(mem: HLobbyMember): var success := await mem.kick_member_async() if not success: print("failed to kick member") return func _on_toggle_hard_mute_btn_pressed(mem: HLobbyMember): var success := await mem.toggle_hard_mute_member_async() if not success: print("failed to toggle member hard mute") return func _on_rtc_data_received(raw_data: PackedByteArray): # We know we will get a Variant # because thats what we send var data = bytes_to_var(raw_data) if not (typeof(data) == TYPE_DICTIONARY and data.t): print("Got invalid rtc data") return var msg_type = data.t if msg_type == RTCDataTypes.CHAT_MSG: chat_history_label.set_text.call_deferred(chat_history_label.text + data.u + ": " + data.m + "\n") if msg_type == RTCDataTypes.JOIN_GAME: if cached_lobby.is_valid() and not cached_lobby.is_owner(): Store.network.join_game.call_deferred(cached_lobby.owner_product_user_id) func _make_chat_message_data(username: String, msg: String): return { t = RTCDataTypes.CHAT_MSG, u = username, m = msg } func _make_join_game_data(): return { t = RTCDataTypes.JOIN_GAME, } func _on_send_chat_btn_pressed(): var text = chat_msg_line_edit.text.strip_edges() if not text: return var mem = cached_lobby.get_current_member() var username_attr = mem.get_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY) var username = "User" if username_attr and username_attr.value: username = username_attr.value var data = _make_chat_message_data(username, text) var ret := cached_lobby.rtc_send_data(data) if not ret: print("failed to send chat msg") return chat_history_label.text += username + ": " + text + "\n" ================================================ FILE: sample/scenes/LobbiesView/CurrentLobby.gd.uid ================================================ uid://dscovu6epeb5t ================================================ FILE: sample/scenes/LobbiesView/LobbiesView.gd ================================================ class_name LobbiesView extends VBoxContainer enum Maps { Margao, Bellandur, NewYork, } const Skins = { Human = "Human", Robot = "Robot", Cyborg = "Cyborg", Alien = "Alien", Martian = "Martian", Werewolf = "Werewolf" } const MAX_PLAYERS = 6 const MAP_ATTRIBUTE_KEY = "LEVEL" # To match with EOS Lobbies sample const SKIN_ATTRIBUTE_KEY = "SKIN" const OWNER_NAME_ATTRIBUTE_KEY = "OWNER_NAME" const USERNAME_ATTRIBUTE_KEY = "USER_NAME" var current_lobby: HLobby @onready var show_create_lobby_btn = %ShowCreateLobbyBtn @onready var create_lobby_popup = %CreateLobbyPopup @onready var search_lobby_type = %SearchLobbyType @onready var search_lobby_line_edit = %SearchLobbyLineEdit @onready var search_lobby_btn = %SearchLobbyBtn @onready var current_lobby_node: CurrentLobby = %CurrentLobby @onready var search_lobby_results: SearchLobbyResults = %SearchLobbyResults func _ready() -> void: show_create_lobby_btn.pressed.connect(_on_show_create_lobby_btn_pressed) current_lobby = HLobby.new() hide() hide_search_results() hide_current_lobby() func set_current_lobby(lobby: HLobby): current_lobby = lobby current_lobby_node.update(current_lobby) func hide_current_lobby(): current_lobby_node.hide() func handle_search_results(search_results: Array[HLobby]): hide_current_lobby() search_lobby_results.show() search_lobby_results.update_results(search_results) func hide_search_results(): search_lobby_results.hide() if current_lobby.is_valid(): current_lobby_node.show() func _on_show_create_lobby_btn_pressed(): create_lobby_popup.popup_centered() ================================================ FILE: sample/scenes/LobbiesView/LobbiesView.gd.uid ================================================ uid://c3mc2b4nqvb5v ================================================ FILE: sample/scenes/LobbiesView/LobbiesView.tscn ================================================ [gd_scene load_steps=11 format=3 uid="uid://dsiyt23hnmngd"] [ext_resource type="Script" uid="uid://c3mc2b4nqvb5v" path="res://scenes/LobbiesView/LobbiesView.gd" id="1_gvnj5"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_guetp"] [ext_resource type="Script" uid="uid://bjqvw2f6qq5vu" path="res://scenes/LobbiesView/SearchLobby.gd" id="4_pnhqb"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="4_vb7hx"] [ext_resource type="Script" uid="uid://dscovu6epeb5t" path="res://scenes/LobbiesView/CurrentLobby.gd" id="5_u3fs3"] [ext_resource type="Script" uid="uid://c42grjipjn5g0" path="res://scenes/LobbiesView/SearchLobbyResults.gd" id="6_myvks"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="6_uc41g"] [ext_resource type="PackedScene" uid="uid://bmxf7kdhjlip6" path="res://scenes/LobbiesView/CreateLobbyPopup.tscn" id="8_a8tgx"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6m7w5"] content_margin_left = 4.0 content_margin_top = 4.0 content_margin_right = 4.0 content_margin_bottom = 4.0 bg_color = Color(0.2, 0.2, 0.2, 1) corner_radius_top_left = 4 corner_radius_top_right = 4 corner_radius_bottom_right = 4 corner_radius_bottom_left = 4 [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2qkjk"] bg_color = Color(0.0417444, 0.0824518, 0.193368, 1) [node name="LobbiesView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1_gvnj5") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Lobbies" label_settings = ExtResource("2_guetp") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 size_flags_vertical = 3 theme_override_constants/separation = 8 [node name="HBoxContainer" type="HBoxContainer" parent="VB"] layout_mode = 2 [node name="ShowCreateLobbyBtn" parent="VB/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Create Lobby" [node name="PanelContainer" type="PanelContainer" parent="VB/HBoxContainer"] layout_mode = 2 size_flags_horizontal = 10 theme_override_styles/panel = SubResource("StyleBoxFlat_6m7w5") [node name="SearchLobby" type="HBoxContainer" parent="VB/HBoxContainer/PanelContainer"] layout_mode = 2 script = ExtResource("4_pnhqb") [node name="SearchLobbyType" type="OptionButton" parent="VB/HBoxContainer/PanelContainer/SearchLobby"] unique_name_in_owner = true custom_minimum_size = Vector2(200, 0) layout_mode = 2 [node name="SearchLobbyLineEdit" type="LineEdit" parent="VB/HBoxContainer/PanelContainer/SearchLobby"] unique_name_in_owner = true custom_minimum_size = Vector2(150, 0) layout_mode = 2 placeholder_text = "Search string" [node name="SearchLobbyBtn" parent="VB/HBoxContainer/PanelContainer/SearchLobby" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Search" [node name="ClearSearchLobbyBtn" parent="VB/HBoxContainer/PanelContainer/SearchLobby" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 disabled = true text = "Clear" [node name="HSeparator" type="HSeparator" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="CurrentLobby" type="VBoxContainer" parent="VB"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 script = ExtResource("5_u3fs3") [node name="MarginContainer" type="MarginContainer" parent="VB/CurrentLobby"] layout_mode = 2 theme_override_constants/margin_left = 12 theme_override_constants/margin_right = 12 [node name="Columns" type="HBoxContainer" parent="VB/CurrentLobby/MarginContainer"] layout_mode = 2 size_flags_vertical = 3 [node name="LeftColumn" type="VBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns"] layout_mode = 2 size_flags_horizontal = 3 [node name="Label" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "MY LOBBY" [node name="HBoxContainer" type="HBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] layout_mode = 2 theme_override_constants/separation = 32 [node name="IdLabel" type="LineEdit" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/HBoxContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(200, 0) layout_mode = 2 text = "Id: ?" editable = false [node name="OwnerLabel" type="LineEdit" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/HBoxContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(200, 0) layout_mode = 2 text = "Owner: ?" editable = false [node name="HBoxContainer2" type="HBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] layout_mode = 2 theme_override_constants/separation = 32 [node name="PermissionLabel" type="LineEdit" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/HBoxContainer2"] unique_name_in_owner = true custom_minimum_size = Vector2(200, 0) layout_mode = 2 text = "Permission: ?" editable = false [node name="MapLabel" type="LineEdit" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/HBoxContainer2"] unique_name_in_owner = true custom_minimum_size = Vector2(200, 0) layout_mode = 2 text = "Map: ?" editable = false [node name="MarginContainer" type="MarginContainer" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] layout_mode = 2 theme_override_constants/margin_left = 0 theme_override_constants/margin_top = 12 theme_override_constants/margin_right = 0 theme_override_constants/margin_bottom = 0 [node name="HBoxContainer" type="HBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer"] layout_mode = 2 [node name="StartGameBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Start" [node name="DestroyLobbyBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Destroy" [node name="RandomMapBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Random Map" [node name="VSeparator" type="VSeparator" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer"] layout_mode = 2 theme_override_constants/separation = 12 [node name="LeaveLobbyBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Leave" [node name="RandomSkinBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Random skin" [node name="MuteUnmuteBtn" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/MarginContainer/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Mute" [node name="Label2" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] custom_minimum_size = Vector2(0, 48) layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "Members" vertical_alignment = 2 [node name="Members" type="GridContainer" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn"] unique_name_in_owner = true layout_mode = 2 theme_override_constants/h_separation = 64 columns = 5 [node name="Name" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/Members"] layout_mode = 2 text = "Name" [node name="IsOwner" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/Members"] layout_mode = 2 text = "Is Owner?" [node name="Skin" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/Members"] layout_mode = 2 text = "Skin" [node name="Talking" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/Members"] layout_mode = 2 text = "Talking?" [node name="Actions" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/LeftColumn/Members"] layout_mode = 2 [node name="RightColumn" type="VBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns"] custom_minimum_size = Vector2(300, 0) layout_mode = 2 [node name="Label" type="Label" parent="VB/CurrentLobby/MarginContainer/Columns/RightColumn"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "CHAT" [node name="ChatHistoryLabel" type="RichTextLabel" parent="VB/CurrentLobby/MarginContainer/Columns/RightColumn"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 theme_override_styles/normal = SubResource("StyleBoxFlat_2qkjk") scroll_following = true [node name="HBoxContainer" type="HBoxContainer" parent="VB/CurrentLobby/MarginContainer/Columns/RightColumn"] layout_mode = 2 theme_override_constants/separation = 6 [node name="ChatMsgLineEdit" type="LineEdit" parent="VB/CurrentLobby/MarginContainer/Columns/RightColumn/HBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 [node name="SendChatBtn" parent="VB/CurrentLobby/MarginContainer/Columns/RightColumn/HBoxContainer" instance=ExtResource("6_uc41g")] unique_name_in_owner = true layout_mode = 2 text = "Send" [node name="SearchLobbyResults" type="VBoxContainer" parent="VB"] unique_name_in_owner = true visible = false layout_mode = 2 script = ExtResource("6_myvks") [node name="Label2" type="Label" parent="VB/SearchLobbyResults"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "SEARCH RESULTS" [node name="GridContainer" type="GridContainer" parent="VB/SearchLobbyResults"] unique_name_in_owner = true layout_mode = 2 theme_override_constants/h_separation = 64 columns = 4 [node name="OwnerName" type="Label" parent="VB/SearchLobbyResults/GridContainer"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "Owner" [node name="Members" type="Label" parent="VB/SearchLobbyResults/GridContainer"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "Members" [node name="Map" type="Label" parent="VB/SearchLobbyResults/GridContainer"] layout_mode = 2 theme_override_fonts/font = ExtResource("4_vb7hx") text = "Map" [node name="JoinBtn" type="Label" parent="VB/SearchLobbyResults/GridContainer"] layout_mode = 2 [node name="CreateLobbyPopup" parent="." instance=ExtResource("8_a8tgx")] unique_name_in_owner = true ================================================ FILE: sample/scenes/LobbiesView/SearchLobby.gd ================================================ extends HBoxContainer #region Enums const SearchType = { Map = "Map", BucketId = "BucketId", LobbyId = "LobbyId", UserId = "UserId", } #endregion #region Onready vars @onready var search_lobby_type: OptionButton = %SearchLobbyType @onready var search_lobby_line_edit: LineEdit = %SearchLobbyLineEdit @onready var search_lobby_btn: Button = %SearchLobbyBtn @onready var clear_search_lobby_btn: Button = %ClearSearchLobbyBtn #endregion #region Built-in methods func _ready() -> void: search_lobby_btn.pressed.connect(_on_search_lobby_btn_pressed) search_lobby_line_edit.text_changed.connect(_on_search_lobby_line_edit_text_changed) search_lobby_line_edit.text_submitted.connect(func(_new_text): _on_search_lobby_btn_pressed()) clear_search_lobby_btn.pressed.connect(_on_clear_search_lobby_btn_pressed) _populate_search_lobby_type_options() #endregion #region Private methods func _populate_search_lobby_type_options(): var idx = 0 for search_type in SearchType.keys(): search_lobby_type.add_item(search_type, idx) search_lobby_type.set_item_metadata(idx, search_type) idx += 1 func _on_search_lobby_btn_pressed(): var search_type = search_lobby_type.get_selected_metadata() var search_string = search_lobby_line_edit.text.strip_edges() var lobbies = [] if search_type == SearchType.Map: if search_string.is_empty(): # Search for any map lobbies = await HLobbies.search_by_attribute_async({ key = LobbiesView.MAP_ATTRIBUTE_KEY, value = search_string, comparison = EOS.ComparisonOp.NotEqual }) else: # Search for specific map lobbies = await HLobbies.search_by_attribute_async({ key = LobbiesView.MAP_ATTRIBUTE_KEY, value = search_string, comparison = EOS.ComparisonOp.Equal }) elif search_type == SearchType.BucketId: lobbies = await HLobbies.search_by_bucket_id_async(search_string) elif search_type == SearchType.LobbyId: lobbies = await HLobbies.search_by_lobby_id_async(search_string) elif search_type == SearchType.UserId: lobbies = await HLobbies.search_by_product_user_id_async(search_string) else: print("Invalid lobby search type") return if lobbies == null: print("Failed to search for lobbies") return var lobbies_view: LobbiesView = Store.get_view("Lobbies") lobbies_view.handle_search_results(lobbies) func _on_clear_search_lobby_btn_pressed(): search_lobby_line_edit.text = "" var lobbies_view: LobbiesView = Store.get_view("Lobbies") lobbies_view.handle_search_results([]) lobbies_view.hide_search_results() func _on_search_lobby_line_edit_text_changed(_new_text: String): search_lobby_btn.disabled = false clear_search_lobby_btn.disabled = false #endregion ================================================ FILE: sample/scenes/LobbiesView/SearchLobby.gd.uid ================================================ uid://bjqvw2f6qq5vu ================================================ FILE: sample/scenes/LobbiesView/SearchLobbyResults.gd ================================================ class_name SearchLobbyResults extends VBoxContainer const GRID_CONTAINER_LABELS_COUNT = 4 var search_results: Array[HLobby] = [] @onready var grid: GridContainer = %GridContainer @onready var PrimaryButton = preload("res://scenes/UI/PrimaryButton.tscn") func update_results(p_search_results: Array[HLobby]): search_results = p_search_results update_ui() func update_ui(): _reset() for lobby in search_results: var owner_name_attr = lobby.get_attribute(LobbiesView.OWNER_NAME_ATTRIBUTE_KEY) var owner_label = Label.new() var owner_name = "?" if owner_name_attr: owner_name = owner_name_attr.value owner_label.text = "%s(%s)" % [owner_name, lobby.owner_product_user_id] grid.add_child(owner_label) var members_label = Label.new() members_label.text = "%s/%s" % [lobby.members.size(), lobby.max_members] grid.add_child(members_label) var map_attr = lobby.get_attribute(LobbiesView.MAP_ATTRIBUTE_KEY) var map_label = Label.new() map_label.text = "?" if map_attr: map_label.text = map_attr.value grid.add_child(map_label) var join_btn = PrimaryButton.instantiate() grid.add_child(join_btn) join_btn.text = "JOIN" join_btn.pressed.connect(_on_lobby_join_pressed.bind(lobby)) func _on_lobby_join_pressed(p_lobby: HLobby): var lobbies_view: LobbiesView = Store.get_view("Lobbies") var lobby = await HLobbies.join_async(p_lobby) if not lobby: print("Failed to join lobby") return lobby.add_current_member_attribute(LobbiesView.SKIN_ATTRIBUTE_KEY, Store.get_new_random(LobbiesView.Skins.values())) lobby.add_current_member_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY, HAuth.display_name) lobbies_view.hide_search_results() await lobby.update_async() lobbies_view.set_current_lobby(lobby) search_results = [] _reset() func _reset(): var i = grid.get_child_count() - 1 while i >= GRID_CONTAINER_LABELS_COUNT: var node = grid.get_child(i) node.queue_free() i -= 1 ================================================ FILE: sample/scenes/LobbiesView/SearchLobbyResults.gd.uid ================================================ uid://c42grjipjn5g0 ================================================ FILE: sample/scenes/LoginView/EnterCredentials.gd ================================================ extends VBoxContainer signal perform_login @onready var id_label = $IdLabel @onready var token_label = $TokenLabel @onready var id_lineedit = $IdLineEdit @onready var token_lineedit = $TokenLineEdit func _ready() -> void: token_lineedit.text_submitted.connect(func(_new_text): perform_login.emit()) func set_helper_texts(id_help: String, token_help: String): if id_help != "": id_label.text = id_help id_label.visible = true id_lineedit.visible = true else: id_label.visible = false id_lineedit.visible = false if token_help != "": token_label.text = token_help token_label.visible = true token_lineedit.visible = true else: token_label.visible = false token_lineedit.visible = false func set_id_text(text: String): id_label.text = text func set_token_text(text: String): token_label.text = text func set_id_value(text: String): id_lineedit.text = text func get_id_value() -> String: return id_lineedit.text func get_token_value() -> String: return token_lineedit.text func reset_lineedits(): id_lineedit.text = "" token_lineedit.text = "" func _notification(what: int) -> void: if what == NOTIFICATION_VISIBILITY_CHANGED: if visible: reset_lineedits() ================================================ FILE: sample/scenes/LoginView/EnterCredentials.gd.uid ================================================ uid://bx0bhh73kw3q7 ================================================ FILE: sample/scenes/LoginView/LoginView.gd ================================================ class_name LoginView extends CenterContainer #region Enums enum States { ChooseMethod, EnterCredentials, Pending, Success, Error } #endregion #region Constants const LOGIN_TYPES = { DEVELOPER = { type = "DEVELOPER", name = "Developer App", id_help = "Devtool Server URL", token_help = "Credential Name" }, DISCORD = { type = "DISCORD", name = "Discord", id_help = "", token_help = "Discord OAuth2 Token" }, DEVICE_ID = { type = "DEVICE_ID", name = "Device Id", id_help = "Display Name", token_help = "" }, ACCOUNT_PORTAL = { type = "ACCOUNT_PORTAL", name = "Account Portal", id_help = "", token_help = "" }, PERSISTENT_AUTH = { type = "PERSISTENT_AUTH", name = "Persistent Auth", id_help = "", token_help = "" } } #endregion #region Private vars var _state = States.ChooseMethod #endregion #region Onready vars @onready var login_type_btn = $HB/Right/ChooseMethod/LoginTypeButton @onready var login_btn = $HB/Right/LoginButton @onready var back_btn = $HB/Right/BackButton @onready var retry_login_btn = $HB/Right/RetryLoginButton @onready var logout_btn = $HB/Right/LogoutButton # Sub Views @onready var choose_method = $HB/Right/ChooseMethod @onready var enter_credentials = $HB/Right/EnterCredentials @onready var login_status = $HB/Right/LoginStatus #endregion #region Built-in methods func _ready() -> void: _populate_login_type_dropdown() enter_credentials.perform_login.connect(_on_login_btn_pressed) login_btn.pressed.connect(_on_login_btn_pressed) back_btn.pressed.connect(_on_back_btn_pressed) retry_login_btn.pressed.connect(_on_retry_login_btn_pressed) logout_btn.pressed.connect(_on_logout_btn_pressed) HAuth.logged_in.connect(_on_logged_in) HAuth.display_name_changed.connect(_on_display_name_changed) HAuth.login_error.connect(_on_login_error) HAuth.logged_out.connect(_on_logged_out) await HPlatform.platform_created # TODO: Remove Autologin (PersistentAuth) for debug purpose # For developement # _set_login_status("Logging in...") # _set_login_state(States.Pending) # HAuth.login_persistent_auth_async() #endregion #region Private methods func _on_logged_in(): _set_login_state(States.Success) func _on_display_name_changed(): print("User display name: ", HAuth.display_name) func _on_logged_out(): _set_login_status("Logout successful") _set_login_state(States.ChooseMethod) func _on_login_error(result_code: EOS.Result): _set_login_state(States.Error) _set_login_status("Login Error: " + EOS.result_str(result_code)) func _on_retry_login_btn_pressed(): _set_login_state(States.ChooseMethod) func _on_back_btn_pressed(): _set_login_state(States.ChooseMethod) func _on_login_btn_pressed(): var login_type_data = login_type_btn.get_selected_metadata() var login_type = login_type_data.type var login_id = enter_credentials.get_id_value() var login_token = enter_credentials.get_token_value() if _state == States.ChooseMethod: # In ChooseMethod state if login_type_data.id_help or login_type_data.token_help: # User needs to enter some data enter_credentials.set_helper_texts(login_type_data.id_help, login_type_data.token_help) _set_login_state(States.EnterCredentials) if login_type == "DISCORD": OS.shell_open("https://discord.com/api/oauth2/authorize?client_id=959047632091762719&redirect_uri=http%3A%2F%2Flocalhost%3A53134&response_type=token&scope=identify%20email") elif login_type == "DEVELOPER": enter_credentials.set_id_value("localhost:4545") else: # No data entry is required login_status.text = "Logging in..." _set_login_state(States.Pending) match login_type: "PERSISTENT_AUTH": HAuth.login_persistent_auth_async() "ACCOUNT_PORTAL": HAuth.login_account_portal_async() else: login_status.text = "Logging in..." _set_login_state(States.Pending) # In EnterCredentials state match login_type: "DEVICE_ID": HAuth.login_anonymous_async(login_id) "DISCORD": _login_game_services(EOS.ExternalCredentialType.DiscordAccessToken, login_token) "DEVELOPER": HAuth.login_devtool_async(login_id, login_token) func _on_logout_btn_pressed(): login_status.text = "Logout pending..." _set_login_state(States.Pending) HAuth.logout_async() func _set_login_state(new_state: States): _state = new_state for child in $HB/Right.get_children(): child.visible = false if _state == States.ChooseMethod: choose_method.visible = true login_btn.visible = true if _state == States.Error: login_status.visible = true retry_login_btn.visible = true if _state == States.EnterCredentials: enter_credentials.visible = true back_btn.visible = true login_btn.visible = true if _state == States.Pending: login_status.visible = true if _state == States.Success: login_status.text = "Login success!" login_status.visible = true logout_btn.visible = true func _login_game_services(type: int, token = null, display_name = null): _set_login_status("Logging in to Epic Games Services...") var login_options = EOS.Connect.LoginOptions.new() login_options.credentials = EOS.Connect.Credentials.new() login_options.credentials.type = type login_options.credentials.token = token if display_name: login_options.user_login_info = EOS.Connect.UserLoginInfo.new() login_options.user_login_info.display_name = display_name HAuth.login_game_services_async(login_options) func _set_login_status(new_login_status: String): login_status.text = new_login_status func _populate_login_type_dropdown(): var idx = 0 for login_type in LOGIN_TYPES.keys(): login_type_btn.add_item(LOGIN_TYPES[login_type].name) login_type_btn.set_item_metadata(idx, LOGIN_TYPES[login_type]) idx += 1 #endregion ================================================ FILE: sample/scenes/LoginView/LoginView.gd.uid ================================================ uid://dwpfnhluy8h4e ================================================ FILE: sample/scenes/LoginView/LoginView.tscn ================================================ [gd_scene load_steps=15 format=3 uid="uid://bht0ln2ftshrw"] [ext_resource type="FontFile" uid="uid://njn2rf7rj4ph" path="res://fonts/roboto-32b.tres" id="1"] [ext_resource type="FontFile" uid="uid://bxcl265jykrk0" path="res://fonts/roboto-16r.tres" id="2"] [ext_resource type="Script" uid="uid://dwpfnhluy8h4e" path="res://scenes/LoginView/LoginView.gd" id="3"] [ext_resource type="FontFile" uid="uid://bghbav6kh0lsl" path="res://fonts/roboto-16b.tres" id="4"] [ext_resource type="Script" uid="uid://bx0bhh73kw3q7" path="res://scenes/LoginView/EnterCredentials.gd" id="5"] [ext_resource type="Texture2D" uid="uid://e0l4pytsqbp1" path="res://logo.png" id="6"] [sub_resource type="StyleBoxFlat" id="1"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.164706, 0.505882, 0.188235, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="2"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.203922, 0.623529, 0.235294, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="3"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.145098, 0.454902, 0.164706, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="7"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.529412, 0.529412, 0.219608, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="8"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.631373, 0.631373, 0.258824, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="9"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.47, 0.47, 0.1927, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="11"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.529412, 0.258824, 0.215686, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="10"] content_margin_top = 3.0 content_margin_bottom = 3.0 bg_color = Color(0.6, 0.294118, 0.247059, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [node name="LoginView" type="CenterContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_vertical = 3 script = ExtResource("3") [node name="HB" type="HBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 8 [node name="Left" type="VBoxContainer" parent="HB"] custom_minimum_size = Vector2(400, 0) layout_mode = 2 theme_override_constants/separation = 8 [node name="TextureRect" type="TextureRect" parent="HB/Left"] custom_minimum_size = Vector2(128, 128) layout_mode = 2 size_flags_horizontal = 0 texture = ExtResource("6") expand_mode = 1 stretch_mode = 5 [node name="Title" type="Label" parent="HB/Left"] layout_mode = 2 theme_override_fonts/font = ExtResource("1") text = "EOS Godot Sample" [node name="Desc" type="RichTextLabel" parent="HB/Left"] layout_mode = 2 theme_override_fonts/bold_font = ExtResource("4") bbcode_enabled = true text = "This sample project demonstrates how to use Epic Online Services SDK in Godot using a SDK wrapper. This project is not affiliated with Epic Games Inc nor does it endorse Epic Online Services. Use at your own risk." [node name="Right" type="VBoxContainer" parent="HB"] custom_minimum_size = Vector2(300, 0) layout_mode = 2 size_flags_vertical = 4 theme_override_constants/separation = 8 [node name="ChooseMethod" type="VBoxContainer" parent="HB/Right"] layout_mode = 2 [node name="LoginTypeLabel" type="Label" parent="HB/Right/ChooseMethod"] layout_mode = 2 text = "Login Type" [node name="LoginTypeButton" type="OptionButton" parent="HB/Right/ChooseMethod"] layout_mode = 2 [node name="EnterCredentials" type="VBoxContainer" parent="HB/Right"] visible = false layout_mode = 2 script = ExtResource("5") [node name="IdLabel" type="Label" parent="HB/Right/EnterCredentials"] layout_mode = 2 text = "Devtool Server URL" [node name="IdLineEdit" type="LineEdit" parent="HB/Right/EnterCredentials"] layout_mode = 2 text = "localhost:8000" [node name="TokenLabel" type="Label" parent="HB/Right/EnterCredentials"] layout_mode = 2 text = "Credential Name" [node name="TokenLineEdit" type="LineEdit" parent="HB/Right/EnterCredentials"] layout_mode = 2 secret = true [node name="LoginButton" type="Button" parent="HB/Right"] layout_mode = 2 mouse_default_cursor_shape = 2 theme_override_fonts/font = ExtResource("2") theme_override_styles/focus = SubResource("1") theme_override_styles/hover = SubResource("2") theme_override_styles/pressed = SubResource("3") theme_override_styles/normal = SubResource("1") text = "Log in" [node name="BackButton" type="Button" parent="HB/Right"] visible = false layout_mode = 2 mouse_default_cursor_shape = 2 theme_override_fonts/font = ExtResource("2") theme_override_styles/focus = SubResource("7") theme_override_styles/hover = SubResource("8") theme_override_styles/pressed = SubResource("9") theme_override_styles/normal = SubResource("7") text = "Back" [node name="LoginStatus" type="Label" parent="HB/Right"] visible = false layout_mode = 2 text = "Logging in..." [node name="RetryLoginButton" type="Button" parent="HB/Right"] visible = false layout_mode = 2 mouse_default_cursor_shape = 2 theme_override_fonts/font = ExtResource("2") theme_override_styles/focus = SubResource("7") theme_override_styles/hover = SubResource("8") theme_override_styles/pressed = SubResource("9") theme_override_styles/normal = SubResource("7") text = "Retry" [node name="LogoutButton" type="Button" parent="HB/Right"] visible = false layout_mode = 2 mouse_default_cursor_shape = 2 theme_override_fonts/font = ExtResource("2") theme_override_styles/focus = SubResource("11") theme_override_styles/hover = SubResource("10") theme_override_styles/pressed = SubResource("10") theme_override_styles/normal = SubResource("11") text = "Logout" ================================================ FILE: sample/scenes/LogsView/LogsView.gd ================================================ class_name LogsView extends VBoxContainer @onready var logs_label = %LogsLabel func _ready() -> void: HPlatform.log_msg.connect(func(msg: EOS.Logging.LogMessage): log_msg(msg.level, msg.message, msg.category) ) HPlatform.platform_initialized.connect(_on_platform_initialized) func _on_platform_initialized() -> void: # Set logging categories and level HPlatform.set_eos_log_level(EOS.Logging.LogCategory.AllCategories, EOS.Logging.LogLevel.Info) func log_msg(level: int, msg: String, category := ""): var color = "#ffffff" var level_str = "Info" match level: EOS.Logging.LogLevel.VeryVerbose: color = "#555555" level_str = "VeryVerbose" EOS.Logging.LogLevel.Verbose: color = "#aaaaaa" level_str = "Verbose" EOS.Logging.LogLevel.Warning: color = "#ffff00" level_str = "Warning" EOS.Logging.LogLevel.Error: color = "#bb0000" level_str = "Error" EOS.Logging.LogLevel.Fatal: color = "#ff0000" level_str = "Fatal" var _category = "LOG" if category != "": _category = category var darkened_color = Color(color).darkened(0.2).to_html(true) var to_print = "[color=#%s]%s\t%s\t[/color][color=%s]%s[/color]" % [darkened_color, _category, level_str, color, msg] logs_label.text += to_print + "\n" # Uncomment the below line if you want to see EOS logs in terminal too # print_rich(to_print) ================================================ FILE: sample/scenes/LogsView/LogsView.gd.uid ================================================ uid://b54ebdyvahtmv ================================================ FILE: sample/scenes/LogsView/LogsView.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://cl2qvlvdhn2iw"] [ext_resource type="FontFile" uid="uid://da6bpapjl4a5k" path="res://fonts/roboto-13r.tres" id="1"] [ext_resource type="Script" uid="uid://b54ebdyvahtmv" path="res://scenes/LogsView/LogsView.gd" id="2"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="3_xmddo"] [sub_resource type="StyleBoxFlat" id="5"] bg_color = Color(0.0627451, 0.0627451, 0.0627451, 1) border_width_left = 1 border_width_top = 1 border_width_right = 1 border_width_bottom = 1 border_color = Color(0.427451, 0.427451, 0.427451, 1) [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_p3j38"] [node name="LogsView" type="VBoxContainer"] custom_minimum_size = Vector2(0, 100) anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_vertical = 3 script = ExtResource("2") [node name="PC" type="PanelContainer" parent="."] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 theme_override_styles/panel = SubResource("5") [node name="SC" type="ScrollContainer" parent="PC"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 [node name="LogsLabel" type="RichTextLabel" parent="PC/SC"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 focus_mode = 2 theme_override_fonts/normal_font = ExtResource("1") theme_override_fonts/bold_font = ExtResource("3_xmddo") theme_override_styles/focus = SubResource("StyleBoxEmpty_p3j38") bbcode_enabled = true text = "[b]EOS Logs[/b] Logs from EOS are shown here. The game's logs are shown in Godot's Output panel. " fit_content = true scroll_following = true context_menu_enabled = true selection_enabled = true ================================================ FILE: sample/scenes/MetricsView/MetricsView.gd ================================================ class_name MetricView extends VBoxContainer @onready var begin_player_session_btn = %BeginPlayerSessionBtn @onready var end_player_session_btn = %EndPlayerSessionBtn func _ready() -> void: begin_player_session_btn.pressed.connect(_on_begin_player_session_btn_pressed) end_player_session_btn.pressed.connect(_on_end_player_session_btn_pressed) func _on_begin_player_session_btn_pressed(): var begin_sess_opts = EOS.Metrics.BeginPlayerSessionOptions.new() begin_sess_opts.controller_type = EOS.Metrics.UserControllerType.MouseKeyboard if HAuth.epic_account_id: begin_sess_opts.account_id_type = EOS.Metrics.MetricsAccountIdType.Epic begin_sess_opts.account_id = HAuth.epic_account_id else: begin_sess_opts.account_id_type = EOS.Metrics.MetricsAccountIdType.External begin_sess_opts.account_id = HAuth.product_user_id if HAuth.display_name: begin_sess_opts.display_name = HAuth.display_name else: begin_sess_opts.display_name = "Unnamed user" var begin_sess_ret = EOS.Metrics.MetricsInterface.begin_player_session(begin_sess_opts) print("--- Metrics: begin_player_session: ",EOS.result_str(begin_sess_ret)) func _on_end_player_session_btn_pressed(): var end_sess_opts = EOS.Metrics.EndPlayerSessionOptions.new() if HAuth.epic_account_id != "": end_sess_opts.account_id_type = EOS.Metrics.MetricsAccountIdType.Epic end_sess_opts.account_id = HAuth.epic_account_id else: end_sess_opts.account_id_type = EOS.Metrics.MetricsAccountIdType.External end_sess_opts.account_id = HAuth.product_user_id var end_sess_ret = EOS.Metrics.MetricsInterface.end_player_session(end_sess_opts) print("--- Metrics: end_player_session: ", EOS.result_str(end_sess_ret)) ================================================ FILE: sample/scenes/MetricsView/MetricsView.gd.uid ================================================ uid://b081ugrjtk51r ================================================ FILE: sample/scenes/MetricsView/MetricsView.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://bd06h3ufkhfd7"] [ext_resource type="Script" uid="uid://b081ugrjtk51r" path="res://scenes/MetricsView/MetricsView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_ftyuk"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [node name="MetricsView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Metrics" label_settings = ExtResource("2_ftyuk") [node name="HB" type="HBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 16 [node name="BeginPlayerSessionBtn" parent="HB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Begin Player Sesssion" [node name="EndPlayerSessionBtn" parent="HB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "End Player Session" ================================================ FILE: sample/scenes/NotificationsView/NotificationsView.gd ================================================ class_name NotificationsView extends MarginContainer @onready var vb = $SC/PC/MC/VB func add_notification(node: Node): vb.add_child(node) node.fade_in() ================================================ FILE: sample/scenes/NotificationsView/NotificationsView.gd.uid ================================================ uid://6tlvlmuarqub ================================================ FILE: sample/scenes/StatsView/StatsView.gd ================================================ class_name StatsView extends VBoxContainer # Local cache of stats var stats = [] @onready var stat_name = %StatNameLineEdit @onready var ingest_amount = %IngestAmount @onready var ingest_btn = %IngestBtn @onready var refresh_my_stats_btn = %RefreshMyStatsBtn @onready var my_stats = %MyStatsRichTextLabel @onready var status_label = %StatusLabel func _ready() -> void: visibility_changed.connect(_on_query_stats) HAuth.logged_out.connect(_on_logged_out) stat_name.text_changed.connect(_on_stat_name_text_changed) ingest_btn.pressed.connect(_on_ingest_btn_pressed) refresh_my_stats_btn.pressed.connect(_on_query_stats) func _on_query_stats(): if not visible: return stats = await HStats.get_stats_async() _rebuild_ui() func _on_logged_out(): stats = [] _rebuild_ui() func _on_stat_name_text_changed(new_text: String): if new_text.strip_edges() == "": ingest_btn.disabled = true else: ingest_btn.disabled = false func _on_ingest_btn_pressed(): ingest_stat(stat_name.text, ingest_amount.value) func ingest_stat(_stat_name: String, _ingest_amount: int): status_label.text = "Ingesting stat..." var ret := await HStats.ingest_stat_async(_stat_name, _ingest_amount) if not EOS.is_success(ret): status_label.text = "Ingesting Failed: " + EOS.result_str(ret) return status_label.text = "Ingesting Success" _on_query_stats() func _rebuild_ui(): var base_bbcode = """[table=4] [cell][b]Name[/b][/cell] [cell][b]Value[/b][/cell] [cell][b]Start Time[/b][/cell] [cell][b]End Time[/b][/cell] %s [/table]""" var rows_bbcode = "" for stat in stats: rows_bbcode += "[cell]%s[/cell]" % stat.name rows_bbcode += "[cell]%s[/cell]" % stat.value if stat.start_time != EOS.Stats.STATS_TIME_UNDEFINED: var time_str = Time.get_datetime_string_from_unix_time(stat.start_time) rows_bbcode += "[cell]%s[/cell]" % time_str else: rows_bbcode += "[cell]Undefined[/cell]" if stat.end_time != EOS.Stats.STATS_TIME_UNDEFINED: var time_str = Time.get_datetime_string_from_unix_time(stat.end_time) rows_bbcode += "[cell]%s[/cell]" % time_str else: rows_bbcode += "[cell]Undefined[/cell]" my_stats.text = base_bbcode % rows_bbcode ================================================ FILE: sample/scenes/StatsView/StatsView.gd.uid ================================================ uid://svry6i6h3yam ================================================ FILE: sample/scenes/StatsView/StatsView.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://dgi7j5qx3xt84"] [ext_resource type="Script" uid="uid://svry6i6h3yam" path="res://scenes/StatsView/StatsView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_8cl02"] [ext_resource type="FontFile" uid="uid://bghbav6kh0lsl" path="res://fonts/roboto-16b.tres" id="3"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_m0bq4"] [node name="StatsView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "Stats" label_settings = ExtResource("2_8cl02") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 8 [node name="StatNameLineEdit" type="LineEdit" parent="VB"] unique_name_in_owner = true custom_minimum_size = Vector2(250, 0) layout_mode = 2 size_flags_horizontal = 0 placeholder_text = "Enter stat name" [node name="HBoxContainer" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 8 [node name="AmountLabel" type="Label" parent="VB/HBoxContainer"] layout_mode = 2 text = "Amount" vertical_alignment = 1 [node name="IngestAmount" type="SpinBox" parent="VB/HBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 max_value = 900000.0 rounded = true [node name="HB" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="IngestBtn" parent="VB/HB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 disabled = true text = "Ingest Stat" [node name="StatusLabel" type="Label" parent="VB/HB"] unique_name_in_owner = true layout_mode = 2 [node name="RefreshMyStatsBtn" parent="VB" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Refresh my stats" [node name="HSeparator" type="HSeparator" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="MyStatsLabel" type="Label" parent="VB"] layout_mode = 2 theme_override_fonts/font = ExtResource("3") text = "My Stats" [node name="VB" type="VBoxContainer" parent="VB"] layout_mode = 2 [node name="MyStatsRichTextLabel" type="RichTextLabel" parent="VB/VB"] unique_name_in_owner = true layout_mode = 2 focus_mode = 2 theme_override_constants/table_h_separation = 16 theme_override_constants/table_v_separation = 4 theme_override_fonts/bold_font = ExtResource("3") theme_override_styles/focus = SubResource("StyleBoxEmpty_m0bq4") bbcode_enabled = true fit_content = true context_menu_enabled = true selection_enabled = true ================================================ FILE: sample/scenes/UI/NetworkImage.gd ================================================ class_name NetworkImage extends MarginContainer var LOADING_TEXTURE = preload("res://scenes/UI/loading-icon.png") @onready var texture_rect = $TR @onready var anim_player = $Anim enum States { Loading, Loaded } var _state = States.Loading var _url: String func _ready() -> void: set_state(States.Loading) func set_state(new_state: States): _state = new_state if _state == States.Loading: texture_rect.texture = LOADING_TEXTURE anim_player.play("rotate") elif _state == States.Loaded: $TR.set_rotation(0) anim_player.stop(true) func fetch_image(url: String): _url = url if Store.network_image_cache.has(url): # Directly set the image texture_rect.texture = Store.network_image_cache[url] set_state(States.Loaded) return set_state(States.Loading) var http_request = HTTPRequest.new() add_child(http_request) http_request.request_completed.connect(_on_request_completed) http_request.request(url) func _on_request_completed(result, response_code, _headers, body): if result != OK or response_code != 200: print("Network Image Error: result=%s, response_code=%s" % [result, response_code]) var image = Image.new() var image_error = image.load_png_from_buffer(body) if image_error != OK: print("Network Image Error: image_error=%s: An error occurred while trying to display the image." % image_error) return var image_texture = ImageTexture.create_from_image(image) Store.network_image_cache[_url] = image_texture texture_rect.texture = image_texture set_state(States.Loaded) ================================================ FILE: sample/scenes/UI/NetworkImage.gd.uid ================================================ uid://bxtewm8nybcvn ================================================ FILE: sample/scenes/UI/NetworkImage.tscn ================================================ [gd_scene load_steps=6 format=3 uid="uid://djhhnis3ksdjk"] [ext_resource type="Script" uid="uid://bxtewm8nybcvn" path="res://scenes/UI/NetworkImage.gd" id="1"] [ext_resource type="Texture2D" uid="uid://dluvbejagvvak" path="res://scenes/UI/loading-icon.png" id="2"] [sub_resource type="Animation" id="1"] length = 0.001 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath(".:rotation") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 0, "values": [360.0] } tracks/1/type = "value" tracks/1/imported = false tracks/1/enabled = true tracks/1/path = NodePath("TR:rotation") tracks/1/interp = 1 tracks/1/loop_wrap = true tracks/1/keys = { "times": PackedFloat32Array(0), "transitions": PackedFloat32Array(1), "update": 0, "values": [360.0] } [sub_resource type="Animation" id="2"] resource_name = "rotate" loop_mode = 1 tracks/0/type = "value" tracks/0/imported = false tracks/0/enabled = true tracks/0/path = NodePath("TR:rotation") tracks/0/interp = 1 tracks/0/loop_wrap = true tracks/0/keys = { "times": PackedFloat32Array(0, 1), "transitions": PackedFloat32Array(1, 1), "update": 0, "values": [0.0, 360.0] } [sub_resource type="AnimationLibrary" id="AnimationLibrary_2aol3"] _data = { &"RESET": SubResource("1"), &"rotate": SubResource("2") } [node name="NetworkImage" type="MarginContainer"] offset_right = 64.0 offset_bottom = 64.0 rotation = 360.0 size_flags_horizontal = 4 size_flags_vertical = 4 mouse_filter = 2 script = ExtResource("1") [node name="TR" type="TextureRect" parent="."] custom_minimum_size = Vector2(64, 64) layout_mode = 2 texture = ExtResource("2") expand_mode = 1 stretch_mode = 6 [node name="Anim" type="AnimationPlayer" parent="."] libraries = { "": SubResource("AnimationLibrary_2aol3") } ================================================ FILE: sample/scenes/UI/PrimaryButton.tscn ================================================ [gd_scene load_steps=5 format=3 uid="uid://t8s6xh1ax7uy"] [sub_resource type="StyleBoxFlat" id="6"] content_margin_left = 8.0 content_margin_top = 2.0 content_margin_right = 8.0 content_margin_bottom = 2.0 bg_color = Color(0.0156863, 0.423529, 0.831373, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="5"] content_margin_left = 4.0 content_margin_right = 4.0 bg_color = Color(0.0196078, 0.478431, 0.941176, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxFlat" id="7"] content_margin_left = 8.0 content_margin_top = 2.0 content_margin_right = 8.0 content_margin_bottom = 2.0 bg_color = Color(0.00784314, 0.239216, 0.470588, 1) corner_radius_top_left = 3 corner_radius_top_right = 3 corner_radius_bottom_right = 3 corner_radius_bottom_left = 3 [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ruuxx"] [node name="PrimaryButton" type="Button"] offset_right = 118.0 offset_bottom = 23.0 mouse_default_cursor_shape = 2 theme_override_colors/font_disabled_color = Color(0.0588235, 0.0588235, 0.0588235, 1) theme_override_styles/normal = SubResource("6") theme_override_styles/hover = SubResource("5") theme_override_styles/pressed = SubResource("5") theme_override_styles/disabled = SubResource("7") theme_override_styles/focus = SubResource("StyleBoxEmpty_ruuxx") text = "PrimaryButton" ================================================ FILE: sample/scenes/UI/joysticks.gd ================================================ extends Control @onready var shoot_joystick: TouchScreenJoystick = $ShootJoystick func _ready() -> void: hide() if Store.is_mobile: show() Store.shoot_joystick = shoot_joystick ================================================ FILE: sample/scenes/UI/joysticks.gd.uid ================================================ uid://dygslh2f7uw40 ================================================ FILE: sample/scenes/UI/nat_type.gd ================================================ extends RichTextLabel #region built-in methods func _ready() -> void: HAuth.logged_in.connect(_on_logged_in) HAuth.logged_out.connect(_on_logged_out) #endregion #region private methods func _on_logged_in(): var nat_type := await HP2P.get_nat_type_async() match nat_type: EOS.P2P.NATType.Open: text = "[right]NAT: [color=#00ff00]Open[/color][/right]" EOS.P2P.NATType.Moderate: text = "[right]NAT: [color=#ffff00]Moderate[/color][/right]" EOS.P2P.NATType.Strict: text = "[right]NAT: [color=#ff0000]Strict[/color][/right]" _: text = "[right]NAT: [color=#515151]Unknown[/color][/right]" show() func _on_logged_out(): text = "" hide() #endregion ================================================ FILE: sample/scenes/UI/nat_type.gd.uid ================================================ uid://ft2lvpnnwl8k ================================================ FILE: sample/scenes/UI/nat_type.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://bdlcslag0jdj1"] [ext_resource type="Script" uid="uid://ft2lvpnnwl8k" path="res://scenes/UI/nat_type.gd" id="1_hma6v"] [node name="NatType" type="RichTextLabel"] anchors_preset = 3 anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 offset_left = -195.0 offset_top = -30.0 offset_right = -8.0 offset_bottom = -7.0 grow_horizontal = 0 grow_vertical = 0 bbcode_enabled = true scroll_active = false script = ExtResource("1_hma6v") ================================================ FILE: sample/scenes/UI/ping.gd ================================================ extends Label const COUNT = 5 var _pings = [] var _ping_in_progress = false var _last_ping_time = 0 func _ready() -> void: hide() func _on_timer_timeout() -> void: if _ping_in_progress and Time.get_ticks_msec() - _last_ping_time > 2000: _ping_in_progress = false if not _ping_in_progress and multiplayer.multiplayer_peer and \ not multiplayer.is_server() and \ multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.ConnectionStatus.CONNECTION_CONNECTED: _ping_in_progress = true _last_ping_time = Time.get_ticks_msec() ping.rpc_id(1) @rpc("any_peer") func ping(): pong.rpc_id(multiplayer.get_remote_sender_id()) @rpc func pong(): var rtt = Time.get_ticks_msec() - _last_ping_time _pings.append(rtt) if _pings.size() > COUNT: _pings.pop_front() var avg = _pings.reduce(func (acc, p): return acc + p, 0.0) / _pings.size() text = "Ping: %s ms" % ceil(avg) show() _ping_in_progress = false ================================================ FILE: sample/scenes/UI/ping.gd.uid ================================================ uid://diilhyvdor4ym ================================================ FILE: sample/scenes/UI/ping.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://c37wtoikc8pgp"] [ext_resource type="Script" uid="uid://diilhyvdor4ym" path="res://scenes/UI/ping.gd" id="1_suu5i"] [node name="Ping" type="Label"] offset_right = 104.0 offset_bottom = 23.0 text = "Ping:" horizontal_alignment = 2 script = ExtResource("1_suu5i") [node name="Timer" type="Timer" parent="."] wait_time = 0.2 autostart = true [connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"] ================================================ FILE: sample/scenes/UI/players_score.gd ================================================ extends Label func _ready() -> void: Store.player_score_changed.connect(_on_players_score_changed) func _on_players_score_changed(): text = "" if not Store.current_lobby: return var players = get_tree().get_nodes_in_group(Store.GROUP_PLAYER) for player in players: var mem = Store.current_lobby.get_member_by_product_user_id(player.puid) if not mem: continue var display_name_attr = mem.get_attribute(LobbiesView.USERNAME_ATTRIBUTE_KEY) var display_name = "User" if display_name_attr: display_name = display_name_attr.value text += "%s: %s\n" % [display_name, player.score] #endregion ================================================ FILE: sample/scenes/UI/players_score.gd.uid ================================================ uid://co6ig2d1xliiu ================================================ FILE: sample/scenes/UI/players_score.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://8kmyoi08gr22"] [ext_resource type="Script" uid="uid://co6ig2d1xliiu" path="res://scenes/UI/players_score.gd" id="1_k6ifu"] [node name="PlayersScore" type="Label"] anchors_preset = 1 anchor_left = 1.0 anchor_right = 1.0 offset_left = -203.0 offset_bottom = 56.0 grow_horizontal = 0 script = ExtResource("1_k6ifu") ================================================ FILE: sample/scenes/UI/touch_screen_joystick.gd ================================================ ## RESPONSIVE TOUCHSCREEN JOYSTICK 1.0.2 ## # From https://github.com/kntCyc1230/TouchScreenJoystick # MIT License # Copyright (c) Kent Aljo G. Coyoca @tool extends Control class_name TouchScreenJoystick @export var use_textures: bool: set(new_bool): use_textures = new_bool notify_property_list_changed() @export var knob_color := Color.WHITE @export var base_color := Color.WHITE @export var background_color := Color(Color.BLACK, 0.25) @export_range(0, 100, 0.1, "or_greater") var base_radius := 130.0 @export_range(0, 100, 0.1, "or_greater") var knob_radius := 65.0 @export var base_thickness := 1.8 @export var anti_aliased: bool @export_group("Textures") @export var use_custom_max_length: bool: set(new_bool): use_custom_max_length = new_bool notify_property_list_changed() @export_range(0, 100, 0.1, "or_greater") var max_length := 120.0 @export_subgroup("Base Texture Params") @export var base_texture: Texture2D @export_range(-360, 360, 0.01, "or_greater", "or_less", "suffix:°") var base_rotation: float @export var base_scale := 1.0 @export_subgroup("Knob Texture Params") @export var knob_texture: Texture2D @export_range(-360, 360, 0.01, "or_greater", "or_less", "suffix:°") var knob_rotation: float @export var knob_scale := 1.0 @export_subgroup("Background Texture Params") @export var background_texture: Texture2D @export_range(-360, 360, 0.01, "or_greater", "or_less", "suffix:°") var backround_rotation: float @export var background_scale := 1.0 @export_group("Joystick Params") @export_enum("FIXED", "DYNAMIC") var mode := 0 @export var deadzone := 10.0: set(new_deadzone): deadzone = clamp(new_deadzone, 10, get_current_max_length()) @export var smooth_reset: bool: set(new_bool): smooth_reset = new_bool notify_property_list_changed() @export var smooth_speed := 5.0 @export var change_opacity_when_touched: bool: set(new_bool): change_opacity_when_touched = new_bool notify_property_list_changed() @export_range(0, 100, 0.01, "suffix:%") var from_opacity := 50.0 @export_range(0, 100, 0.01, "suffix:%") var to_opacity := 100.0 @export var use_input_actions: bool: set(new_bool): use_input_actions = new_bool notify_property_list_changed() @export_subgroup("Input Actions") @export var action_left := "ui_left" @export var action_right := "ui_right" @export var action_up := "ui_up" @export var action_down := "ui_down" @export_group("Debug") @export var draw_debugs: bool: set(new_bool): draw_debugs = new_bool notify_property_list_changed() @export var deadzone_color := Color(Color.RED, 0.5) @export var current_max_length_color := Color(Color.BLUE, 0.5) var is_pressing: bool var knob_position: Vector2 var finger_index := -1 var default_pos: Vector2 var current_input_event: InputEvent var global_touch: Vector2 func _ready() -> void: default_pos = position change_opacity() func _process(_delta: float) -> void: # checks if currently pressing if is_pressing: move_knob_pos() else: reset_knob_pos() # update necessities update_input_actions() pivot_offset = size / 2 queue_redraw() #moves the knob position when pressing func move_knob_pos() -> void: var centered_touch := global_touch - size / 2 if get_distance() <= get_current_max_length(): knob_position = centered_touch else: # calculates the angle position of the knob if it's position -- # -- exceeds from the current max length var angle := Vector2.ZERO.angle_to_point(centered_touch) knob_position.x = cos(angle) * get_current_max_length() knob_position.y = sin(angle) * get_current_max_length() # resets knob position if not pressing func reset_knob_pos() -> void: if smooth_reset: knob_position = lerp(knob_position, Vector2.ZERO, smooth_speed * get_process_delta_time()) else: knob_position = Vector2.ZERO # triggers a specific input action based on the -- # -- current direction func trigger_input_actions() -> void: var dir := get_deadzoned_vector() if dir.x > 0: Input.action_release(action_left) Input.action_press(action_right, dir.x) else: Input.action_release(action_right) Input.action_press(action_left, - dir.x) if dir.y < 0: Input.action_release(action_down) Input.action_press(action_up, - dir.y) else: Input.action_release(action_up) Input.action_press(action_down, dir.y) # releases all input actions func release_input_actions() -> void: Input.action_release(action_right) Input.action_release(action_left) Input.action_release(action_up) Input.action_release(action_down) func update_input_actions() -> void: if use_input_actions and is_pressing: trigger_input_actions() # please ignore these validitate functions it has nothing -- # -- to do with the joystick's functionality func _validate_property(property: Dictionary) -> void: deadzone = clamp(deadzone, 10, get_current_max_length()) validitate_default_drawing_properties(property) validitate_texture_drawing_properties(property) validitate_input_action_properties(property) if property.name == "smooth_speed" and not smooth_reset: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "from_opacity" and not change_opacity_when_touched: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "to_opacity" and not change_opacity_when_touched: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "deadzone_color" and not draw_debugs: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "current_max_length_color" and not draw_debugs: property.usage = PROPERTY_USAGE_READ_ONLY func validitate_input_action_properties(property: Dictionary) -> void: if property.name == "action_left" and not use_input_actions: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "action_right" and not use_input_actions: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "action_up" and not use_input_actions: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "action_down" and not use_input_actions: property.usage = PROPERTY_USAGE_READ_ONLY func validitate_default_drawing_properties(property: Dictionary) -> void: if property.name == "base_color" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "knob_color" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "background_color" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "base_radius" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "knob_radius" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "base_thickness" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "anti_aliased" and use_textures: property.usage = PROPERTY_USAGE_READ_ONLY func validitate_texture_drawing_properties(property: Dictionary) -> void: if property.name == "background_texture" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "use_custom_max_length" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "max_length" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "max_length" and not use_custom_max_length: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "base_texture" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "knob_texture" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "base_scale" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "base_rotation" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "knob_scale" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "knob_rotation" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "background_scale" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY if property.name == "backround_rotation" and not use_textures: property.usage = PROPERTY_USAGE_READ_ONLY # draws the joystick func _draw() -> void: if not use_textures: draw_default_joystick() else: draw_textured_joystick() if draw_debugs: draw_debug() # default joystick func draw_default_joystick() -> void: draw_set_transform(size / 2) # background draw_circle(Vector2.ZERO, base_radius, background_color, true, -1.0, anti_aliased) # base draw_circle(Vector2.ZERO, base_radius, base_color, false, base_thickness, anti_aliased) var pos := knob_position # knob draw_circle(pos, knob_radius, knob_color, true, -1.0, anti_aliased) # draw joystick using custom textures func draw_textured_joystick() -> void: if background_texture: var texture_size := background_texture.get_size() var centered_base_pos := size / 2 draw_set_transform(centered_base_pos, deg_to_rad(backround_rotation), Vector2(background_scale, background_scale)) draw_texture_rect(background_texture, Rect2(Vector2.ZERO - texture_size / 2, base_texture.get_size()), false) # draw textured base if base_texture: var centered_base_pos := size / 2 var texture_size := base_texture.get_size() size.x = clamp(size.x, texture_size.x, INF) size.y = clamp(size.y, texture_size.y, INF) draw_set_transform(centered_base_pos, deg_to_rad(base_rotation), Vector2(base_scale, base_scale)) draw_texture_rect(base_texture, Rect2(Vector2.ZERO - texture_size / 2, base_texture.get_size()), false) # draw texture knob if knob_texture: var centered_knob_pos := size / 2 var texture_size := knob_texture.get_size() draw_set_transform(centered_knob_pos, deg_to_rad(knob_rotation), Vector2(knob_scale, knob_scale)) draw_texture_rect(knob_texture, Rect2((knob_position / knob_scale) - (texture_size / 2), knob_texture.get_size()), false) # draws the deadzone and the current max length func draw_debug() -> void: var length_string := str(round(get_weight() * 100.0), "%") var string_pos := knob_position / 2 + Vector2.UP * 100 var lerped_color := lerp(Color.RED, Color.GREEN, get_weight()) as Color draw_set_transform(size / 2) # draw deadzone draw_circle(Vector2.ZERO, deadzone, deadzone_color, false, 1.0, true) # draw current max length draw_circle(Vector2.ZERO, get_current_max_length(), current_max_length_color, false, 1.0, true) draw_line(Vector2.ZERO, knob_position, lerped_color, 3.0, true) draw_string_outline(SystemFont.new(), string_pos, length_string, HORIZONTAL_ALIGNMENT_CENTER, -1.0, 18, 10, Color.BLACK) @warning_ignore("narrowing_conversion") draw_string(SystemFont.new(), string_pos, length_string, HORIZONTAL_ALIGNMENT_CENTER, -1.0, 18, lerped_color, get_weight()) # handles input func _input(event: InputEvent) -> void: # check if touching if event is InputEventScreenTouch: # checks if touch is pressed if event.pressed: # if touch is pressed, checks first if there's currently no touch index and the touch position has a point -- # -- or is inside the rect if finger_index == -1 and get_global_rect().has_point(event.position): # sets the touch index to the event index finger_index = event.index if event.index == finger_index: on_touched(event) # sets the touch position to event position subtracted to the center position # so the touch position starts from the center of the rect instead of -- # the top left corner of the rect, then divide it to scale.x so the # position keeps centered regardless of the current scale global_touch = (event.position - global_position) / get_global_transform().get_scale().x get_viewport().set_input_as_handled() else: # resets the touch index and touch position if event.index == finger_index: finger_index = -1 global_touch = Vector2.ZERO on_touch_released() get_viewport().set_input_as_handled() # update the position when touch is moving if event is InputEventScreenDrag: if event.index == finger_index: global_touch = (event.position - global_position) / get_global_transform().get_scale().x get_viewport().set_input_as_handled() func on_touched(event: InputEventScreenTouch) -> void: is_pressing = true change_opacity() var mouse_pos := event.position - size / 2 if mode == 1: position = mouse_pos func on_touch_released() -> void: is_pressing = false if mode == 1: position = default_pos release_input_actions() change_opacity() # returns the direction func get_vector() -> Vector2: return get_center_pos().direction_to(knob_position + get_center_pos()) # returns the direction affected by the deadzone func get_deadzoned_vector() -> Vector2: var vector: Vector2 if is_pressing and not is_in_deadzone(): vector = get_center_pos().direction_to(knob_position + get_center_pos()) else: vector = Vector2.ZERO return vector # returns a position that starts at the center of the rect func get_center_pos() -> Vector2: return position + size / 2 # returns the distance from center of the joystick to the position of the knob func get_distance() -> float: return (global_touch - size / 2).distance_to(Vector2.ZERO) # get the current max distance of the knob's position. -- # -- if you use textures, the current max length will -- # -- automatically set to the half base texture's width func get_current_max_length() -> float: var curr_max_length: float if not use_textures: curr_max_length = base_radius else: if use_custom_max_length: curr_max_length = max_length elif not use_custom_max_length and base_texture: curr_max_length = (base_texture.get_size().x / 2) * base_scale return curr_max_length # changes the opacity when touched func change_opacity() -> void: if change_opacity_when_touched and not Engine.is_editor_hint(): if is_pressing: modulate.a = to_opacity / 100.0 else: modulate.a = from_opacity / 100.0 else: modulate.a = 1.0 func is_in_deadzone() -> bool: return get_distance() <= deadzone func is_correct_event_index(event: InputEventScreenTouch) -> bool: return event is InputEventScreenTouch and event.index == finger_index func is_touch_in_rect(touch_pos: Vector2) -> bool: return get_global_rect().has_point(touch_pos) func get_weight() -> float: var weight := (get_distance() / get_current_max_length()) weight = clamp(weight, 0, 1) return weight ================================================ FILE: sample/scenes/UI/touch_screen_joystick.gd.uid ================================================ uid://cgmpdyovn5vfm ================================================ FILE: sample/scenes/UIView/UIView.gd ================================================ class_name UIView extends VBoxContainer @onready var friends_visible_label = %FriendsVisibleLabel @onready var notification_location_btn = %NotificationLocationBtn @onready var notification_location_label = %NotificationLocationLabel @onready var get_toggle_friends_key_btn = %GetToggleFriendsKeyBtn @onready var get_toggle_friends_key_label = %GetToggleFriendsKeyLabel @onready var show_friends_btn = %ShowFriendsBtn @onready var show_block_player_btn = %ShowBlockPlayerBtn @onready var show_report_player_btn = %ShowReportPlayerBtn func _ready() -> void: notification_location_btn.pressed.connect(_on_notification_location_btn_pressed) get_toggle_friends_key_btn.pressed.connect(_on_get_toggle_friends_key_btn) show_friends_btn.pressed.connect(_on_show_friends_btn_pressed) show_block_player_btn.pressed.connect(_on_show_block_player_btn_pressed) show_report_player_btn.pressed.connect(_on_show_report_player_btn_pressed) IEOS.ui_interface_display_settings_updated_callback.connect(_on_display_settings_updated_callback) func _on_notification_location_btn_pressed(): var notification_location = EOS.UI.UIInterface.get_notification_location_preference() notification_location_label.text = EOS.UI.NotificationLocation.keys()[notification_location] func _on_get_toggle_friends_key_btn(): var opts = EOS.UI.GetToggleFriendsKeyOptions.new() var toggle_friends_key = EOS.UI.UIInterface.get_toggle_friends_key(opts) var key_combination = [] var keys = EOS.UI.KeyCombination.keys() for key in keys: if key == "None" or key == "Numpad0": continue if toggle_friends_key&EOS.UI.KeyCombination[key] == EOS.UI.KeyCombination[key]: key_combination.append(key) get_toggle_friends_key_label.text = " + ".join(key_combination) func _on_show_friends_btn_pressed(): var options = EOS.UI.ShowFriendsOptions.new() options.local_user_id = HAuth.epic_account_id EOS.UI.UIInterface.show_friends(options) func _on_show_block_player_btn_pressed(): var opts = EOS.UI.ShowBlockPlayerOptions.new() opts.local_user_id = HAuth.epic_account_id opts.target_user_id = HAuth.epic_account_id EOS.UI.UIInterface.show_block_player(opts) print("--- UI: show_block_player_callback: ", EOS.result_str(await IEOS.ui_interface_show_block_player_callback)) func _on_show_report_player_btn_pressed(): var opts = EOS.UI.ShowReportPlayerOptions.new() opts.local_user_id = HAuth.epic_account_id opts.target_user_id = HAuth.epic_account_id EOS.UI.UIInterface.show_report_player(opts) print("--- UI: show_report_player_callback: ", EOS.result_str(await IEOS.ui_interface_show_report_player_callback)) func _on_display_settings_updated_callback(data: Dictionary): # print("--- UI: display_settings_updated_callback: ", data) if data.is_visible: friends_visible_label.text = "Friends Overlay Visible: Yes" else: friends_visible_label.text = "Friends Overlay Visible: No" ================================================ FILE: sample/scenes/UIView/UIView.gd.uid ================================================ uid://dja7fd2sotvig ================================================ FILE: sample/scenes/UIView/UIView.tscn ================================================ [gd_scene load_steps=4 format=3 uid="uid://b51w7a6ofuubp"] [ext_resource type="Script" uid="uid://dja7fd2sotvig" path="res://scenes/UIView/UIView.gd" id="1"] [ext_resource type="LabelSettings" uid="uid://dcb771ib3tfno" path="res://styles/ViewTitleLabelSettings.tres" id="2_pdqi2"] [ext_resource type="PackedScene" uid="uid://t8s6xh1ax7uy" path="res://scenes/UI/PrimaryButton.tscn" id="5"] [node name="UIView" type="VBoxContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 size_flags_horizontal = 3 size_flags_vertical = 3 script = ExtResource("1") [node name="Title" type="Label" parent="."] layout_mode = 2 size_flags_vertical = 1 text = "UI" label_settings = ExtResource("2_pdqi2") [node name="VB" type="VBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 16 [node name="HB" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="FriendsVisibleLabel" type="Label" parent="VB/HB"] unique_name_in_owner = true layout_mode = 2 text = "Friends Overlay Visible: ?" [node name="HB2" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="NotificationLocationBtn" parent="VB/HB2" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Get Notification Location" [node name="NotificationLocationLabel" type="Label" parent="VB/HB2"] unique_name_in_owner = true layout_mode = 2 text = "Click button to load data" [node name="HB4" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="GetToggleFriendsKeyBtn" parent="VB/HB4" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Get Toggle Friends Key" [node name="GetToggleFriendsKeyLabel" type="Label" parent="VB/HB4"] unique_name_in_owner = true layout_mode = 2 text = "Click button to load data" [node name="HB3" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="ShowFriendsBtn" parent="VB/HB3" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Show Friends Overlay" [node name="HB5" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="ShowBlockPlayerBtn" parent="VB/HB5" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Show Block Player" [node name="HB6" type="HBoxContainer" parent="VB"] layout_mode = 2 theme_override_constants/separation = 16 [node name="ShowReportPlayerBtn" parent="VB/HB6" instance=ExtResource("5")] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 0 text = "Show Report Player" ================================================ FILE: sample/scripts/Env.gd ================================================ extends Node var _env = {} func load_env(p_path := "res://.env"): if not FileAccess.file_exists(p_path): return {} var file = FileAccess.open(p_path, FileAccess.READ) var ret = {} while not file.eof_reached(): var line = file.get_line() # skip comment lines if line.lstrip(" ").begins_with("#"): continue var tokens = line.split("=", false, 1) if tokens.size() == 2: ret[tokens[0]] = tokens[1].lstrip("\"").rstrip("\""); return ret func _ready() -> void: _env = load_env() func get_var(p_name: String, p_default = null): if _env.has(p_name): return _env[p_name] return p_default ================================================ FILE: sample/scripts/Env.gd.uid ================================================ uid://bjyq7tcpdc3ac ================================================ FILE: sample/scripts/Store.gd ================================================ extends Node @warning_ignore("unused_signal") signal player_score_changed const GAME_SOCKET_NAME = "EOSGSAMPLE" const GROUP_PLAYER = "GRP_PLAYER" const GROUP_MAP = "GRP_MAP" var display_name = "" # For testing things that need a second player var second_epic_account_id = "e5d94c924f204a63bd343b976ae662cd" var second_product_user_id = "0002620f4b524b099abf1b48db7137a2" var _main_node: Control var network: MyNetwork var current_lobby: HLobby var shoot_joystick: TouchScreenJoystick @onready var is_mobile := OS.get_name() in ["iOS", "Android"] func get_view(view_name: String): return _main_node.get_view_manager().get_view(view_name) func set_view(view_name: String): return _main_node.get_view_manager().set_view(view_name) # NetworkImage cache var network_image_cache = { # url: Texture } func get_new_random(arr: Array, cur = null): if arr.size() == 1: return arr[0] var new_elm = arr.pick_random() while cur != null and cur == new_elm: new_elm = arr.pick_random() return new_elm ================================================ FILE: sample/scripts/Store.gd.uid ================================================ uid://b4qb3afy3c108 ================================================ FILE: sample/scripts/ViewManager.gd ================================================ class_name ViewManager extends VBoxContainer @onready var auth_btn = %AuthBtn @onready var achievements_btn = %AchievementsBtn @onready var custominvites_btn = %CustomInvitesBtn @onready var stats_btn = %StatsBtn @onready var leaderboards_btn = %LeaderboardsBtn @onready var friends_btn = %FriendsBtn @onready var ui_btn = %UIBtn @onready var metrics_btn = %MetricsBtn @onready var lobbies_btn = %LobbiesBtn @onready var needs_login_btns = %NeedsLoginBtns func _ready() -> void: _disable_needs_login_btns() HAuth.logged_in.connect(_enable_needs_login_btns) HAuth.logged_out.connect(_disable_needs_login_btns) auth_btn.pressed.connect(Callable(self, "set_view").bind("Login")) achievements_btn.pressed.connect(Callable(self, "set_view").bind("Achievements")) custominvites_btn.pressed.connect(Callable(self, "set_view").bind("CustomInvites")) stats_btn.pressed.connect(Callable(self, "set_view").bind("Stats")) leaderboards_btn.pressed.connect(Callable(self, "set_view").bind("Leaderboards")) friends_btn.pressed.connect(Callable(self, "set_view").bind("Friends")) ui_btn.pressed.connect(Callable(self, "set_view").bind("UI")) metrics_btn.pressed.connect(Callable(self, "set_view").bind("Metrics")) lobbies_btn.pressed.connect(Callable(self, "set_view").bind("Lobbies")) func get_view(view_name: String): if view_name == "Logs": return $VSC/LogsView elif view_name == "Notifications": return $VSC/VB/NotificationsLayer/NotificationsView return get_node("VSC/VB/" + view_name + "View") func set_view(view_name: String): for child in $VSC/VB.get_children(): if child is CanvasItem: child.visible = false get_view(view_name).visible = true func _disable_needs_login_btns(): for child in needs_login_btns.get_children(): child.disabled = true func _enable_needs_login_btns(): for child in needs_login_btns.get_children(): child.disabled = false ================================================ FILE: sample/scripts/ViewManager.gd.uid ================================================ uid://b8dbhfvrewi8w ================================================ FILE: sample/scripts/network.gd ================================================ class_name MyNetwork extends Node2D const PLAYER = preload("res://game/entities/player/player.tscn") const MAP_BELLANDUR = preload("res://game/maps/map_bellandur.tscn") const MAP_MARGAO = preload("res://game/maps/map_margao.tscn") const MAP_NEW_YORK = preload("res://game/maps/map_new_york.tscn") @onready var _player_spawner: MultiplayerSpawner = $Game/PlayerSpawner @onready var _network_spawn: Node2D = $Game/NetworkSpawn @onready var _mc: MarginContainer = $"../MC" @onready var _canvas_layer: CanvasLayer = $Game/CanvasLayer var peer = EOSGMultiplayerPeer.new() func _ready() -> void: Store.network = self _player_spawner.spawn_function = _spawn_player _mc.show() _canvas_layer.hide() func create_server(): print("Creating server...") peer.create_server(Store.GAME_SOCKET_NAME) peer.peer_connected.connect(_on_peer_connected) peer.peer_disconnected.connect(_on_peer_disconnected) multiplayer.multiplayer_peer = peer func start_game(map: LobbiesView.Maps): print("Starting game: map=%s" % LobbiesView.Maps.find_key(map)) _spawn_map(map) _player_spawner.spawn({peer_id = 1, puid = HAuth.product_user_id}) _show_map() func join_game(remote_puid: String): print("Joining game: remote_puid=%s" % remote_puid) peer.create_client(Store.GAME_SOCKET_NAME, remote_puid) peer.peer_disconnected.connect(_on_peer_disconnected) multiplayer.multiplayer_peer = peer _show_map() @rpc("any_peer", "call_local", "reliable") func add_score(peer_id: int, amt: int): var player = _network_spawn.get_node(str(peer_id)) if player: player.score += amt Store.player_score_changed.emit() func _on_peer_connected(peer_id: int): var puid = peer.get_peer_user_id(peer_id) print("server:: client connected: peer_id=%s, puid=%s" % [peer_id, puid]) _player_spawner.spawn({peer_id = peer_id, puid = puid}) func _on_peer_disconnected(peer_id: int): if multiplayer.is_server(): print("server:: client disconnected: %s" % peer_id) _remove_player_by_peer_id(peer_id) else: print("client:: server disconnected") multiplayer.multiplayer_peer = null func _spawn_map(map: LobbiesView.Maps): var map_node = null match map: LobbiesView.Maps.Margao: map_node = MAP_MARGAO.instantiate() LobbiesView.Maps.Bellandur: map_node = MAP_BELLANDUR.instantiate() LobbiesView.Maps.NewYork: map_node = MAP_NEW_YORK.instantiate() if map_node: _network_spawn.add_child(map_node, true) func _spawn_player(data: Dictionary): var peer_id = data.peer_id var puid = data.puid var player: Player = PLAYER.instantiate() player.name = str(peer_id) player.peer_id = peer_id player.puid = puid player.global_position = _get_player_spawn_point().global_position player.died.connect(_on_player_died) return player func _remove_player_by_peer_id(peer_id: int): var players = get_tree().get_nodes_in_group(Store.GROUP_PLAYER) for player: Player in players: if player.peer_id == peer_id: player.queue_free() func _on_player_died(player: Player, _killing_peer_id: int): if player.is_multiplayer_authority(): print("Respawning player: peer_id=%s, puid=%s" % [player.peer_id, player.puid]) player.global_position = _get_random_spawn_point().global_position player.health = 100 func _show_map(): show() _mc.hide() _canvas_layer.show() func _get_random_spawn_point() -> Node2D: var map = get_tree().get_nodes_in_group(Store.GROUP_MAP)[0] var total_spawn_points = map.get_node("SpawnPoints").get_child_count() return map.get_node("SpawnPoints").get_child(randi_range(0, total_spawn_points - 1)) func _get_player_spawn_point() -> Node2D: var map = get_tree().get_nodes_in_group(Store.GROUP_MAP)[0] var total_spawn_points = map.get_node("SpawnPoints").get_child_count() var idx = get_tree().get_nodes_in_group(Store.GROUP_PLAYER).size() % total_spawn_points return map.get_node("SpawnPoints").get_child(idx) ================================================ FILE: sample/scripts/network.gd.uid ================================================ uid://d0vgtesnrato5 ================================================ FILE: sample/styles/ViewTitleLabelSettings.tres ================================================ [gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://dcb771ib3tfno"] [ext_resource type="FontFile" uid="uid://djdr1hyqtgka6" path="res://fonts/Roboto-Bold.ttf" id="1_d1ecq"] [resource] font = ExtResource("1_d1ecq") font_size = 24 ================================================ FILE: sample/test.gd ================================================ extends Node @onready var IS_ANTICHEAT_SERVER: String = Env.get_var("IS_ANTICHEAT_SERVER", "false") var anticheat_server_main: AntiCheatServerMain = null func _ready() -> void: if IS_ANTICHEAT_SERVER != "false": anticheat_server_main = AntiCheatServerMain.new() add_child(anticheat_server_main) return # For dev testing HAuth.logged_in.connect(_on_tab_pressed) func _input(event: InputEvent) -> void: if event is InputEventKey and event.pressed: if event.keycode == KEY_QUOTELEFT: # Press ` (backtick) key to toggle Logs Store.get_view("Logs").visible = not Store.get_view("Logs").visible elif event.keycode == KEY_TAB: # Press tab key to run dev testing stuff _on_tab_pressed() # Dev testing stuff func _on_tab_pressed(): print("Tab pressed") # test_auth_interface() # test_connect_interface() # test_ecom_interface() # test_kws_interface() # test_mods_interface() # test_platform_interface() # test_playerdatastorage_interface() # test_presence_interface() # test_progression_snapshot_interface() # test_reports_interface() # test_sanctions_interface() # test_sessions_interface() # test_titlestorage_interface() # test_user_info_interface() # test_rtc_interface() # test_anticheat_server_interface() # test_anticheat_client_interface() func test_auth_interface(): print("--- Auth: get_logged_in_accounts_count: ", EOS.Auth.AuthInterface.get_logged_in_accounts_count()) print("--- Auth: Logged in account by index(0): ", EOS.Auth.AuthInterface.get_logged_in_account_by_index(0)) print("--- Auth: get_login_status: ", EOS.Auth.AuthInterface.get_login_status(HAuth.epic_account_id)) print("--- Auth: get_merged_accounts_count: ", EOS.Auth.AuthInterface.get_merged_accounts_count(HAuth.epic_account_id)) print("--- Auth: Get merged account by index(0): ", EOS.Auth.AuthInterface.get_merged_account_by_index(HAuth.epic_account_id, 0)) print("--- Auth: get_selected_account_id: ", EOS.Auth.AuthInterface.get_selected_account_id(HAuth.epic_account_id)) var copy_user_auth_token = EOS.Auth.AuthInterface.copy_user_auth_token(EOS.Auth.CopyUserAuthTokenOptions.new(), HAuth.epic_account_id) var token = copy_user_auth_token.token var verify_user_auth_options = EOS.Auth.VerifyUserAuthOptions.new() verify_user_auth_options.auth_token = token EOS.Auth.AuthInterface.verify_user_auth(verify_user_auth_options) var verify_user_auth_ret = await IEOS.auth_interface_verify_user_auth_callback print("--- Auth: verify_user_auth: ", verify_user_auth_ret) var copy_id_token_options = EOS.Auth.CopyIdTokenOptions.new() copy_id_token_options.account_id = HAuth.epic_account_id print("--- Auth: copy_id_token: ", EOS.result_str(EOS.Auth.AuthInterface.copy_id_token(copy_id_token_options))) func test_platform_interface(): print("--- Platform: get_active_country_code: ", EOS.Platform.PlatformInterface.get_active_country_code(HAuth.epic_account_id)) print("--- Platform: get_active_locale_code: ", EOS.Platform.PlatformInterface.get_active_locale_code(HAuth.epic_account_id)) print("--- Platform: get_override_country_code: ", EOS.Platform.PlatformInterface.get_override_country_code()) print("--- Platform: get_override_locale_code: ", EOS.Platform.PlatformInterface.get_override_locale_code()) func test_connect_interface(): var opts12 = EOS.Connect.QueryProductUserIdMappingsOptions.new() opts12.product_user_ids = [Store.second_product_user_id] EOS.Connect.ConnectInterface.query_product_user_id_mappings(opts12) var res12 = await IEOS.connect_interface_query_product_user_id_mappings_callback print("--- Connect: query_product_user_id_mappings: ", _get_res_if_sucess(res12)) var opts4 = EOS.Connect.CopyProductUserInfoOptions.new() print("--- Connect: copy_product_user_info: ", EOS.Connect.ConnectInterface.copy_product_user_info(opts4)) var opts11 = EOS.Connect.QueryExternalAccountMappingsOptions.new() opts11.account_id_type = EOS.ExternalAccountType.Epic opts11.external_account_ids = [Store.second_epic_account_id] EOS.Connect.ConnectInterface.query_external_account_mappings(opts11) var res11 = await IEOS.connect_interface_query_external_account_mappings_callback print("--- Connect: query_external_account_mappings: ", _get_res_if_sucess(res11)) var opts1 = EOS.Connect.CopyProductUserExternalAccountByAccountIdOptions.new() opts1.account_id = Store.second_epic_account_id var res1 = EOS.Connect.ConnectInterface.copy_product_user_external_account_by_account_id(opts1) print("--- Connect: copy_product_user_external_account: By account Id: ", _get_res_if_sucess(res1)) var opts2 = EOS.Connect.CopyProductUserExternalAccountByAccountTypeOptions.new() opts2.account_id_type = EOS.ExternalAccountType.Epic var res2 = EOS.Connect.ConnectInterface.copy_product_user_external_account_by_account_type(opts2) print("--- Connect: copy_product_user_external_account: By account type: ", _get_res_if_sucess(res2)) var opts3 = EOS.Connect.CopyProductUserExternalAccountByIndexOptions.new() opts3.external_account_info_index = 0 var res3 = EOS.Connect.ConnectInterface.copy_product_user_external_account_by_index(opts3) print("--- Connect: copy_product_user_external_account: By account index: ", _get_res_if_sucess(res3)) #var opts5 = EOS.Connect.CopyIdTokenOptions.new() #var ret5 = EOS.Connect.ConnectInterface.copy_id_token(opts5) #var id_token = EOS.Connect.IdToken.new() #id_token.json_web_token = ret5.id_token.json_web_token #id_token.product_user_id = ret5.id_token.product_user_id #print("-- Connect: copy_id_token: Id token: ", id_token) #var opts10 = EOS.Connect.VerifyIdTokenOptions.new() #opts10.id_token = id_token #EOS.Connect.ConnectInterface.verify_id_token(opts10) #print("--- Connect: verify_id_token: ", await IEOS.connect_interface_verify_id_token_callback) var opts6 = EOS.Connect.GetExternalAccountMappingsOptions.new() opts6.account_id_type = EOS.ExternalAccountType.Epic opts6.target_external_user_id = Store.second_epic_account_id print("--- Connect: get_external_account_mapping: ", EOS.Connect.ConnectInterface.get_external_account_mapping(opts6)) print("--- Connect: get_logged_in_user_by_index(0): ", EOS.Connect.ConnectInterface.get_logged_in_user_by_index(0)) print("--- Connect: get_logged_in_users_count: ", EOS.Connect.ConnectInterface.get_logged_in_users_count()) print("--- Connecet: get_login_status: ", EOS.Connect.ConnectInterface.get_login_status(HAuth.product_user_id)) var opts7 = EOS.Connect.GetProductUserExternalAccountCountOptions.new() print("--- Connect: get_product_user_external_account_count: ", EOS.Connect.ConnectInterface.get_product_user_external_account_count(opts7)) var opts8 = EOS.Connect.GetProductUserIdMappingOptions.new() opts8.account_id_type = EOS.ExternalAccountType.Discord opts8.target_product_user_id = "External Account Id Here" print("--- Connect: get_product_user_id_mapping: ", EOS.Connect.ConnectInterface.get_product_user_id_mapping(opts8)) #var opts9 = EOS.Connect.LinkAccountOptions.new() #opts9.continuance_token = ContinunanceTokenEOSG object here #EOS.Connect.ConnectInterface.link_account(opts9) #print("--- Connect: link_account: ", _get_res_if_success(await IEOS.connect_interface_link_account_callback)) func test_ecom_interface(): var offers_options = EOS.Ecom.QueryOffersOptions.new() EOS.Ecom.EcomInterface.query_offers(offers_options) print("--- Ecom: query_offers: ", await IEOS.ecom_interface_query_offers_callback) var checkout_options = EOS.Ecom.CheckoutOptions.new() checkout_options.entries = [ { offer_id = "OFFER ID HERE" }] EOS.Ecom.EcomInterface.checkout(checkout_options) var checkout_data = await IEOS.ecom_interface_checkout_callback print("--- Ecom: checkout: ", checkout_data) func test_user_info_interface(): var opts1 = EOS.UserInfo.QueryUserInfoOptions.new() EOS.UserInfo.UserInfoInterface.query_user_info(opts1) print("--- UserInfo: query_user_info: ", EOS.result_str(await IEOS.user_info_interface_query_user_info_callback)) var opts2 = EOS.UserInfo.CopyUserInfoOptions.new() print("--- UserInfo: copy_user_info: ", EOS.UserInfo.UserInfoInterface.copy_user_info(opts2)) var opts3 = EOS.UserInfo.QueryUserInfoByDisplayNameOptions.new() opts3.display_name = "3ddelano" EOS.UserInfo.UserInfoInterface.query_user_info_by_display_name(opts3) print("--- UserInfo: query_user_info_by_display_name: ", EOS.result_str(await IEOS.user_info_interface_query_user_info_by_display_name_callback)) var opts4 = EOS.UserInfo.QueryUserInfoByExternalAccountOptions.new() opts4.account_type = EOS.ExternalAccountType.Epic opts4.external_account_id = HAuth.epic_account_id EOS.UserInfo.UserInfoInterface.query_user_info_by_external_account(opts4) print("--- UserInfo: query_user_info_by_external_account: ", EOS.result_str(await IEOS.user_info_interface_query_user_info_by_external_account_callback)) var opts5 = EOS.UserInfo.CopyExternalUserInfoByAccountIdOptions.new() opts5.account_id = HAuth.epic_account_id print("--- UserInfo: copy_external_user_info_by_account_id: ", EOS.UserInfo.UserInfoInterface.copy_external_user_info_by_account_id(opts5)) var opts6 = EOS.UserInfo.CopyExternalUserInfoByAccountTypeOptions.new() opts6.account_type = EOS.ExternalAccountType.Epic EOS.UserInfo.UserInfoInterface.copy_external_user_info_by_account_type(opts6) var opts7 = EOS.UserInfo.CopyExternalUserInfoByIndexOptions.new() opts7.index = 0 EOS.UserInfo.UserInfoInterface.copy_external_user_info_by_index(opts7) var opts8 = EOS.UserInfo.GetExternalUserInfoCountOptions.new() EOS.UserInfo.UserInfoInterface.get_external_user_info_count(opts8) func test_mods_interface(): var opts1 = EOS.Mods.EnumerateModsOptions.new() opts1.type = EOS.Mods.ModEnumerationType.AllAvailable EOS.Mods.ModsInterface.enumerate_mods(opts1) print("--- Mods: enumerate_mods: ", EOS.result_str(await IEOS.mods_interface_enumerate_mods_callback)) var opts2 = EOS.Mods.CopyModInfoOptions.new() opts2.type = EOS.Mods.ModEnumerationType.AllAvailable var copy_mods_info_data = EOS.Mods.ModsInterface.copy_mod_info(opts2) print("--- Mods: copy_mod_info: ", copy_mods_info_data) var mod = {} if copy_mods_info_data.mods != null: mod = copy_mods_info_data.mods.mods[0] var opts3 = EOS.Mods.InstallModOptions.new() opts3.mod = mod EOS.Mods.ModsInterface.install_mod(opts3) print("--- Mods: install_mod: ", EOS.result_str(await IEOS.mods_interface_install_mod_callback)) func test_reports_interface(): var report_options = EOS.Reports.SendPlayerBehaviorReportOptions.new() report_options.reported_user_id = Store.second_product_user_id report_options.category = EOS.Reports.PlayerReportsCategory.Cheating report_options.message = "this is a test report from godot" report_options.context = JSON.stringify({hello = "testing"}) EOS.Reports.ReportsInterface.send_player_behavior_report(report_options) print("--- Reports: send_player_behavior_report: ", EOS.result_str(await IEOS.reports_interface_send_player_behavior_report_callback)) func test_progression_snapshot_interface(): var opts1 = EOS.ProgressionSnapshot.BeginSnapshotOptions.new() var begin_snapshot_ret = EOS.ProgressionSnapshot.ProgressionSnapshotInterface.begin_snapshot(opts1) print("--- ProgressionSnapshot: begin_snapshot: ", EOS.result_str(begin_snapshot_ret)) var key_values = { test_key = "test_value", health = "100", pos_x = "-158.6", pos_y = "14.7" } for key in key_values: var opts2 = EOS.ProgressionSnapshot.AddProgressionOptions.new() opts2.snapshot_id = begin_snapshot_ret.snapshot_id opts2.key = key opts2.value = str(key_values[key]) print("--- ProgressionSnapshot: add_progression: ", EOS.result_str(EOS.ProgressionSnapshot.ProgressionSnapshotInterface.add_progression(opts2))) var opts3 = EOS.ProgressionSnapshot.SubmitSnapshotOptions.new() opts3.snapshot_id = begin_snapshot_ret.snapshot_id EOS.ProgressionSnapshot.ProgressionSnapshotInterface.submit_snapshot(opts3) print("--- ProgressionSnapshot: submit_snapshot_callback: ", EOS.result_str(await IEOS.progression_snapshot_interface_submit_snapshot_callback)) var opts4 = EOS.ProgressionSnapshot.DeleteSnapshotOptions.new() EOS.ProgressionSnapshot.ProgressionSnapshotInterface.delete_snapshot(opts4) print("--- ProgressionSnapshot: delete_snapshot_callback: ", EOS.result_str(await IEOS.progression_snapshot_interface_delete_snapshot_callback)) func test_presence_interface(): var opts1 = EOS.Presence.CreatePresenceModificationOptions.new() var create_pmod_ret = EOS.Presence.PresenceInterface.create_presence_modification(opts1) print("--- Presence: create_presence_modification: ", EOS.result_str(create_pmod_ret)) var presence_modification: EOSGPresenceModification = create_pmod_ret.presence_modification var set_data_ret = presence_modification.set_data({ test_key = "test_val", hello = "world" }) print("--- EOSGPresenceModification: set_data: ", EOS.result_str(set_data_ret)) var delete_data_ret = presence_modification.delete_data(["hello"]) print("--- EOSGPresenceModification: delete_data: ", EOS.result_str(delete_data_ret)) var set_join_info_ret = presence_modification.set_join_info("this is sample join info") print("--- EOSGPresenceModification: set_join_info: ", EOS.result_str(set_join_info_ret)) var set_raw_rich_text_ret = presence_modification.set_raw_rich_text("this is sample join info") print("--- EOSGPresenceModification: set_raw_rich_text: ", EOS.result_str(set_raw_rich_text_ret)) var set_status_ret = presence_modification.set_status(EOS.Presence.Status.Online) print("--- EOSGPresenceModification: set_status: ", EOS.result_str(set_status_ret)) var opts6 = EOS.Presence.SetPresenceOptions.new() opts6.presence_modification = presence_modification EOS.Presence.PresenceInterface.set_presence(opts6) print("--- Presence: set_presence: ", EOS.result_str(await IEOS.presence_interface_set_presence_callback)) var opts2 = EOS.Presence.GetJoinInfoOptions.new() var get_join_info_ret = EOS.Presence.PresenceInterface.get_join_info(opts2) print("--- Presence: get_join_info: ", EOS.result_str(get_join_info_ret)) var opts3 = EOS.Presence.HasPresenceOptions.new() var has_presence_ret: bool = EOS.Presence.PresenceInterface.has_presence(opts3) print("--- Presence: has_presence: ", has_presence_ret) var opst4 = EOS.Presence.QueryPresenceOptions.new() EOS.Presence.PresenceInterface.query_presence(opst4) print("--- Presence: query_presence: ", EOS.result_str(await IEOS.presence_interface_query_presence_callback)) var opts5 = EOS.Presence.CopyPresenceOptions.new() var copy_presence_ret = EOS.Presence.PresenceInterface.copy_presence(opts5) print("--- Presence: copy_presence: ", EOS.result_str(copy_presence_ret)) print(JSON.stringify(copy_presence_ret, "\t", true, true)) func test_kws_interface(): var opts1 = EOS.KWS.CreateUserOptions.new() opts1.parent_email = "3ddelano1@gmail.com" opts1.date_of_birth = "2002-01-01" EOS.KWS.KWSInterface.create_user(opts1) var create_user_ret = await IEOS.kws_interface_create_user_callback print("--- KWS: create_user_callback: ", EOS.result_str(create_user_ret)) var opts2 = EOS.KWS.QueryPermissionsOptions.new() EOS.KWS.KWSInterface.query_permissions(opts2) print("--- KWS: query_permissions_callback: ", await IEOS.kws_interface_query_permissions_callback) # EOS.KWS.KWSInterface.get_permissions_count() # EOS.KWS.KWSInterface.get_permission_by_key() # KWS.KWSInterface.copy_permission_by_index() # KWS.KWSInterface.query_age_gate() # EOS.KWS.KWSInterface.request_permissions() # EOS.KWS.KWSInterface.update_parent_email() func test_playerdatastorage_interface(): # var query_file_opts = EOS.PlayerDataStorage.QueryFileOptions.new() # query_file_opts.filename = "testfile-001.txt" # EOS.PlayerDataStorage.PlayerDataStorageInterface.query_file(query_file_opts) # print("--- PlayerDataStorage: query_file: ", await IEOS.playerdatastorage_interface_query_file_callback) # var query_file_list_opts = EOS.PlayerDataStorage.QueryFileListOptions.new() # EOS.PlayerDataStorage.PlayerDataStorageInterface.query_file_list(query_file_list_opts) # print("--- PlayerDataStorage: query_file_list: ", await IEOS.playerdatastorage_interface_query_file_list_callback) # var get_meta_count_opts = EOS.PlayerDataStorage.GetFileMetadataCountOptions.new() # print("--- PlayerDataStorage: get_file_metadata_count: ", EOS.PlayerDataStorage.PlayerDataStorageInterface.get_file_metadata_count(get_meta_count_opts)) # var copy_meta_at_index_opts = EOS.PlayerDataStorage.CopyFileMetadataAtIndexOptions.new() # copy_meta_at_index_opts.index = 0 # print("--- PlayerDataStorage: copy_file_metadata_at_index: ", EOS.PlayerDataStorage.PlayerDataStorageInterface.copy_file_metadata_at_index(copy_meta_at_index_opts)) # var copy_meta_by_filename_opts = EOS.PlayerDataStorage.CopyFileMetadataByFilenameOptions.new() # copy_meta_by_filename_opts.filename = "testfile-001.txt" # print("--- PlayerDataStorage: copy_file_metadata_by_filename: ", EOS.PlayerDataStorage.PlayerDataStorageInterface.copy_file_metadata_by_filename(copy_meta_by_filename_opts)) # var duplicate_opts = EOS.PlayerDataStorage.DuplicateFileOptions.new() # duplicate_opts.source_filename = "testfile-001.txt" # duplicate_opts.destination_filename = "testfile-001-copy.txt" # EOS.PlayerDataStorage.PlayerDataStorageInterface.duplicate_file(duplicate_opts) # print("--- PlayerDataStorage: duplicate_file: ", await IEOS.playerdatastorage_interface_duplicate_file_callback) # var delete_file_opts = EOS.PlayerDataStorage.DeleteFileOptions.new() # delete_file_opts.filename = "testfile-001-copy.txt" # EOS.PlayerDataStorage.PlayerDataStorageInterface.delete_file(delete_file_opts) # print("--- PlayerDataStorage: delete_file: ", await IEOS.playerdatastorage_interface_delete_file_callback) # var delete_cache_opts = EOS.PlayerDataStorage.DeleteCacheOptions.new() # EOS.PlayerDataStorage.PlayerDataStorageInterface.delete_cache(delete_cache_opts) # print("--- PlayerDataStorage: delete_cache: ", await IEOS.playerdatastorage_interface_delete_cache_callback) IEOS.playerdatastorage_interface_file_transfer_progress_callback.connect(func(data): print("--- PlayerDataStorage: file_transfer_progress_callback: ", data) ) IEOS.playerdatastorage_interface_write_file_callback.connect(func(data): print("--- PlayerDataStorage: write_file_callback: ", data) ) IEOS.playerdatastorage_interface_write_file_data_callback.connect(func(data): print("--- PlayerDataStorage: write_file_data_callback: ", data) ) IEOS.playerdatastorage_interface_read_file_callback.connect(func(data): print("--- PlayerDataStorage: read_file_callback: ", data) ) IEOS.playerdatastorage_interface_read_file_data_callback.connect(func(data): print("--- PlayerDataStorage: read_file_data_callback: ", data) ) var write_file_opts = EOS.PlayerDataStorage.WriteFileOptions.new() write_file_opts.filename = "testfile-002.txt" write_file_opts.data = PackedByteArray([65, 66, 67]) var _write_transfer_request: EOSGFileTransferRequest = EOS.PlayerDataStorage.PlayerDataStorageInterface.write_file(write_file_opts) # write_transfer_request.cancel_request() await IEOS.playerdatastorage_interface_write_file_callback print("Reading file") var read_file_opts = EOS.PlayerDataStorage.ReadFileOptions.new() read_file_opts.filename = "testfile-002.txt" var _read_transfer_request: EOSGFileTransferRequest = EOS.PlayerDataStorage.PlayerDataStorageInterface.read_file(read_file_opts) # print(read_transfer_request.get_filename()) # print(read_transfer_request.cancel_request()) func test_titlestorage_interface(): IEOS.titlestorage_interface_file_transfer_progress_callback.connect(func(data): print("--- TitleStorage: file_transfer_progress_callback: ", data) ) IEOS.titlestorage_interface_read_file_callback.connect(func(data): print("--- TitleStorage: read_file_callback: ", data) ) IEOS.titlestorage_interface_read_file_data_callback.connect(func(data): print("--- TitleStorage: read_file_data_callback: ", data) ) var read_file_opts = EOS.TitleStorage.ReadFileOptions.new() read_file_opts.filename = "title-001.txt" var _read_transfer_request: EOSGFileTransferRequest = EOS.TitleStorage.TitleStorageInterface.read_file(read_file_opts) #print(read_transfer_request.get_filename()) #print(read_transfer_request.cancel_request()) var query_file_opts = EOS.TitleStorage.QueryFileOptions.new() query_file_opts.filename = "title-001.txt" EOS.TitleStorage.TitleStorageInterface.query_file(query_file_opts) print("--- TitleStorage: query_file: ", await IEOS.titlestorage_interface_query_file_callback) var query_file_list_opts = EOS.TitleStorage.QueryFileListOptions.new() query_file_list_opts.list_of_tags = ["hey"] EOS.TitleStorage.TitleStorageInterface.query_file_list(query_file_list_opts) print("--- TitleStorage: query_file_list: ", await IEOS.titlestorage_interface_query_file_list_callback) var get_meta_count_opts = EOS.TitleStorage.GetFileMetadataCountOptions.new() print("--- TitleStorage: get_file_metadata_count: ", EOS.TitleStorage.TitleStorageInterface.get_file_metadata_count(get_meta_count_opts)) var copy_meta_at_index_opts = EOS.TitleStorage.CopyFileMetadataAtIndexOptions.new() copy_meta_at_index_opts.index = 0 print("--- TitleStorage: copy_file_metadata_at_index: ", EOS.TitleStorage.TitleStorageInterface.copy_file_metadata_at_index(copy_meta_at_index_opts)) var copy_meta_by_filename_opts = EOS.TitleStorage.CopyFileMetadataByFilenameOptions.new() copy_meta_by_filename_opts.filename = "title-001.txt" print("--- TitleStorage: copy_file_metadata_by_filename: ", EOS.TitleStorage.TitleStorageInterface.copy_file_metadata_by_filename(copy_meta_by_filename_opts)) var delete_cache_opts = EOS.TitleStorage.DeleteCacheOptions.new() EOS.TitleStorage.TitleStorageInterface.delete_cache(delete_cache_opts) print("--- TitleStorage: delete_cache: ", await IEOS.titlestorage_interface_delete_cache_callback) func test_sanctions_interface(): var query_active_sanctions_opts = EOS.Sanctions.QueryActivePlayerSanctionsOptions.new() EOS.Sanctions.SanctionsInterface.query_active_player_sanctions(query_active_sanctions_opts) print("--- Sanctions: query_active_player_sanctions: ", await IEOS.sanctions_interface_query_active_player_sanctions_callback) var get_player_sanction_count_opts = EOS.Sanctions.GetPlayerSanctionCountOptions.new() var sanctions_count = EOS.Sanctions.SanctionsInterface.get_player_sanction_count(get_player_sanction_count_opts) print("--- Sanctions: get_active_player_sanction_count: ", sanctions_count) for i in range(sanctions_count): var copy_active_sanction_by_index_opts = EOS.Sanctions.CopyPlayerSanctionByIndexOptions.new() copy_active_sanction_by_index_opts.sanction_index = i print("--- Sanctions: copy_active_player_sanction_by_index(%s): " % i, EOS.Sanctions.SanctionsInterface.copy_player_sanction_by_index(copy_active_sanction_by_index_opts)) func test_sessions_interface(): var bucket_id = "mode:region01:map01" var create_sess_mod_opts = EOS.Sessions.CreateSessionModificationOptions.new() create_sess_mod_opts.session_name = "TestSession001" create_sess_mod_opts.bucket_id = bucket_id create_sess_mod_opts.max_players = 16 create_sess_mod_opts.presence_enabled = true create_sess_mod_opts.sanctions_enabled = false var create_sess_mod = EOS.Sessions.SessionsInterface.create_session_modification(create_sess_mod_opts) print("--- Sessions: create_session_modification: ", create_sess_mod) var session_mod: EOSGSessionModification = create_sess_mod.session_modification print("--- Sessions: SessionModification: set_permission_level: ", EOS.result_str(session_mod.set_permission_level(EOS.Sessions.OnlineSessionPermissionLevel.PublicAdvertised))) print("--- Sessions: SessionModification: set_join_in_progess_allowed: ", EOS.result_str(session_mod.set_join_in_progress_allowed(true))) print("--- Sessions: SessionModification: set_invites_allowed: ", EOS.result_str(session_mod.set_invites_allowed(true))) print("--- Sessions: SessionModification: add_attribute: ", EOS.result_str(session_mod.add_attribute(EOS.Sessions.SEARCH_BUCKET_ID, bucket_id, EOS.Sessions.SessionAttributeAdvertisementType.Advertise))) var update_sess_opts = EOS.Sessions.UpdateSessionOptions.new() update_sess_opts.session_modification = session_mod EOS.Sessions.SessionsInterface.update_session(update_sess_opts) session_mod = null var update_sess = await IEOS.sessions_interface_update_session_callback print("--- Sessions: update_session: ", update_sess) var dump_sess_opts = EOS.Sessions.DumpSessionStateOptions.new() dump_sess_opts.session_name = "TestSession001" print("--- Sessions: dump_session_state: ", EOS.result_str(EOS.Sessions.SessionsInterface.dump_session_state(dump_sess_opts))) var copy_active_session_opts = EOS.Sessions.CopyActiveSessionDetailsOptions.new() copy_active_session_opts.session_name = "TestSession001" var copy_active_session = EOS.Sessions.SessionsInterface.copy_active_session_details(copy_active_session_opts) print("--- Sessions: copy_active_session_details: ", copy_active_session) var active_session: EOSGActiveSession = copy_active_session.active_session print("--- Sessions: ActiveSession: copy_info:", active_session.copy_info()) # Update session var update_sess_mod_opts = EOS.Sessions.UpdateSessionModificationOptions.new() update_sess_mod_opts.session_name = "TestSession001" var update_sess_mod = EOS.Sessions.SessionsInterface.update_session_modification(update_sess_mod_opts) print("--- Sessions: update_session_modification: ", update_sess_mod) session_mod = update_sess_mod.session_modification print("--- Sessions: SessionModification: set_invites_allowed: ", session_mod.set_invites_allowed(false)) update_sess_opts = EOS.Sessions.UpdateSessionOptions.new() update_sess_opts.session_modification = session_mod EOS.Sessions.SessionsInterface.update_session(update_sess_opts) update_sess = await IEOS.sessions_interface_update_session_callback print("--- Sessions: update_session: ", update_sess) # Search session var create_search_opts = EOS.Sessions.CreateSessionSearchOptions.new() var create_session_search = EOS.Sessions.SessionsInterface.create_session_search(create_search_opts) print("--- Sessions: create_session_search: ", create_session_search) var session_search: EOSGSessionSearch = create_session_search.session_search session_search.set_parameter(EOS.Sessions.SEARCH_BUCKET_ID, bucket_id, EOS.ComparisonOp.Equal) session_search.find(EOSGRuntime.local_product_user_id) print("--- Sessions: SessionSearch: find: ", await IEOS.session_search_find_callback) var search_result_count = session_search.get_search_result_count() print("--- Sessions: SessionSearch: get_search_result_count: ", search_result_count) for i in range(search_result_count): var search_result = session_search.copy_search_result_by_index(i) print("--- Sessions: copy_search_result_by_index(%d): " % i, search_result) var session_details: EOSGSessionDetails = search_result.session_details print("--- Sessions: SessionDetails: copy_info: ", session_details.copy_info()) # # Join session # var join_sess_opts = EOS.Sessions.JoinSessionOptions.new() # join_sess_opts.session_name = "TestSession001" # join_sess_opts.presence_enabled = true # join_sess_opts.session_details = EOSGSesssionDetails here # EOS.Sessions.SessionsInterface.join_session(join_sess_opts) # print("--- Sessions: join_session: ", await IEOS.sessions_interface_join_session_callback) # Register players var reg_players_opts = EOS.Sessions.RegisterPlayersOptions.new() reg_players_opts.session_name = "TestSession001" reg_players_opts.players_to_register = [EOSGRuntime.local_product_user_id] EOS.Sessions.SessionsInterface.register_players(reg_players_opts) print("--- Sessions: register_players: ", await IEOS.sessions_interface_register_players_callback) # Start session var start_sess_opts = EOS.Sessions.StartSessionOptions.new() start_sess_opts.session_name = "TestSession001" EOS.Sessions.SessionsInterface.start_session(start_sess_opts) print("--- Sessions: start_session: ", await IEOS.sessions_interface_start_session_callback) # # Destroy session # var destroy_sess_opts = EOS.Sessions.DestroySessionOptions.new() # destroy_sess_opts.session_name = "TestSession001" # EOS.Sessions.SessionsInterface.destroy_session(destroy_sess_opts) # print("--- Sessions: destroy_session: ", await IEOS.sessions_interface_destroy_session_callback) func test_rtc_interface(): #TODO: test rtc interface pass func test_anticheat_server_interface(): IEOS.anticheatserver_interface_message_to_client_callback.connect(func(data): print("--- AntiCheatServer: message_to_client_callback: ", data) ) IEOS.anticheatserver_interface_client_action_required_callback.connect(func(data): print("--- AntiCheatServer: client_action_required_callback: ", data) ) IEOS.anticheatserver_interface_client_auth_status_changed_callback.connect(func(data): print("--- AntiCheatServer: client_auth_status_changed_callback: ", data) ) var begin_sess_opts = EOS.AntiCheatServer.BeginSessionOptions.new() begin_sess_opts.register_timeout_seconds = 60 begin_sess_opts.server_name = "Godot 4.2+ server" begin_sess_opts.enable_gameplay_data = false print("--- AntiCheatServer: begin_session: ", EOS.AntiCheatServer.AntiCheatServerInterface.begin_session(begin_sess_opts)) #print("--- AntiCheatServer: end_session: ", EOS.AntiCheatServer.AntiCheatServerInterface.end_session()) var reg_client_opts = EOS.AntiCheatServer.RegisterClientOptions.new() reg_client_opts.client_handle = "client001" reg_client_opts.client_type = EOS.AntiCheatCommonClientType.ProtectedClient reg_client_opts.client_platform = EOS.AntiCheatCommonClientPlatform.Windows reg_client_opts.ip_address = "192.168.4.1" reg_client_opts.user_id = EOSGRuntime.local_product_user_id print("--- AntiCheatServer: register_client: ", EOS.AntiCheatServer.AntiCheatServerInterface.register_client(reg_client_opts)) func test_anticheat_client_interface(): var copy_id_token_opts = EOS.Connect.CopyIdTokenOptions.new() var id_token_ret = EOS.Connect.ConnectInterface.copy_id_token(copy_id_token_opts) var jwt = id_token_ret.id_token.json_web_token var product_user_id = id_token_ret.id_token.product_user_id IEOS.anticheat_client_interface_message_to_server_callback.connect(func(data): logger("--- AntiCheatClient: message_to_server_callback: " + str(data)) ) var begin_sess_opts = EOS.AntiCheatClient.BeginSessionOptions.new() begin_sess_opts.mode = EOS.AntiCheatClientMode.ClientServer begin_sess_opts.local_user_id = product_user_id logger("--- AntiCheatClient: begin_session: " + EOS.result_str(EOS.AntiCheatClient.AntiCheatClientInterface.begin_session(begin_sess_opts))) multiplayer.connected_to_server.connect(func(): logger("--- AntiCheatClient:: connected_to_server") _send_msg_to_server.rpc_id(1, product_user_id, jwt, EOS.AntiCheatCommonClientPlatform.Windows) ) multiplayer.server_disconnected.connect(func(): logger("--- AntiCheatClient:: server_disconnected") ) var peer = ENetMultiplayerPeer.new() peer.create_client("127.0.0.1", 12345) multiplayer.multiplayer_peer = peer @rpc("any_peer", "reliable", "call_remote") func _send_msg_to_server(local_user_id: String, jwt: String, mode: int): var peer_id = multiplayer.get_remote_sender_id() print("--- AntiCheatServer: _send_msg_to_server: got rpc: peer: %s, local_user_id: %d" % [peer_id, local_user_id]) if anticheat_server_main: anticheat_server_main.on_client_message_receive(peer_id, "register", { local_user_id = local_user_id, jwt = jwt, mode = mode }) func _get_res_if_sucess(res: Dictionary): if EOS.is_success(res): return res else: return EOS.result_str(res) func logger(msg: String): var logs = Store.get_view("Logs") logs.log_msg(EOS.Logging.LogLevel.Info, msg) ================================================ FILE: sample/test.gd.uid ================================================ uid://bb2nerxdrhped ================================================ FILE: sample/test_manual_audio_output.gd ================================================ # This script is an example of using EOS manual audio output and input. # In this sample, user2 will automatically create a lobby # and user1 will join that lobby. # Now once in the lobby: # user2's voice will be sent via EOS to user1. # And user2's voice will be heard by user1 # using manual audio output. # Audio output is muted for user2 to prevent loopback so # that you can test on the same device. # # In user2's window, hold T to speak. # The audio should be heard in user1's window. # # To run this sample, uncomment the _run() line in _ready() method extends Node # Provide 2 different Product User IDs for testing var user1_puid = "" var user2_puid = Store.second_product_user_id const BUCKET_ID = "test_manual_audio_output" var _lobby: HLobby var _connected_lobby_signals = false var _rtc_room_name = "" var player_voices = {} # for audio input var _audio_input: AudioStreamPlayer var _audio_effect_capture: AudioEffectCapture var _audio_bus_index: int const SAMPLE_RATE = 48000 const CHANNELS = 1 const CHUNK_DURATION_MS = 10 # 10 milliseconds const FRAMES_PER_CHUNK = int(SAMPLE_RATE * CHUNK_DURATION_MS / 1000.0) # 480 frames # Recording state var is_recording = false var audio_buffer = PackedFloat32Array() # Voice activity detection (optional) var voice_threshold = 0.01 # Adjust based on your needs var silence_frames = 0 var max_silence_frames = 10 # Stop recording after this many silent chunks func _ready(): # Uncomment the below line to run this sample #_run() pass func _run(): IEOS.rtc_audio_audio_before_render.connect(_on_rtc_audio_audio_before_render) await HAuth.logged_in if is_user2(): _setup_audio_capture() print("Running the EOS manual audio output sample") HLobbies.local_rtc_options.use_manual_audio_output = true if EOSGRuntime.local_product_user_id == user2_puid: _run_user2() else: _run_user1() func _run_user1(): _log("Running as user1") _log("waiting for lobby to be created") await get_tree().create_timer(5).timeout _log("searching for lobbies now") var lobbies: Array = await HLobbies.search_by_product_user_id_async(user2_puid) _log("found lobbies count: ", len(lobbies)) if len(lobbies) > 0: var most_recent: HLobby = lobbies.back() _lobby = await HLobbies.join_async(most_recent) _log("joined lobby with id ", most_recent.lobby_id) _on_lobby_joined() func _run_user2(): _log("Running as user2") var create_opts := EOS.Lobby.CreateLobbyOptions.new() create_opts.bucket_id = "test_manual_audio_output" create_opts.max_lobby_members = 4 create_opts.presence_enabled = true create_opts.enable_rtc_room = true create_opts.permission_level = EOS.Lobby.LobbyPermissionLevel.PublicAdvertised create_opts.allow_invites = true create_opts.local_rtc_options = { flags = EOS.RTC.JoinRoomFlags.EnableDataChannel, use_manual_audio_input = true, use_manual_audio_output = true, } _lobby = await HLobbies.create_lobby_async(create_opts) _log("created lobby with id ", _lobby.lobby_id) _on_lobby_joined() func _on_lobby_joined(): _lobby.lobby_updated.connect(_on_lobby_updated) func _on_lobby_updated(): if _lobby.rtc_room_name and not _connected_lobby_signals: _connected_lobby_signals = true _connect_rtc_signals() func _connect_rtc_signals(): _rtc_room_name = _lobby.rtc_room_name _log("Got rtc room name: ", _rtc_room_name) if not is_user2(): _log("setting up manual audio output for user1") var opts = EOS.RTCAudio.AddNotifyAudioBeforeRenderOptions.new() opts.unmixed_audio = true opts.room_name = _rtc_room_name EOS.RTCAudio.RTCAudioInterface.add_notify_audio_before_render(opts) # create the audio stream player node to hear user2's voice # AudioStreamGenerator for real-time audio streaming var stream_generator = AudioStreamGenerator.new() stream_generator.mix_rate = 48000 # 48000 is what EOS gives stream_generator.buffer_length = 0.01 # 10ms buffer is what EOS gives var audio_player = AudioStreamPlayer.new() add_child(audio_player) audio_player.stream = stream_generator audio_player.autoplay = true audio_player.play() player_voices[user2_puid] = { audio_player = audio_player, playback = audio_player.get_stream_playback() } func _setup_audio_capture(): _log("Setting up capturing microphone audio") # Get the microphone input bus _audio_bus_index = AudioServer.get_bus_index("Microphone") # Get the audio effect capture _audio_effect_capture = AudioServer.get_bus_effect(_audio_bus_index, 0) # Create AudioStreamPlayer for microphone input _audio_input = AudioStreamPlayer.new() add_child(_audio_input) # Set up microphone input var microphone_stream = AudioStreamMicrophone.new() _audio_input.stream = microphone_stream _audio_input.bus = "Microphone" func _on_rtc_audio_audio_before_render(data: Dictionary): var puid = data.participant_id if puid in player_voices: #_log("got audio before render: for player: ", puid) var buffer_data = data.buffer var playback = player_voices[puid].playback # Convert the frame data to audio samples var frames = buffer_data["frames"] @warning_ignore("unused_variable") var sample_rate = buffer_data["sample_rate"] @warning_ignore("unused_variable") var channels = buffer_data["channels"] # Create PackedVector2Array for audio data (required by AudioStreamGeneratorPlayback) var audio_data = PackedVector2Array() # Convert integer frames to float samples (normalize to -1.0 to 1.0 range) # Assuming the frames are 16-bit integers (-32768 to 32767) for frame in frames: var sample = float(frame) / 32768.0 # For mono audio, use the same sample for both left and right channels audio_data.append(Vector2(sample, sample)) # Push the audio data to the stream playback.push_buffer(audio_data) func _process(_delta: float): if not is_user2(): return if not is_recording: return # Get available audio frames var available_frames = _audio_effect_capture.get_frames_available() if available_frames >= FRAMES_PER_CHUNK: # Capture audio chunk var audio_data = _audio_effect_capture.get_buffer(FRAMES_PER_CHUNK) process_audio_chunk(audio_data) func start_voice_recording(): if is_recording or not _audio_input: return is_recording = true audio_buffer.clear() # Start microphone recording _audio_input.play() # Enable audio capture _audio_effect_capture.clear_buffer() _log("Voice recording started") func stop_voice_recording(): if not is_recording or not _audio_input: return is_recording = false _audio_input.stop() _log("Voice recording stopped") func process_audio_chunk(audio_data: PackedVector2Array): # Convert stereo Vector2 data to mono float array var mono_samples = PackedFloat32Array() for sample in audio_data: # Convert stereo to mono by averaging left and right channels var mono_sample = (sample.x + sample.y) / 2.0 mono_samples.append(mono_sample) # Convert to 16-bit integer format var frames_16bit = convert_to_16bit(mono_samples) # Broadcast the voice data var send_opts = EOS.RTCAudio.SendAudioOptions.new() send_opts.room_name = _rtc_room_name send_opts.frames = PackedInt32Array(frames_16bit) var send_res = EOS.RTCAudio.RTCAudioInterface.send_audio(send_opts) if not EOS.is_success(send_res): _log("Failed to send audio data code=", EOS.result_str(send_res)) return else: _log("Send audio success") func convert_to_16bit(float_samples: PackedFloat32Array) -> Array: var int_samples = [] for sample in float_samples: # Clamp sample to [-1.0, 1.0] range sample = clamp(sample, -1.0, 1.0) # Convert to 16-bit integer (-32768 to 32767) var int_sample = int(sample * 32767.0) int_samples.append(int_sample) return int_samples func has_voice_activity(samples: PackedFloat32Array) -> bool: # Simple voice activity detection based on RMS (Root Mean Square) var rms = 0.0 for sample in samples: rms += sample * sample rms = sqrt(rms / samples.size()) return rms > voice_threshold func _input(_event: InputEvent) -> void: if Input.is_action_just_pressed(&"push_to_talk"): start_voice_recording() if Input.is_action_just_released(&"push_to_talk"): stop_voice_recording() #region internals func _log_only_user1(v0, v1 = null): _log(v0, v1, user1_puid) func _log_only_user2(v0, v1 = null): _log(v0, v1, user2_puid) func is_user2() -> bool: return HAuth.product_user_id == user2_puid func _log(v0, v1 = null, only_user_with_puid = null): var user_num = "2" if is_user2() else "1" if only_user_with_puid and only_user_with_puid != HAuth.product_user_id: return if v1 == null: print("user:: " + str(user_num) + ":: ", v0) else: print("user:: " + str(user_num) + ":: ", v0, v1) #endregion ================================================ FILE: sample/test_manual_audio_output.gd.uid ================================================ uid://k3lb65sounwm ================================================ FILE: src/achievements_interface.cpp ================================================ #include "ieos.h" using namespace godot; Dictionary IEOS::achievements_interface_copy_achievement_definition_v2_by_achievement_id(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, {}); CharString achievement_id = VARIANT_TO_CHARSTRING(p_options->get("achievement_id")); EOS_Achievements_CopyAchievementDefinitionV2ByAchievementIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_COPYACHIEVEMENTDEFINITIONV2BYACHIEVEMENTID_API_LATEST; options.AchievementId = achievement_id.get_data(); EOS_Achievements_DefinitionV2 *outDefinition = nullptr; EOS_EResult res = EOS_Achievements_CopyAchievementDefinitionV2ByAchievementId(s_achievementsInterface, &options, &outDefinition); Dictionary ret; ret["result_code"] = static_cast(res); ret["definition_v2"] = eosg_achievements_definition_to_dict_and_release(outDefinition); return ret; } Dictionary IEOS::achievements_interface_copy_achievement_definition_v2_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, {}); EOS_Achievements_CopyAchievementDefinitionV2ByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_COPYACHIEVEMENTDEFINITIONV2BYINDEX_API_LATEST; options.AchievementIndex = static_cast(static_cast(p_options->get("achievement_index"))); EOS_Achievements_DefinitionV2 *outDefinition = nullptr; EOS_EResult res = EOS_Achievements_CopyAchievementDefinitionV2ByIndex(s_achievementsInterface, &options, &outDefinition); Dictionary ret; ret["result_code"] = static_cast(res); ret["definition_v2"] = eosg_achievements_definition_to_dict_and_release(outDefinition); return ret; } Dictionary IEOS::achievements_interface_copy_player_achievement_by_achievement_id(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString achievement_id = VARIANT_TO_CHARSTRING(p_options->get("achievement_id")); EOS_Achievements_CopyPlayerAchievementByAchievementIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_COPYPLAYERACHIEVEMENTBYACHIEVEMENTID_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.AchievementId = achievement_id.get_data(); EOS_Achievements_PlayerAchievement *outAchievement = nullptr; EOS_EResult res = EOS_Achievements_CopyPlayerAchievementByAchievementId(s_achievementsInterface, &options, &outAchievement); Dictionary ret; ret["result_code"] = static_cast(res); ret["player_achievement"] = eosg_achievements_player_achievement_to_dict_and_release(outAchievement); return ret; } Dictionary IEOS::achievements_interface_copy_player_achievement_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Achievements_CopyPlayerAchievementByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_COPYPLAYERACHIEVEMENTBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.AchievementIndex = static_cast(static_cast(p_options->get("achievement_index"))); EOS_Achievements_PlayerAchievement *outAchievement = nullptr; EOS_EResult res = EOS_Achievements_CopyPlayerAchievementByIndex(s_achievementsInterface, &options, &outAchievement); Dictionary ret; ret["result_code"] = static_cast(res); ret["player_achievement"] = eosg_achievements_player_achievement_to_dict_and_release(outAchievement); return ret; } int IEOS::achievements_interface_get_achievement_definition_count(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, 0); EOS_Achievements_GetAchievementDefinitionCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_GETACHIEVEMENTDEFINITIONCOUNT_API_LATEST; return static_cast(EOS_Achievements_GetAchievementDefinitionCount(s_achievementsInterface, &options)); } void IEOS::achievements_interface_query_definitions(Ref p_options) { ERR_FAIL_NULL(s_achievementsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Achievements_QueryDefinitionsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_QUERYDEFINITIONS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_Achievements_QueryDefinitions(s_achievementsInterface, &options, (void *)*p_options, [](const EOS_Achievements_OnQueryDefinitionsCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("achievements_interface_query_definitions_callback", ret); }); } int IEOS::achievements_interface_get_player_achievement_count(Ref p_options) { ERR_FAIL_NULL_V(s_achievementsInterface, 0); CharString user_id = VARIANT_TO_CHARSTRING(p_options->get("user_id")); EOS_Achievements_GetPlayerAchievementCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_GETPLAYERACHIEVEMENTCOUNT_API_LATEST; options.UserId = eosg_string_to_product_user_id(user_id.get_data()); return static_cast(EOS_Achievements_GetPlayerAchievementCount(s_achievementsInterface, &options)); } void IEOS::achievements_interface_query_player_achievements(Ref p_options) { ERR_FAIL_NULL(s_achievementsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Achievements_QueryPlayerAchievementsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_QUERYPLAYERACHIEVEMENTS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); p_options->reference(); EOS_Achievements_QueryPlayerAchievements(s_achievementsInterface, &options, (void *)*p_options, [](const EOS_Achievements_OnQueryPlayerAchievementsCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("achievements_interface_query_player_achievements_callback", ret); }); } void IEOS::achievements_interface_unlock_achievements(Ref p_options) { ERR_FAIL_NULL(s_achievementsInterface); CharString user_id = VARIANT_TO_CHARSTRING(p_options->get("user_id")); Array p_achievement_ids = p_options->get("achievement_ids"); int achievement_ids_count = p_achievement_ids.size(); PERSISTENT_CHAR_ARRAY_CREATE(achievement_ids, achievement_id_charstrings, achievement_ids_count) for (int i = 0; i < achievement_ids_count; i++) { PERSISTENT_CHAR_ARRAY_SET(achievement_ids, achievement_id_charstrings, i, p_achievement_ids[i]); } EOS_Achievements_UnlockAchievementsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACHIEVEMENTS_UNLOCKACHIEVEMENTS_API_LATEST; options.UserId = eosg_string_to_product_user_id(user_id.get_data()); options.AchievementIds = achievement_ids; options.AchievementsCount = achievement_ids_count; p_options->reference(); EOS_Achievements_UnlockAchievements(s_achievementsInterface, &options, (void *)*p_options, [](const EOS_Achievements_OnUnlockAchievementsCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["user_id"] = eosg_product_user_id_to_string(data->UserId); ret["achievements_count"] = static_cast(data->AchievementsCount); IEOS::get_singleton()->emit_signal("achievements_interface_unlock_achievements_callback", ret); }); } ================================================ FILE: src/anticheat_client_interface.cpp ================================================ #include "ieos.h" using namespace godot; int IEOS::anticheat_client_interface_begin_session(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int p_mode = p_options->get("mode"); EOS_AntiCheatClient_BeginSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_BEGINSESSION_API_LATEST; options.Mode = static_cast(p_mode); options.LocalUserId = eosg_string_to_product_user_id(p_local_user_id); return static_cast(EOS_AntiCheatClient_BeginSession(s_antiCheatClientInterface, &options)); }; int IEOS::anticheat_client_interface_end_session(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_AntiCheatClient_EndSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_ENDSESSION_API_LATEST; return static_cast(EOS_AntiCheatClient_EndSession(s_antiCheatClientInterface, &options)); } int IEOS::anticheat_client_interface_add_external_integrity_catalog(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_path_to_bin_file = VARIANT_TO_CHARSTRING(p_options->get("path_to_bin_file")); EOS_AntiCheatClient_AddExternalIntegrityCatalogOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_ADDEXTERNALINTEGRITYCATALOG_API_LATEST; options.PathToBinFile = ProjectSettings::get_singleton()->globalize_path(p_path_to_bin_file.get_data()).utf8(); return static_cast(EOS_AntiCheatClient_AddExternalIntegrityCatalog(s_antiCheatClientInterface, &options)); } int IEOS::anticheat_client_interface_receive_message_from_server(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); PackedByteArray p_data = p_options->get("data"); EOS_AntiCheatClient_ReceiveMessageFromServerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_RECEIVEMESSAGEFROMSERVER_API_LATEST; options.DataLengthBytes = static_cast(p_data.size()); options.Data = p_data.ptr(); return static_cast(EOS_AntiCheatClient_ReceiveMessageFromServer(s_antiCheatClientInterface, &options)); } Dictionary IEOS::anticheat_client_interface_get_protect_message_output_length(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, {}); int p_data_length_bytes = p_options->get("data_length_bytes"); EOS_AntiCheatClient_GetProtectMessageOutputLengthOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_GETPROTECTMESSAGEOUTPUTLENGTH_API_LATEST; options.DataLengthBytes = static_cast(p_data_length_bytes); uint32_t outBufferSizeBytes = 0; EOS_EResult res = EOS_AntiCheatClient_GetProtectMessageOutputLength(s_antiCheatClientInterface, &options, &outBufferSizeBytes); Dictionary ret; ret["result_code"] = static_cast(res); ret["buffer_size_bytes"] = 0; if (res == EOS_EResult::EOS_Success) { ret["buffer_size_bytes"] = outBufferSizeBytes; } return ret; } Dictionary IEOS::anticheat_client_interface_protect_message(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, {}); PackedByteArray p_data = p_options->get("data"); int data_length_bytes = p_data.size(); int out_buffer_size_bytes = p_options->get("out_buffer_size_bytes"); EOS_AntiCheatClient_ProtectMessageOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_PROTECTMESSAGE_API_LATEST; options.DataLengthBytes = static_cast(data_length_bytes); options.Data = p_data.ptr(); options.OutBufferSizeBytes = static_cast(out_buffer_size_bytes); uint32_t outBytesWritten = 0; void *outBuffer = memalloc(out_buffer_size_bytes); EOS_EResult res = EOS_AntiCheatClient_ProtectMessage(s_antiCheatClientInterface, &options, outBuffer, &outBytesWritten); Dictionary ret; ret["result_code"] = static_cast(res); ret["out_buffer"] = Variant(); ret["out_bytes_written"] = 0; if (res == EOS_EResult::EOS_Success) { PackedByteArray buffer; buffer.resize(outBytesWritten); memcpy(buffer.ptrw(), outBuffer, outBytesWritten); ret["out_buffer"] = buffer; ret["out_bytes_written"] = outBytesWritten; } memfree(outBuffer); return ret; } Dictionary IEOS::anticheat_client_interface_unprotect_message(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, {}); PackedByteArray p_data = p_options->get("data"); int data_length_bytes = p_data.size(); int out_buffer_size_bytes = p_options->get("out_buffer_size_bytes"); EOS_AntiCheatClient_UnprotectMessageOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_UNPROTECTMESSAGE_API_LATEST; options.DataLengthBytes = static_cast(data_length_bytes); options.Data = p_data.ptr(); options.OutBufferSizeBytes = static_cast(out_buffer_size_bytes); uint32_t outBytesWritten = 0; void *outBuffer = memalloc(out_buffer_size_bytes); EOS_EResult res = EOS_AntiCheatClient_UnprotectMessage(s_antiCheatClientInterface, &options, outBuffer, &outBytesWritten); Dictionary ret; ret["result_code"] = static_cast(res); ret["out_buffer"] = Variant(); ret["out_bytes_written"] = 0; if (res == EOS_EResult::EOS_Success) { PackedByteArray buffer; buffer.resize(outBytesWritten); memcpy(buffer.ptrw(), outBuffer, outBytesWritten); ret["out_buffer"] = buffer; ret["out_bytes_written"] = outBytesWritten; } memfree(outBuffer); return ret; } int IEOS::anticheat_client_interface_register_peer(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_peer_handle = VARIANT_TO_CHARSTRING(p_options->get("peer_handle")); int p_client_type = p_options->get("client_type"); int p_client_platform = p_options->get("client_platform"); int p_authentication_timeout = p_options->get("authentication_timeout"); CharString p_ip_address = VARIANT_TO_CHARSTRING(p_options->get("ip_address")); CharString p_peer_product_user_id = VARIANT_TO_CHARSTRING(p_options->get("peer_product_user_id")); EOS_AntiCheatClient_RegisterPeerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_REGISTERPEER_API_LATEST; options.PeerHandle = _anticheat_player_id_to_handle(p_peer_handle); options.ClientType = static_cast(p_client_type); options.ClientPlatform = static_cast(p_client_platform); options.AuthenticationTimeout = static_cast(p_authentication_timeout); if (p_ip_address.size() > 1) { options.IpAddress = p_ip_address.get_data(); } options.PeerProductUserId = eosg_string_to_product_user_id(p_peer_product_user_id); return static_cast(EOS_AntiCheatClient_RegisterPeer(s_antiCheatClientInterface, &options)); } int IEOS::anticheat_client_interface_unregister_peer(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_peer_handle = VARIANT_TO_CHARSTRING(p_options->get("peer_handle")); EOS_AntiCheatClient_UnregisterPeerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_UNREGISTERPEER_API_LATEST; options.PeerHandle = _anticheat_player_id_to_handle(p_peer_handle); return static_cast(EOS_AntiCheatClient_UnregisterPeer(s_antiCheatClientInterface, &options)); } int IEOS::anticheat_client_interface_receive_message_from_peer(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatClientInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_peer_handle = VARIANT_TO_CHARSTRING(p_options->get("peer_handle")); PackedByteArray p_data = p_options->get("data"); EOS_AntiCheatClient_ReceiveMessageFromPeerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCLIENT_RECEIVEMESSAGEFROMPEER_API_LATEST; options.DataLengthBytes = static_cast(p_data.size()); options.Data = p_data.ptr(); options.PeerHandle = _anticheat_player_id_to_handle(p_peer_handle); return static_cast(EOS_AntiCheatClient_ReceiveMessageFromPeer(s_antiCheatClientInterface, &options)); } ================================================ FILE: src/anticheat_server_interface.cpp ================================================ #include "ieos.h" using namespace godot; int IEOS::anticheat_server_interface_begin_session(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); int p_register_timeout_seconds = p_options->get("register_timeout_seconds"); CharString p_server_name = VARIANT_TO_CHARSTRING(p_options->get("server_name")); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_AntiCheatServer_BeginSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_BEGINSESSION_API_LATEST; options.RegisterTimeoutSeconds = static_cast(p_register_timeout_seconds); options.ServerName = p_server_name.get_data(); options.bEnableGameplayData = VARIANT_TO_EOS_BOOL(p_options->get("enable_gameplay_data")); if (p_local_user_id.size() > 1) { options.LocalUserId = eosg_string_to_product_user_id(p_local_user_id); } return static_cast(EOS_AntiCheatServer_BeginSession(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_end_session(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_AntiCheatServer_EndSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_ENDSESSION_API_LATEST; return static_cast(EOS_AntiCheatServer_EndSession(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_register_client(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); int p_client_type = p_options->get("client_type"); int p_client_platform = p_options->get("client_platform"); CharString p_ip_address = VARIANT_TO_CHARSTRING(p_options->get("ip_address")); CharString p_user_id = VARIANT_TO_CHARSTRING(p_options->get("user_id")); EOS_AntiCheatServer_RegisterClientOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_REGISTERCLIENT_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.ClientType = static_cast(p_client_type); options.ClientPlatform = static_cast(p_client_platform); options.IpAddress = p_ip_address.get_data(); options.UserId = eosg_string_to_product_user_id(p_user_id); return static_cast(EOS_AntiCheatServer_RegisterClient(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_unregister_client(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); EOS_AntiCheatServer_UnregisterClientOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_UNREGISTERCLIENT_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); return static_cast(EOS_AntiCheatServer_UnregisterClient(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_receive_message_from_client(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); PackedByteArray p_data = p_options->get("data"); void *data = memalloc(p_data.size()); std::memcpy(data, p_data.ptr(), p_data.size()); EOS_AntiCheatServer_ReceiveMessageFromClientOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_RECEIVEMESSAGEFROMCLIENT_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.DataLengthBytes = static_cast(p_data.size()); options.Data = data; return static_cast(EOS_AntiCheatServer_ReceiveMessageFromClient(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_set_client_details(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); int p_client_flags = p_options->get("client_flags"); int p_client_input = p_options->get("client_input"); EOS_AntiCheatCommon_SetClientDetailsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_SETCLIENTDETAILS_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.ClientFlags = static_cast(p_client_flags); options.ClientInputMethod = static_cast(p_client_input); return static_cast(EOS_AntiCheatServer_SetClientDetails(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_set_game_session_id(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_game_session_id = VARIANT_TO_CHARSTRING(p_options->get("game_session_id")); EOS_AntiCheatCommon_SetGameSessionIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_SETGAMESESSIONID_API_LATEST; options.GameSessionId = p_game_session_id.get_data(); return static_cast(EOS_AntiCheatServer_SetGameSessionId(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_set_client_network_state(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); EOS_AntiCheatServer_SetClientNetworkStateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_SETCLIENTNETWORKSTATE_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.bIsNetworkActive = VARIANT_TO_EOS_BOOL(p_options->get("is_network_active")); return static_cast(EOS_AntiCheatServer_SetClientNetworkState(s_antiCheatServerInterface, &options)); } Dictionary IEOS::anticheat_server_interface_get_protect_message_output_length(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, {}); int p_data_length_bytes = p_options->get("data_length_bytes"); EOS_AntiCheatServer_GetProtectMessageOutputLengthOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_GETPROTECTMESSAGEOUTPUTLENGTH_API_LATEST; options.DataLengthBytes = static_cast(p_data_length_bytes); uint32_t outBufferSizeBytes = 0; EOS_EResult res = EOS_AntiCheatServer_GetProtectMessageOutputLength(s_antiCheatServerInterface, &options, &outBufferSizeBytes); Dictionary ret; ret["result_code"] = static_cast(res); ret["buffer_size_bytes"] = 0; if (res == EOS_EResult::EOS_Success) { ret["buffer_size_bytes"] = outBufferSizeBytes; } return ret; } Dictionary IEOS::anticheat_server_interface_protect_message(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, {}); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); PackedByteArray p_data = p_options->get("data"); int data_length_bytes = p_data.size(); void *data = memalloc(data_length_bytes); std::memcpy(data, p_data.ptr(), data_length_bytes); int out_buffer_size_bytes = p_options->get("out_buffer_size_bytes"); EOS_AntiCheatServer_ProtectMessageOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_PROTECTMESSAGE_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.DataLengthBytes = static_cast(data_length_bytes); options.Data = data; options.OutBufferSizeBytes = static_cast(out_buffer_size_bytes); uint32_t outBytesWritten = 0; void *outBuffer = (void *)memalloc(out_buffer_size_bytes + 1); EOS_EResult res = EOS_AntiCheatServer_ProtectMessage(s_antiCheatServerInterface, &options, &outBuffer, &outBytesWritten); Dictionary ret; ret["result_code"] = static_cast(res); ret["out_buffer"] = Variant(); ret["out_bytes_written"] = 0; if (res == EOS_EResult::EOS_Success) { ret["out_buffer"] = outBuffer; ret["out_bytes_written"] = outBytesWritten; } return ret; } Dictionary IEOS::anticheat_server_interface_unprotect_message(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, {}); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); PackedByteArray p_data = p_options->get("data"); int data_length_bytes = p_data.size(); void *data = memalloc(data_length_bytes); std::memcpy(data, p_data.ptr(), data_length_bytes); int out_buffer_size_bytes = p_options->get("out_buffer_size_bytes"); EOS_AntiCheatServer_UnprotectMessageOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATSERVER_UNPROTECTMESSAGE_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.DataLengthBytes = static_cast(data_length_bytes); options.Data = data; options.OutBufferSizeBytes = static_cast(out_buffer_size_bytes); uint32_t outBytesWritten = 0; void *outBuffer = (void *)memalloc(out_buffer_size_bytes + 1); EOS_EResult res = EOS_AntiCheatServer_UnprotectMessage(s_antiCheatServerInterface, &options, &outBuffer, &outBytesWritten); Dictionary ret; ret["result_code"] = static_cast(res); ret["out_buffer"] = Variant(); ret["out_bytes_written"] = 0; if (res == EOS_EResult::EOS_Success) { ret["out_buffer"] = outBuffer; ret["out_bytes_written"] = outBytesWritten; } return ret; } int IEOS::anticheat_server_interface_register_event(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); int p_event_id = p_options->get("event_id"); CharString p_event_name = VARIANT_TO_CHARSTRING(p_options->get("event_name")); int p_event_type = p_options->get("event_type"); Array p_param_defs = p_options->get("param_defs"); PERSISTENT_CHAR_ARRAY_CREATE(param_names, param_names_charstrings, p_param_defs.size()); EOS_AntiCheatCommon_RegisterEventParamDef *paramDefs = (EOS_AntiCheatCommon_RegisterEventParamDef *)memalloc(sizeof(EOS_AntiCheatCommon_RegisterEventParamDef) * p_param_defs.size()); for (int i = 0; i < p_param_defs.size(); i++) { Dictionary paramDef = p_param_defs[i]; int param_type = paramDef["param_type"]; PERSISTENT_CHAR_ARRAY_SET(param_names, param_names_charstrings, i, paramDef["param_name"]); paramDefs[i].ParamType = static_cast(param_type); paramDefs[i].ParamName = param_names[i]; } EOS_AntiCheatCommon_RegisterEventOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_REGISTEREVENT_API_LATEST; options.EventId = static_cast(p_event_id); options.EventName = p_event_name.get_data(); options.EventType = static_cast(p_event_type); options.ParamDefsCount = p_param_defs.size(); options.ParamDefs = paramDefs; return static_cast(EOS_AntiCheatServer_RegisterEvent(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_event(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_client_handle = VARIANT_TO_CHARSTRING(p_options->get("client_handle")); int p_event_id = p_options->get("event_id"); Array p_params = p_options->get("params"); int params_count = p_params.size(); EOS_AntiCheatCommon_LogEventParamPair *params = (EOS_AntiCheatCommon_LogEventParamPair *)memalloc(sizeof(EOS_AntiCheatCommon_LogEventParamPair) * params_count); for (int i = 0; i < params_count; i++) { Variant param = p_params[i]; if (param.get_type() == Variant::INT) { params[i].ParamValueType = EOS_EAntiCheatCommonEventParamType::EOS_ACCEPT_Int64; params[i].ParamValue.Int64 = param; } else if (param.get_type() == Variant::STRING) { params[i].ParamValueType = EOS_EAntiCheatCommonEventParamType::EOS_ACCEPT_String; params[i].ParamValue.String = VARIANT_TO_CHARSTRING(param).get_data(); break; } else if (param.get_type() == Variant::VECTOR3) { params[i].ParamValueType = EOS_EAntiCheatCommonEventParamType::EOS_ACCEPT_Vector3f; Vector3 vec = param; params[i].ParamValue.Vec3f.x = vec.x; params[i].ParamValue.Vec3f.y = vec.y; params[i].ParamValue.Vec3f.z = vec.z; break; } else if (param.get_type() == Variant::FLOAT) { params[i].ParamValueType = EOS_EAntiCheatCommonEventParamType::EOS_ACCEPT_Float; params[i].ParamValue.Float = param; break; } else if (param.get_type() == Variant::QUATERNION) { Quaternion quat = param; params[i].ParamValueType = EOS_EAntiCheatCommonEventParamType::EOS_ACCEPT_Quat; params[i].ParamValue.Quat.x = quat.x; params[i].ParamValue.Quat.y = quat.y; params[i].ParamValue.Quat.z = quat.z; params[i].ParamValue.Quat.w = quat.w; break; } else { ERR_FAIL_V_MSG(static_cast(EOS_EResult::EOS_InvalidParameters), "Invalid param type provided."); } } EOS_AntiCheatCommon_LogEventOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGEVENT_API_LATEST; options.ClientHandle = _anticheat_player_id_to_handle(p_client_handle); options.EventId = static_cast(p_event_id); options.ParamsCount = params_count; options.Params = params; return static_cast(EOS_AntiCheatServer_LogEvent(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_game_round_start(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_session_identifier = VARIANT_TO_CHARSTRING(p_options->get("session_identifier")); CharString p_level_name = VARIANT_TO_CHARSTRING(p_options->get("level_name")); CharString p_mode_name = VARIANT_TO_CHARSTRING(p_options->get("mode_name")); int p_round_time_seconds = p_options->get("round_time_seconds"); int p_competition_type = p_options->get("competition_type"); EOS_AntiCheatCommon_LogGameRoundStartOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGGAMEROUNDSTART_API_LATEST; if (p_session_identifier.size() > 1) { options.SessionIdentifier = p_session_identifier.get_data(); } if (p_level_name.size() > 1) { options.LevelName = p_level_name.get_data(); } if (p_mode_name.size() > 1) { options.ModeName = p_mode_name.get_data(); } options.RoundTimeSeconds = static_cast(p_round_time_seconds); options.CompetitionType = static_cast(p_competition_type); return static_cast(EOS_AntiCheatServer_LogGameRoundStart(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_game_round_end(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); int p_winning_team_id = p_options->get("winning_team_id"); EOS_AntiCheatCommon_LogGameRoundEndOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGGAMEROUNDEND_API_LATEST; options.WinningTeamId = static_cast(p_winning_team_id); return static_cast(EOS_AntiCheatServer_LogGameRoundEnd(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_spawn(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_spawned_player_handle = VARIANT_TO_CHARSTRING(p_options->get("spawned_player_handle")); int p_team_id = p_options->get("team_id"); int p_character_id = p_options->get("character_id"); EOS_AntiCheatCommon_LogPlayerSpawnOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERSPAWN_API_LATEST; options.SpawnedPlayerHandle = _anticheat_player_id_to_handle(p_spawned_player_handle); options.TeamId = static_cast(p_team_id); options.CharacterId = static_cast(p_character_id); return static_cast(EOS_AntiCheatServer_LogPlayerSpawn(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_despawn(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_despawned_player_handle = VARIANT_TO_CHARSTRING(p_options->get("despawned_player_handle")); EOS_AntiCheatCommon_LogPlayerDespawnOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERDESPAWN_API_LATEST; options.DespawnedPlayerHandle = _anticheat_player_id_to_handle(p_despawned_player_handle); return static_cast(EOS_AntiCheatServer_LogPlayerDespawn(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_revive(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_revived_player_handle = VARIANT_TO_CHARSTRING(p_options->get("revived_player_handle")); CharString p_reviver_player_handle = VARIANT_TO_CHARSTRING(p_options->get("reviver_player_handle")); EOS_AntiCheatCommon_LogPlayerReviveOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERREVIVE_API_LATEST; options.RevivedPlayerHandle = _anticheat_player_id_to_handle(p_revived_player_handle); options.ReviverPlayerHandle = _anticheat_player_id_to_handle(p_reviver_player_handle); return static_cast(EOS_AntiCheatServer_LogPlayerRevive(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_tick(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_player_handle = VARIANT_TO_CHARSTRING(p_options->get("player_handle")); Vector3 p_player_position = p_options->get("player_position"); Quaternion p_player_view_rotation = p_options->get("player_view_rotation"); float p_player_health = p_options->get("player_health"); int p_player_movement_state = p_options->get("player_movement_state"); Vector3 p_player_view_position = p_options->get("player_view_position"); EOS_AntiCheatCommon_Vec3f player_position; player_position.x = p_player_position.x; player_position.y = p_player_position.y; player_position.z = p_player_position.z; EOS_AntiCheatCommon_Quat player_view_rotation; player_view_rotation.x = p_player_view_rotation.x; player_view_rotation.y = p_player_view_rotation.y; player_view_rotation.z = p_player_view_rotation.z; player_view_rotation.w = p_player_view_rotation.w; EOS_AntiCheatCommon_Vec3f player_view_position; player_view_position.x = p_player_view_position.x; player_view_position.y = p_player_view_position.y; player_view_position.z = p_player_view_position.z; EOS_AntiCheatCommon_LogPlayerTickOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERTICK_API_LATEST; options.PlayerHandle = _anticheat_player_id_to_handle(p_player_handle); options.PlayerPosition = &player_position; options.PlayerViewRotation = &player_view_rotation; options.bIsPlayerViewZoomed = VARIANT_TO_EOS_BOOL(p_options->get("is_player_view_zoomed")); options.PlayerHealth = p_player_health; options.PlayerMovementState = static_cast(p_player_movement_state); options.PlayerViewPosition = &player_view_position; return static_cast(EOS_AntiCheatServer_LogPlayerTick(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_use_weapon(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); Variant p_player_handle = p_options->get("player_handle"); Vector3 p_player_position = p_options->get("player_position"); Quaternion p_player_view_rotation = p_options->get("player_view_rotation"); CharString p_weapon_name = VARIANT_TO_CHARSTRING(p_options->get("weapon_name")); EOS_AntiCheatCommon_Vec3f player_position; player_position.x = p_player_position.x; player_position.y = p_player_position.y; player_position.z = p_player_position.z; EOS_AntiCheatCommon_Quat player_view_rotation; player_view_rotation.x = p_player_view_rotation.x; player_view_rotation.y = p_player_view_rotation.y; player_view_rotation.z = p_player_view_rotation.z; player_view_rotation.w = p_player_view_rotation.w; EOS_AntiCheatCommon_LogPlayerUseWeaponData use_weapon_data; memset(&use_weapon_data, 0, sizeof(use_weapon_data)); use_weapon_data.PlayerHandle = (void *)p_player_handle; use_weapon_data.PlayerPosition = &player_position; use_weapon_data.PlayerViewRotation = &player_view_rotation; use_weapon_data.bIsPlayerViewZoomed = VARIANT_TO_EOS_BOOL(p_options->get("is_player_view_zoomed")); use_weapon_data.bIsMeleeAttack = VARIANT_TO_EOS_BOOL(p_options->get("is_melee_attack")); use_weapon_data.WeaponName = p_weapon_name.get_data(); EOS_AntiCheatCommon_LogPlayerUseWeaponOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERUSEWEAPON_API_LATEST; options.UseWeaponData = &use_weapon_data; return static_cast(EOS_AntiCheatServer_LogPlayerUseWeapon(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_use_ability(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_player_handle = VARIANT_TO_CHARSTRING(p_options->get("player_handle")); int p_ability_id = p_options->get("ability_id"); int p_ability_duration_ms = p_options->get("ability_duration_ms"); int p_ability_cooldown_ms = p_options->get("ability_cooldown_ms"); EOS_AntiCheatCommon_LogPlayerUseAbilityOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERUSEABILITY_API_LATEST; options.PlayerHandle = _anticheat_player_id_to_handle(p_player_handle); options.AbilityId = static_cast(p_ability_id); options.AbilityDurationMs = static_cast(p_ability_duration_ms); options.AbilityCooldownMs = static_cast(p_ability_cooldown_ms); return static_cast(EOS_AntiCheatServer_LogPlayerUseAbility(s_antiCheatServerInterface, &options)); } int IEOS::anticheat_server_interface_log_player_take_damage(Ref p_options) { ERR_FAIL_NULL_V(s_antiCheatServerInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString p_victim_player_handle = VARIANT_TO_CHARSTRING(p_options->get("victim_player_handle")); Vector3 p_victim_player_position = p_options->get("victim_player_position"); Quaternion p_victim_player_view_rotation = p_options->get("victim_player_view_rotation"); CharString p_attacker_player_handle = VARIANT_TO_CHARSTRING(p_options->get("attacker_player_handle")); Vector3 p_attacker_player_position = p_options->get("attacker_player_position"); Quaternion p_attacker_player_view_rotation = p_options->get("attacker_player_view_rotation"); float p_damage_taken = p_options->get("damage_taken"); float p_health_remaining = p_options->get("health_remaining"); int p_damage_source = p_options->get("damage_source"); int p_damage_type = p_options->get("damage_type"); int p_damage_result = p_options->get("damage_result"); int p_time_since_player_use_weapon_ms = p_options->get("time_since_player_use_weapon_ms"); Vector3 p_damage_position = p_options->get("damage_position"); Vector3 p_attacker_player_view_position = p_options->get("attacker_player_view_position"); Ref use_weapon_data_options = p_options->get("player_use_weapon_data"); Variant uw_player_handle = use_weapon_data_options->get("player_handle"); Vector3 uw_player_position = use_weapon_data_options->get("player_position"); Quaternion uw_player_view_rotation = use_weapon_data_options->get("player_view_rotation"); CharString uw_weapon_name = VARIANT_TO_CHARSTRING(use_weapon_data_options->get("weapon_name")); EOS_AntiCheatCommon_Vec3f uw_v_player_position; uw_v_player_position.x = uw_player_position.x; uw_v_player_position.y = uw_player_position.y; uw_v_player_position.z = uw_player_position.z; EOS_AntiCheatCommon_Quat uw_q_player_view_rotation; uw_q_player_view_rotation.x = uw_player_view_rotation.x; uw_q_player_view_rotation.y = uw_player_view_rotation.y; uw_q_player_view_rotation.z = uw_player_view_rotation.z; uw_q_player_view_rotation.w = uw_player_view_rotation.w; EOS_AntiCheatCommon_LogPlayerUseWeaponData use_weapon_data; memset(&use_weapon_data, 0, sizeof(use_weapon_data)); use_weapon_data.PlayerHandle = (void *)uw_player_handle; use_weapon_data.PlayerPosition = &uw_v_player_position; use_weapon_data.PlayerViewRotation = &uw_q_player_view_rotation; use_weapon_data.bIsPlayerViewZoomed = VARIANT_TO_EOS_BOOL(use_weapon_data_options->get("is_player_view_zoomed")); use_weapon_data.bIsMeleeAttack = VARIANT_TO_EOS_BOOL(use_weapon_data_options->get("is_melee_attack")); use_weapon_data.WeaponName = uw_weapon_name.get_data(); EOS_AntiCheatCommon_Vec3f victim_position; victim_position.x = p_victim_player_position.x; victim_position.y = p_victim_player_position.y; victim_position.z = p_victim_player_position.z; EOS_AntiCheatCommon_Quat victim_view_rotation; victim_view_rotation.x = p_victim_player_view_rotation.x; victim_view_rotation.y = p_victim_player_view_rotation.y; victim_view_rotation.z = p_victim_player_view_rotation.z; victim_view_rotation.w = p_victim_player_view_rotation.w; EOS_AntiCheatCommon_Vec3f attacker_position; attacker_position.x = p_attacker_player_position.x; attacker_position.y = p_attacker_player_position.y; attacker_position.z = p_attacker_player_position.z; EOS_AntiCheatCommon_Quat attacker_view_rotation; attacker_view_rotation.x = p_attacker_player_view_rotation.x; attacker_view_rotation.y = p_attacker_player_view_rotation.y; attacker_view_rotation.z = p_attacker_player_view_rotation.z; attacker_view_rotation.w = p_attacker_player_view_rotation.w; EOS_AntiCheatCommon_Vec3f damage_position_v; damage_position_v.x = p_damage_position.x; damage_position_v.y = p_damage_position.y; damage_position_v.z = p_damage_position.z; EOS_AntiCheatCommon_Vec3f attacker_view_position_v; attacker_view_position_v.x = p_attacker_player_view_position.x; attacker_view_position_v.y = p_attacker_player_view_position.y; attacker_view_position_v.z = p_attacker_player_view_position.z; EOS_AntiCheatCommon_LogPlayerTakeDamageOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ANTICHEATCOMMON_LOGPLAYERTAKEDAMAGE_API_LATEST; options.VictimPlayerHandle = _anticheat_player_id_to_handle(p_victim_player_handle); options.VictimPlayerPosition = &victim_position; options.VictimPlayerViewRotation = &victim_view_rotation; options.AttackerPlayerHandle = _anticheat_player_id_to_handle(p_attacker_player_handle); options.AttackerPlayerPosition = &attacker_position; options.AttackerPlayerViewRotation = &attacker_view_rotation; options.bIsHitscanAttack = VARIANT_TO_EOS_BOOL(p_options->get("is_hitscan_attack")); options.bHasLineOfSight = VARIANT_TO_EOS_BOOL(p_options->get("has_line_of_sight")); options.bIsCriticalHit = VARIANT_TO_EOS_BOOL(p_options->get("is_critical_hit")); options.DamageTaken = p_damage_taken; options.HealthRemaining = p_health_remaining; options.DamageSource = static_cast(p_damage_source); options.DamageType = static_cast(p_damage_type); options.DamageResult = static_cast(p_damage_result); options.PlayerUseWeaponData = &use_weapon_data; options.TimeSincePlayerUseWeaponMs = static_cast(p_time_since_player_use_weapon_ms); options.DamagePosition = &damage_position_v; options.AttackerPlayerViewPosition = &attacker_view_position_v; return static_cast(EOS_AntiCheatServer_LogPlayerTakeDamage(s_antiCheatServerInterface, &options)); } ================================================ FILE: src/auth_interface.cpp ================================================ #include "ieos.h" using namespace godot; void IEOS::auth_interface_login(Ref p_options) { ERR_FAIL_NULL(s_authInterface); Ref p_credentials = p_options->get("credentials"); EOS_Auth_Credentials credentials; memset(&credentials, 0, sizeof(credentials)); credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST; credentials.Type = static_cast((int)p_credentials->get("type")); CharString id = VARIANT_TO_CHARSTRING(p_credentials->get("id")); if (id.length() > 0) { credentials.Id = id.get_data(); } CharString token = VARIANT_TO_CHARSTRING(p_credentials->get("token")); if (token.length() > 0) { credentials.Token = token.get_data(); } int p_external_type = p_credentials->get("external_type"); if (p_external_type != -1) { credentials.ExternalType = static_cast(p_external_type); } EOS_Auth_LoginOptions loginOptions; memset(&loginOptions, 0, sizeof(loginOptions)); loginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST; loginOptions.Credentials = &credentials; loginOptions.ScopeFlags = static_cast(static_cast(p_options->get("scope_flags"))); loginOptions.LoginFlags = static_cast(p_options->get("login_flags")); p_options->reference(); EOS_Auth_Login(s_authInterface, &loginOptions, (void *)*p_options, [](const EOS_Auth_LoginCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["pin_grant_info"] = eosg_auth_pin_grant_info_to_dict(data->PinGrantInfo); ret["continuance_token"] = eosg_continuance_token_to_wrapper(data->ContinuanceToken); ret["selected_account_id"] = eosg_epic_account_id_to_string(data->SelectedAccountId); if (data->ResultCode == EOS_EResult::EOS_Success) { ret["pending"] = false; ret["success"] = true; } else if (EOS_EResult_IsOperationComplete(data->ResultCode)) { ret["pending"] = false; ret["success"] = false; } else { ret["pending"] = true; ret["success"] = false; } IEOS::get_singleton()->emit_signal("auth_interface_login_callback", ret); }); } void IEOS::auth_interface_logout(Ref p_options) { ERR_FAIL_NULL(s_authInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Auth_LogoutOptions logoutOptions; memset(&logoutOptions, 0, sizeof(logoutOptions)); logoutOptions.ApiVersion = EOS_AUTH_LOGOUT_API_LATEST; logoutOptions.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); p_options->reference(); EOS_Auth_Logout(s_authInterface, &logoutOptions, (void *)*p_options, [](const EOS_Auth_LogoutCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("auth_interface_logout_callback", ret); }); } Dictionary IEOS::auth_interface_copy_id_token(Ref p_options) { ERR_FAIL_NULL_V(s_authInterface, {}); CharString account_id = VARIANT_TO_CHARSTRING(p_options->get("account_id")); EOS_Auth_CopyIdTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_COPYIDTOKEN_API_LATEST; options.AccountId = eosg_string_to_epic_account_id(account_id.get_data()); EOS_Auth_IdToken *outToken = nullptr; EOS_EResult res = EOS_Auth_CopyIdToken(s_authInterface, &options, &outToken); Dictionary ret; ret["result_code"] = static_cast(res); ret["id_token"] = eosg_auth_id_token_to_dict_and_release(outToken); return ret; } Dictionary IEOS::auth_interface_copy_user_auth_token(Ref p_options, const String &p_local_user_id) { ERR_FAIL_NULL_V(s_authInterface, {}); CharString local_user_id = p_local_user_id.utf8(); EOS_Auth_CopyUserAuthTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST; EOS_Auth_Token *outToken = nullptr; EOS_EpicAccountId localUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); EOS_EResult res = EOS_Auth_CopyUserAuthToken(s_authInterface, &options, localUserId, &outToken); Dictionary ret; ret["result_code"] = static_cast(res); ret["token"] = eosg_auth_token_to_dict_and_release(outToken); return ret; } void IEOS::auth_interface_delete_persistent_auth(Ref p_options) { ERR_FAIL_NULL(s_authInterface); CharString refresh_token = VARIANT_TO_CHARSTRING(p_options->get("refresh_token")); EOS_Auth_DeletePersistentAuthOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_DELETEPERSISTENTAUTH_API_LATEST; options.RefreshToken = nullptr; if (refresh_token.size() != 0) { options.RefreshToken = refresh_token.get_data(); } p_options->reference(); EOS_Auth_DeletePersistentAuth(s_authInterface, &options, (void *)*p_options, [](const EOS_Auth_DeletePersistentAuthCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("auth_interface_delete_persistent_auth_callback", ret); }); } String IEOS::auth_interface_get_logged_in_account_by_index(int index) { ERR_FAIL_NULL_V(s_authInterface, ""); EOS_EpicAccountId accountId = EOS_Auth_GetLoggedInAccountByIndex(s_authInterface, index); return eosg_epic_account_id_to_string(accountId); } int IEOS::auth_interface_get_logged_in_accounts_count() { ERR_FAIL_NULL_V(s_authInterface, 0); return static_cast(EOS_Auth_GetLoggedInAccountsCount(s_authInterface)); } int IEOS::auth_interface_get_login_status(const String &p_local_user_id) { ERR_FAIL_NULL_V(s_authInterface, static_cast(EOS_ELoginStatus::EOS_LS_NotLoggedIn)); CharString local_user_id = p_local_user_id.utf8(); EOS_EpicAccountId accountId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Auth_GetLoginStatus(s_authInterface, accountId)); } String IEOS::auth_interface_get_merged_account_by_index(const String &p_local_user_id, int index) { ERR_FAIL_NULL_V(s_authInterface, ""); CharString local_user_id = p_local_user_id.utf8(); EOS_EpicAccountId localUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); EOS_EpicAccountId accountId = EOS_Auth_GetMergedAccountByIndex(s_authInterface, localUserId, index); return eosg_epic_account_id_to_string(accountId); } int IEOS::auth_interface_get_merged_accounts_count(const String &p_local_user_id) { ERR_FAIL_NULL_V(s_authInterface, 0); CharString local_user_id = p_local_user_id.utf8(); EOS_EpicAccountId localUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Auth_GetMergedAccountsCount(s_authInterface, localUserId)); } Dictionary IEOS::auth_interface_get_selected_account_id(const String &p_local_user_id) { ERR_FAIL_NULL_V(s_authInterface, {}); CharString local_user_id = p_local_user_id.utf8(); EOS_EpicAccountId localUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); EOS_EpicAccountId outSelectedAccountId = nullptr; EOS_EResult res = EOS_Auth_GetSelectedAccountId(s_authInterface, localUserId, &outSelectedAccountId); Dictionary ret; ret["result_code"] = static_cast(res); ret["selected_account_id"] = eosg_epic_account_id_to_string(outSelectedAccountId); return ret; } void IEOS::auth_interface_query_id_token(Ref p_options) { ERR_FAIL_NULL(s_authInterface); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString p_target_account_id = VARIANT_TO_CHARSTRING(p_options->get("target_account_id")); EOS_Auth_QueryIdTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_QUERYIDTOKEN_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(p_local_user_id.get_data()); options.TargetAccountId = eosg_string_to_epic_account_id(p_target_account_id.get_data()); p_options->reference(); EOS_Auth_QueryIdToken(s_authInterface, &options, (void *)*p_options, [](const EOS_Auth_QueryIdTokenCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_account_id"] = eosg_epic_account_id_to_string(data->TargetAccountId); IEOS::get_singleton()->emit_signal("auth_interface_query_id_token_callback", ret); }); } void IEOS::auth_interface_verify_id_token(Ref p_options) { ERR_FAIL_NULL(s_authInterface); Ref p_id_token = p_options->get("id_token"); CharString p_account_id = VARIANT_TO_CHARSTRING(p_id_token->get("account_id")); CharString p_json_web_token = VARIANT_TO_CHARSTRING(p_id_token->get("json_web_token")); EOS_Auth_IdToken idToken; memset(&idToken, 0, sizeof(idToken)); idToken.ApiVersion = EOS_AUTH_IDTOKEN_API_LATEST; idToken.AccountId = eosg_string_to_epic_account_id(p_account_id.get_data()); idToken.JsonWebToken = p_json_web_token.get_data(); EOS_Auth_VerifyIdTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_VERIFYIDTOKEN_API_LATEST; options.IdToken = &idToken; p_options->reference(); EOS_Auth_VerifyIdToken(s_authInterface, &options, (void *)*p_options, [](const EOS_Auth_VerifyIdTokenCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["application_id"] = EOSG_GET_STRING(data->ApplicationId); ret["client_id"] = EOSG_GET_STRING(data->ClientId); ret["product_id"] = EOSG_GET_STRING(data->ProductId); ret["sandbox_id"] = EOSG_GET_STRING(data->SandboxId); ret["deployment_id"] = EOSG_GET_STRING(data->DeploymentId); ret["display_name"] = EOSG_GET_STRING(data->DisplayName); ret["is_external_account_info_present"] = EOSG_GET_BOOL(data->bIsExternalAccountInfoPresent); ret["external_account_id_type"] = static_cast(data->ExternalAccountIdType); ret["external_account_id"] = EOSG_GET_STRING(data->ExternalAccountId); ret["external_account_display_name"] = EOSG_GET_STRING(data->ExternalAccountDisplayName); ret["platform"] = EOSG_GET_STRING(data->Platform); IEOS::get_singleton()->emit_signal("auth_interface_verify_id_token_callback", ret); }); } void IEOS::auth_interface_link_account(Ref p_options) { ERR_FAIL_NULL(s_authInterface); Ref p_continuance_token = Object::cast_to(p_options->get("continuance_token")); ERR_FAIL_NULL_MSG(p_continuance_token, "Error linking account. LinkAccountOptions.continuance_token is null."); ERR_FAIL_NULL_MSG(p_continuance_token->get_internal(), "Error linking account. EOSGContinuanceToken is null."); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Auth_LinkAccountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_LINKACCOUNT_API_LATEST; options.LinkAccountFlags = static_cast((int)p_options->get("link_account_flags")); options.ContinuanceToken = p_continuance_token->get_internal(); options.LocalUserId = eosg_string_to_epic_account_id(p_local_user_id.get_data()); p_options->reference(); EOS_Auth_LinkAccount(s_authInterface, &options, (void *)*p_options, [](const EOS_Auth_LinkAccountCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["pin_grant_info"] = eosg_auth_pin_grant_info_to_dict(data->PinGrantInfo); ret["selected_account_id"] = eosg_epic_account_id_to_string(data->SelectedAccountId); IEOS::get_singleton()->emit_signal("auth_interface_link_account_callback", ret); }); } void IEOS::auth_interface_verify_user_auth(Ref p_options) { ERR_FAIL_NULL(s_authInterface); Ref p_auth_token = Object::cast_to(p_options->get("auth_token")); CharString p_app = VARIANT_TO_CHARSTRING(p_auth_token->get("app")); CharString p_client_id = VARIANT_TO_CHARSTRING(p_auth_token->get("client_id")); CharString p_access_token = VARIANT_TO_CHARSTRING(p_auth_token->get("access_token")); CharString p_expires_at = VARIANT_TO_CHARSTRING(p_auth_token->get("expires_at")); CharString p_refresh_token = VARIANT_TO_CHARSTRING(p_auth_token->get("refresh_token")); CharString p_refresh_expires_at = VARIANT_TO_CHARSTRING(p_auth_token->get("refresh_expires_at")); CharString p_account_id = VARIANT_TO_CHARSTRING(p_auth_token->get("account_id")); int p_auth_type = p_auth_token->get("auth_type"); EOS_EpicAccountId accountId = eosg_string_to_epic_account_id(p_account_id.get_data()); EOS_Auth_Token authToken; memset(&authToken, 0, sizeof(authToken)); authToken.ApiVersion = EOS_AUTH_TOKEN_API_LATEST; authToken.App = p_app.get_data(); authToken.ClientId = p_client_id.get_data(); authToken.AccessToken = p_access_token.get_data(); authToken.ExpiresAt = p_expires_at.get_data(); authToken.RefreshToken = p_refresh_token.get_data(); authToken.RefreshExpiresAt = p_refresh_expires_at.get_data(); authToken.AccountId = accountId; authToken.AuthType = static_cast(p_auth_type); authToken.RefreshExpiresIn = static_cast(p_auth_token->get("refresh_expires_in")); authToken.ExpiresIn = static_cast(p_auth_token->get("expires_in")); EOS_Auth_VerifyUserAuthOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_VERIFYUSERAUTH_API_LATEST; options.AuthToken = &authToken; p_options->reference(); EOS_Auth_VerifyUserAuth(s_authInterface, &options, (void *)*p_options, [](const EOS_Auth_VerifyUserAuthCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("auth_interface_verify_user_auth_callback", ret); }); } ================================================ FILE: src/connect_interface.cpp ================================================ #include "ieos.h" using namespace godot; void IEOS::connect_interface_login(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); Ref p_credentials = p_options->get("credentials"); Ref p_user_login_info = p_options->get("user_login_info"); CharString token = VARIANT_TO_CHARSTRING(p_credentials->get("token")); String p_display_name; String p_nsa_id_token; if (p_user_login_info != nullptr) { p_display_name = p_user_login_info->get("display_name"); p_nsa_id_token = p_user_login_info->get("nsa_id_token"); } CharString display_name = p_display_name.utf8(); CharString nsa_id_token = p_nsa_id_token.utf8(); EOS_Connect_Credentials credentials; memset(&credentials, 0, sizeof(credentials)); credentials.ApiVersion = EOS_CONNECT_CREDENTIALS_API_LATEST; credentials.Type = static_cast((int)p_credentials->get("type")); if (credentials.Type == EOS_EExternalCredentialType::EOS_ECT_DEVICEID_ACCESS_TOKEN) { credentials.Token = nullptr; //It needs to be nullptr for deviceid, otherwise login will fail } else { credentials.Token = token.get_data(); } EOS_Connect_UserLoginInfo userLoginInfo; memset(&userLoginInfo, 0, sizeof(userLoginInfo)); if (!p_display_name.is_empty()) { userLoginInfo.ApiVersion = EOS_CONNECT_USERLOGININFO_API_LATEST; userLoginInfo.DisplayName = display_name.get_data(); } if (!p_nsa_id_token.is_empty()) { userLoginInfo.ApiVersion = EOS_CONNECT_USERLOGININFO_API_LATEST; userLoginInfo.NsaIdToken = nsa_id_token.get_data(); } EOS_Connect_LoginOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST; options.Credentials = &credentials; if (!p_display_name.is_empty()) { options.UserLoginInfo = &userLoginInfo; } p_options->reference(); EOS_Connect_Login(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_LoginCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["continuance_token"] = eosg_continuance_token_to_wrapper(data->ContinuanceToken); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); if (data->ResultCode == EOS_EResult::EOS_Success) { ret["success"] = true; ret["pending"] = false; } else if (EOS_EResult_IsOperationComplete(data->ResultCode)) { ret["success"] = false; ret["pending"] = true; } else { ret["success"] = false; ret["pending"] = false; } IEOS::get_singleton()->emit_signal("connect_interface_login_callback", ret); }); } void IEOS::connect_interface_logout(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Connect_LogoutOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_LOGOUT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_Connect_Logout(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_LogoutCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_logout_callback", ret); }); } Dictionary IEOS::connect_interface_copy_id_token(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Connect_CopyIdTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_AUTH_COPYIDTOKEN_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); EOS_Connect_IdToken *outToken = nullptr; EOS_EResult res = EOS_Connect_CopyIdToken(s_connectInterface, &options, &outToken); Dictionary ret; ret["result_code"] = static_cast(res); ret["id_token"] = eosg_connect_id_token_to_dict_and_release(outToken); return ret; } Dictionary IEOS::connect_interface_copy_product_user_external_account_by_account_id(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString account_id = VARIANT_TO_CHARSTRING(p_options->get("account_id")); EOS_Connect_CopyProductUserExternalAccountByAccountIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_COPYPRODUCTUSEREXTERNALACCOUNTBYACCOUNTID_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.AccountId = account_id.get_data(); EOS_Connect_ExternalAccountInfo *outExternalAccountInfo = nullptr; EOS_EResult res = EOS_Connect_CopyProductUserExternalAccountByAccountId(s_connectInterface, &options, &outExternalAccountInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_account_info"] = eosg_connect_external_account_info_to_dict_and_release(outExternalAccountInfo); return ret; } Dictionary IEOS::connect_interface_copy_product_user_external_account_by_account_type(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); int account_id_type = p_options->get("account_id_type"); EOS_Connect_CopyProductUserExternalAccountByAccountTypeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_COPYPRODUCTUSEREXTERNALACCOUNTBYACCOUNTTYPE_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.AccountIdType = static_cast(account_id_type); EOS_Connect_ExternalAccountInfo *outExternalAccountInfo = nullptr; EOS_EResult res = EOS_Connect_CopyProductUserExternalAccountByAccountType(s_connectInterface, &options, &outExternalAccountInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_account_info"] = eosg_connect_external_account_info_to_dict_and_release(outExternalAccountInfo); return ret; } Dictionary IEOS::connect_interface_copy_product_user_external_account_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Connect_CopyProductUserExternalAccountByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_COPYPRODUCTUSEREXTERNALACCOUNTBYINDEX_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.ExternalAccountInfoIndex = static_cast(static_cast(p_options->get("external_account_info_index"))); EOS_Connect_ExternalAccountInfo *outExternalAccountInfo = nullptr; EOS_EResult res = EOS_Connect_CopyProductUserExternalAccountByIndex(s_connectInterface, &options, &outExternalAccountInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_account_info"] = eosg_connect_external_account_info_to_dict_and_release(outExternalAccountInfo); return ret; } Dictionary IEOS::connect_interface_copy_product_user_info(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Connect_CopyProductUserInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_COPYPRODUCTUSERINFO_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); EOS_Connect_ExternalAccountInfo *outExternalAccountInfo = nullptr; EOS_EResult res = EOS_Connect_CopyProductUserInfo(s_connectInterface, &options, &outExternalAccountInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_account_info"] = eosg_connect_external_account_info_to_dict_and_release(outExternalAccountInfo); return ret; } void IEOS::connect_interface_create_device_id(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); CharString p_device_model = VARIANT_TO_CHARSTRING(p_options->get("device_model")); EOS_Connect_CreateDeviceIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_CREATEDEVICEID_API_LATEST; options.DeviceModel = p_device_model.get_data(); p_options->reference(); EOS_Connect_CreateDeviceId(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_CreateDeviceIdCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("connect_interface_create_device_id_callback", ret); }); } void IEOS::connect_interface_delete_device_id(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); EOS_Connect_DeleteDeviceIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_DELETEDEVICEID_API_LATEST; p_options->reference(); EOS_Connect_DeleteDeviceId(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_DeleteDeviceIdCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("connect_interface_delete_device_id_callback", ret); }); } void IEOS::connect_interface_create_user(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); Ref p_continuance_token = Object::cast_to(p_options->get("continuance_token")); ERR_FAIL_NULL_MSG(p_continuance_token, "Error linking account. LinkAccountOptions.continuance_token is null."); ERR_FAIL_NULL_MSG(p_continuance_token->get_internal(), "Error linking account. EOSGContinuanceToken is null."); EOS_Connect_CreateUserOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST; options.ContinuanceToken = p_continuance_token->get_internal(); p_options->reference(); EOS_Connect_CreateUser(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_CreateUserCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_create_user_callback", ret); }); } String IEOS::connect_interface_get_external_account_mapping(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, ""); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_external_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_external_user_id")); int account_id_type = p_options->get("account_id_type"); EOS_Connect_GetExternalAccountMappingsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_GETEXTERNALACCOUNTMAPPINGS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.AccountIdType = static_cast(account_id_type); options.TargetExternalUserId = target_external_user_id.get_data(); return eosg_product_user_id_to_string(EOS_Connect_GetExternalAccountMapping(s_connectInterface, &options)); } String IEOS::connect_interface_get_logged_in_user_by_index(int p_index) { ERR_FAIL_NULL_V(s_connectInterface, ""); return eosg_product_user_id_to_string(EOS_Connect_GetLoggedInUserByIndex(s_connectInterface, p_index)); } int IEOS::connect_interface_get_logged_in_users_count() { ERR_FAIL_NULL_V(s_connectInterface, 0); return static_cast(EOS_Connect_GetLoggedInUsersCount(s_connectInterface)); } int IEOS::connect_interface_get_login_status(const String &p_local_user_id) { ERR_FAIL_NULL_V(s_connectInterface, static_cast(EOS_ELoginStatus::EOS_LS_NotLoggedIn)); CharString local_user_id = p_local_user_id.utf8(); EOS_ProductUserId localUserId = eosg_string_to_product_user_id(local_user_id.get_data()); return static_cast(EOS_Connect_GetLoginStatus(s_connectInterface, localUserId)); } int IEOS::connect_interface_get_product_user_external_account_count(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, 0); CharString p_target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Connect_GetProductUserExternalAccountCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_GETPRODUCTUSEREXTERNALACCOUNTCOUNT_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(p_target_user_id.get_data()); return static_cast(EOS_Connect_GetProductUserExternalAccountCount(s_connectInterface, &options)); } Dictionary IEOS::connect_interface_get_product_user_id_mapping(Ref p_options) { ERR_FAIL_NULL_V(s_connectInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_product_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_product_user_id")); int account_id_type = p_options->get("account_id_type"); EOS_Connect_GetProductUserIdMappingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_GETPRODUCTUSERIDMAPPING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.AccountIdType = static_cast(account_id_type); options.TargetProductUserId = eosg_string_to_product_user_id(target_product_user_id.get_data()); char *outAccountId = (char *)memalloc(EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH + 1); int outAccountIdLength = EOS_CONNECT_EXTERNAL_ACCOUNT_ID_MAX_LENGTH + 1; EOS_EResult res = EOS_Connect_GetProductUserIdMapping(s_connectInterface, &options, outAccountId, &outAccountIdLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["account_id"] = EOSG_GET_STRING(outAccountId); return ret; } void IEOS::connect_interface_query_product_user_id_mappings(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); TypedArray p_product_user_ids = p_options->get("product_user_ids"); EOS_ProductUserId *product_user_ids = nullptr; PERSISTENT_CHAR_ARRAY_CREATE(product_user_ids_cstr, product_user_ids_cstr_charstrings, p_product_user_ids.size()); if (p_product_user_ids.size() > 0) { product_user_ids = (EOS_ProductUserId *)memalloc(sizeof(EOS_ProductUserId) * p_product_user_ids.size()); for (int i = 0; i < p_product_user_ids.size(); i++) { PERSISTENT_CHAR_ARRAY_SET(product_user_ids_cstr, product_user_ids_cstr_charstrings, i, p_product_user_ids[i]); product_user_ids[i] = eosg_string_to_product_user_id(product_user_ids_cstr[i]); } } EOS_Connect_QueryProductUserIdMappingsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_QUERYPRODUCTUSERIDMAPPINGS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(p_local_user_id.get_data()); options.ProductUserIds = product_user_ids; options.ProductUserIdCount = p_product_user_ids.size(); p_options->reference(); EOS_Connect_QueryProductUserIdMappings(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_QueryProductUserIdMappingsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_query_product_user_id_mappings_callback", ret); }); } void IEOS::connect_interface_query_external_account_mappings(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); CharString p_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int p_account_id_type = p_options->get("account_id_type"); TypedArray p_external_account_ids = p_options->get("external_account_ids"); const char **external_account_ids = nullptr; PERSISTENT_CHAR_ARRAY_CREATE(external_account_ids_cstr, external_account_ids_cstr_charstrings, p_external_account_ids.size()); if (p_external_account_ids.size() > 0) { external_account_ids = (const char **)memalloc(sizeof(const char *) * p_external_account_ids.size()); for (int i = 0; i < p_external_account_ids.size(); i++) { PERSISTENT_CHAR_ARRAY_SET(external_account_ids_cstr, external_account_ids_cstr_charstrings, i, p_external_account_ids[i]); external_account_ids[i] = external_account_ids_cstr[i]; } } EOS_Connect_QueryExternalAccountMappingsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_QUERYEXTERNALACCOUNTMAPPINGS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(p_local_user_id.get_data()); options.AccountIdType = static_cast(p_account_id_type); options.ExternalAccountIds = external_account_ids; options.ExternalAccountIdCount = p_external_account_ids.size(); p_options->reference(); EOS_Connect_QueryExternalAccountMappings(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_QueryExternalAccountMappingsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_query_external_account_mappings_callback", ret); }); } void IEOS::connect_interface_link_account(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); Ref p_continuance_token = Object::cast_to(p_options->get("continuance_token")); ERR_FAIL_NULL_MSG(p_continuance_token, "Error linking account. LinkAccountOptions.continuance_token is null."); ERR_FAIL_NULL_MSG(p_continuance_token->get_internal(), "Error linking account. EOSGContinuanceToken is null."); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Connect_LinkAccountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_LINKACCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.ContinuanceToken = p_continuance_token->get_internal(); p_options->reference(); EOS_Connect_LinkAccount(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_LinkAccountCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_link_account_callback", ret); }); } void IEOS::connect_interface_verify_id_token(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); Ref p_id_token = p_options->get("id_token"); CharString p_product_user_id = VARIANT_TO_CHARSTRING(p_id_token->get("product_user_id")); CharString p_json_web_token = VARIANT_TO_CHARSTRING(p_id_token->get("json_web_token")); EOS_Connect_IdToken idToken; memset(&idToken, 0, sizeof(idToken)); idToken.ApiVersion = EOS_CONNECT_IDTOKEN_API_LATEST; idToken.ProductUserId = eosg_string_to_product_user_id(p_product_user_id.get_data()); idToken.JsonWebToken = p_json_web_token.get_data(); EOS_Connect_VerifyIdTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_VERIFYIDTOKEN_API_LATEST; options.IdToken = &idToken; EOS_Connect_VerifyIdToken(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_VerifyIdTokenCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["product_user_id"] = eosg_product_user_id_to_string(data->ProductUserId); ret["is_account_info_present"] = EOSG_GET_BOOL(data->bIsAccountInfoPresent); ret["account_id_type"] = static_cast(data->AccountIdType); ret["account_id"] = EOSG_GET_STRING(data->AccountId); IEOS::get_singleton()->emit_signal("connect_interface_verify_id_token_callback", ret); }); } void IEOS::connect_interface_transfer_device_id_account(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); String p_primary_local_user_id = p_options->get("primary_local_user_id"); CharString primary_local_user_id = VARIANT_TO_CHARSTRING(p_options->get("primary_local_user_id")); CharString local_device_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_device_user_id")); String p_product_user_id_to_preserve = p_options->get("product_user_id_to_preserve"); CharString product_user_id_to_preserve = VARIANT_TO_CHARSTRING(p_options->get("product_user_id_to_preserve")); EOS_ProductUserId primaryLocalUserId = eosg_string_to_product_user_id(primary_local_user_id.get_data()); EOS_ProductUserId localDeviceUserId = eosg_string_to_product_user_id(local_device_user_id.get_data()); EOS_Connect_TransferDeviceIdAccountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_TRANSFERDEVICEIDACCOUNT_API_LATEST; options.PrimaryLocalUserId = primaryLocalUserId; options.LocalDeviceUserId = localDeviceUserId; if (p_product_user_id_to_preserve == p_primary_local_user_id) { options.ProductUserIdToPreserve = primaryLocalUserId; } else { options.PrimaryLocalUserId = localDeviceUserId; } p_options->reference(); EOS_Connect_TransferDeviceIdAccount(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_TransferDeviceIdAccountCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_transfer_device_id_account_callback", ret); }); } void IEOS::connect_interface_unlink_account(Ref p_options) { ERR_FAIL_NULL(s_connectInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Connect_UnlinkAccountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CONNECT_UNLINKACCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_Connect_UnlinkAccount(s_connectInterface, &options, (void *)*p_options, [](const EOS_Connect_UnlinkAccountCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_unlink_account_callback", ret); }); } ================================================ FILE: src/custom_invites_interface.cpp ================================================ #include "ieos.h" using namespace std; int IEOS::custom_invites_interface_finalize_invite(Ref p_options) { ERR_FAIL_NULL_V(s_customInvitesInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString custom_invite_id = VARIANT_TO_CHARSTRING(p_options->get("custom_invite_id")); EOS_CustomInvites_FinalizeInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_FINALIZEINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.CustomInviteId = custom_invite_id.get_data(); options.ProcessingResult = static_cast(static_cast(p_options->get("processing_result"))); return static_cast(EOS_CustomInvites_FinalizeInvite(s_customInvitesInterface, &options)); } void IEOS::custom_invites_interface_send_custom_invite(Ref p_options) { ERR_FAIL_NULL(s_customInvitesInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); Array p_target_user_ids = p_options->get("target_user_ids"); int target_user_ids_count = p_target_user_ids.size(); EOS_ProductUserId *targetUserIds = (EOS_ProductUserId *)memalloc(sizeof(EOS_ProductUserId) * target_user_ids_count); PERSISTENT_CHAR_ARRAY_CREATE(target_user_ids, target_user_ids_charstrings, target_user_ids_count) for (int i = 0; i < target_user_ids_count; i++) { PERSISTENT_CHAR_ARRAY_SET(target_user_ids, target_user_ids_charstrings, i, p_target_user_ids[i]); targetUserIds[i] = eosg_string_to_product_user_id(target_user_ids[i]); } EOS_CustomInvites_SendCustomInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_SENDCUSTOMINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserIds = targetUserIds; options.TargetUserIdsCount = target_user_ids_count; p_options->reference(); EOS_CustomInvites_SendCustomInvite(s_customInvitesInterface, &options, (void *)*p_options, [](const EOS_CustomInvites_SendCustomInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); Array target_user_ids = Array(); for (int i = 0; i < data->TargetUserIdsCount; i++) { target_user_ids.append(eosg_product_user_id_to_string(data->TargetUserIds[i])); } ret["target_user_ids"] = target_user_ids; IEOS::get_singleton()->emit_signal("custom_invites_interface_send_custom_invite_callback", ret); }); } int IEOS::custom_invites_interface_set_custom_invite(Ref p_options) { ERR_FAIL_NULL_V(s_customInvitesInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString payload = VARIANT_TO_CHARSTRING(p_options->get("payload")); EOS_CustomInvites_SetCustomInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_SETCUSTOMINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Payload = payload.get_data(); return static_cast(EOS_CustomInvites_SetCustomInvite(s_customInvitesInterface, &options)); } void IEOS::custom_invites_interface_send_request_to_join(Ref p_options) { ERR_FAIL_NULL(s_customInvitesInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_CustomInvites_SendRequestToJoinOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_SENDREQUESTTOJOIN_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); p_options->reference(); EOS_CustomInvites_SendRequestToJoin(s_customInvitesInterface, &options, (void *)*p_options, [](const EOS_CustomInvites_SendRequestToJoinCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_send_request_to_join_callback", ret); }); } // TODO: impl EOS_CustomInvites_AddNotifySendCustomNativeInviteRequested // TODO: impl EOS_CustomInvites_RemoveNotifySendCustomNativeInviteRequested void IEOS::custom_invites_interface_accept_request_to_join(Ref p_options) { ERR_FAIL_NULL(s_customInvitesInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_CustomInvites_AcceptRequestToJoinOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_ACCEPTREQUESTTOJOIN_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); p_options->reference(); EOS_CustomInvites_AcceptRequestToJoin(s_customInvitesInterface, &options, (void *)*p_options, [](const EOS_CustomInvites_AcceptRequestToJoinCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_accept_request_to_join_callback", ret); }); } void IEOS::custom_invites_interface_reject_request_to_join(Ref p_options) { ERR_FAIL_NULL(s_customInvitesInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_CustomInvites_RejectRequestToJoinOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_CUSTOMINVITES_REJECTREQUESTTOJOIN_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); p_options->reference(); EOS_CustomInvites_RejectRequestToJoin(s_customInvitesInterface, &options, (void *)*p_options, [](const EOS_CustomInvites_RejectRequestToJoinCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_reject_request_to_join_callback", ret); }); } ================================================ FILE: src/ecom_interface.cpp ================================================ #include "ieos.h" using namespace std; void IEOS::ecom_interface_checkout(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString override_catalog_namespace = VARIANT_TO_CHARSTRING(p_options->get("override_catalog_namespace")); int preferred_orientation = p_options->get("preferred_orientation"); EOS_Ecom_CheckoutOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_CHECKOUT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.PreferredOrientation = static_cast(preferred_orientation); if (override_catalog_namespace.length() > 0) { options.OverrideCatalogNamespace = override_catalog_namespace.get_data(); } TypedArray p_entries = p_options->get("entries"); options.EntryCount = static_cast(p_entries.size()); EOS_Ecom_CheckoutEntry *entries = nullptr; if (options.EntryCount > 0) { entries = (EOS_Ecom_CheckoutEntry *)memalloc(sizeof(EOS_Ecom_CheckoutEntry) * options.EntryCount); for (int i = 0; i < options.EntryCount; i++) { entries[i].ApiVersion = EOS_ECOM_CHECKOUTENTRY_API_LATEST; entries[i].OfferId = VARIANT_TO_CHARSTRING(p_entries[i].get("offer_id")).get_data(); } } options.Entries = entries; p_options->reference(); EOS_Ecom_Checkout(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_CheckoutCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["transaction_id"] = String(data->TransactionId); IEOS::get_singleton()->emit_signal("ecom_interface_checkout_callback", ret); }); } Dictionary IEOS::ecom_interface_copy_entitlement_by_id(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString entitlement_id = VARIANT_TO_CHARSTRING(p_options->get("entitlement_id")); EOS_Ecom_CopyEntitlementByIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYENTITLEMENTBYID_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.EntitlementId = entitlement_id.get_data(); EOS_Ecom_Entitlement *outEntitlement = nullptr; EOS_EResult res = EOS_Ecom_CopyEntitlementById(s_ecomInterface, &options, &outEntitlement); Dictionary ret; ret["result_code"] = static_cast(res); ret["entitlement"] = eosg_ecom_entitlement_to_dict_and_release(outEntitlement); return ret; } Dictionary IEOS::ecom_interface_copy_entitlement_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_CopyEntitlementByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYENTITLEMENTBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.EntitlementIndex = static_cast(p_options->get("entitlement_index")); EOS_Ecom_Entitlement *outEntitlement = nullptr; EOS_EResult res = EOS_Ecom_CopyEntitlementByIndex(s_ecomInterface, &options, &outEntitlement); Dictionary ret; ret["result_code"] = static_cast(res); ret["entitlement"] = eosg_ecom_entitlement_to_dict_and_release(outEntitlement); return ret; } Dictionary IEOS::ecom_interface_copy_entitlement_by_name_and_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString entitlement_name = VARIANT_TO_CHARSTRING(p_options->get("entitlement_name")); EOS_Ecom_CopyEntitlementByNameAndIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYENTITLEMENTBYNAMEANDINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.EntitlementName = entitlement_name.get_data(); options.Index = static_cast(p_options->get("index")); EOS_Ecom_Entitlement *outEntitlement = nullptr; EOS_EResult res = EOS_Ecom_CopyEntitlementByNameAndIndex(s_ecomInterface, &options, &outEntitlement); Dictionary ret; ret["result_code"] = static_cast(res); ret["entitlement"] = eosg_ecom_entitlement_to_dict_and_release(outEntitlement); return ret; } Dictionary IEOS::ecom_interface_copy_item_by_id(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString item_id = VARIANT_TO_CHARSTRING(p_options->get("item_id")); EOS_Ecom_CopyItemByIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYITEMBYID_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ItemId = item_id.get_data(); EOS_Ecom_CatalogItem *outItem = nullptr; EOS_EResult res = EOS_Ecom_CopyItemById(s_ecomInterface, &options, &outItem); Dictionary ret; ret["result_code"] = static_cast(res); ret["item"] = eosg_ecom_catalog_item_to_dict_and_release(outItem); return ret; } Dictionary IEOS::ecom_interface_copy_item_image_info_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString item_id = VARIANT_TO_CHARSTRING(p_options->get("item_id")); int image_info_index = p_options->get("image_info_index"); EOS_Ecom_CopyItemImageInfoByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYITEMIMAGEINFOBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ItemId = item_id.get_data(); options.ImageInfoIndex = static_cast(image_info_index); EOS_Ecom_KeyImageInfo *outImageInfo = nullptr; EOS_EResult res = EOS_Ecom_CopyItemImageInfoByIndex(s_ecomInterface, &options, &outImageInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["image_info"] = eosg_ecom_key_image_info_to_dict_and_release(outImageInfo); return ret; } Dictionary IEOS::ecom_interface_copy_item_release_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString item_id = VARIANT_TO_CHARSTRING(p_options->get("item_id")); int release_index = p_options->get("release_index"); EOS_Ecom_CopyItemReleaseByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYITEMRELEASEBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ItemId = item_id.get_data(); options.ReleaseIndex = static_cast(release_index); EOS_Ecom_CatalogRelease *outRelease = nullptr; EOS_EResult res = EOS_Ecom_CopyItemReleaseByIndex(s_ecomInterface, &options, &outRelease); Dictionary ret; ret["result_code"] = static_cast(res); ret["release"] = eosg_ecom_catalog_release_to_dict_and_release(outRelease); return ret; } Dictionary IEOS::ecom_interface_copy_offer_by_id(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString offer_id = VARIANT_TO_CHARSTRING(p_options->get("offer_id")); EOS_Ecom_CopyOfferByIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYOFFERBYID_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferId = offer_id.get_data(); EOS_Ecom_CatalogOffer *outOffer = nullptr; EOS_EResult res = EOS_Ecom_CopyOfferById(s_ecomInterface, &options, &outOffer); Dictionary ret; ret["result_code"] = static_cast(res); ret["offer"] = eosg_ecom_catalog_offer_to_dict_and_release(outOffer); return ret; } Dictionary IEOS::ecom_interface_copy_offer_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int offer_index = p_options->get("offer_index"); EOS_Ecom_CopyOfferByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYOFFERBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferIndex = static_cast(offer_index); EOS_Ecom_CatalogOffer *outOffer = nullptr; EOS_EResult res = EOS_Ecom_CopyOfferByIndex(s_ecomInterface, &options, &outOffer); Dictionary ret; ret["result_code"] = static_cast(res); ret["offer"] = eosg_ecom_catalog_offer_to_dict_and_release(outOffer); return ret; } Dictionary IEOS::ecom_interface_copy_offer_image_info_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString offer_id = VARIANT_TO_CHARSTRING(p_options->get("offer_id")); int image_info_index = p_options->get("image_info_index"); EOS_Ecom_CopyOfferImageInfoByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYOFFERIMAGEINFOBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferId = offer_id.get_data(); options.ImageInfoIndex = static_cast(image_info_index); EOS_Ecom_KeyImageInfo *outImageInfo = nullptr; EOS_EResult res = EOS_Ecom_CopyOfferImageInfoByIndex(s_ecomInterface, &options, &outImageInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["image_info"] = eosg_ecom_key_image_info_to_dict_and_release(outImageInfo); return ret; } Dictionary IEOS::ecom_interface_copy_offer_item_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString offer_id = VARIANT_TO_CHARSTRING(p_options->get("offer_id")); int image_index = p_options->get("item_index"); EOS_Ecom_CopyOfferItemByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYOFFERITEMBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferId = offer_id.get_data(); options.ItemIndex = static_cast(image_index); EOS_Ecom_CatalogItem *outItem = nullptr; EOS_EResult res = EOS_Ecom_CopyOfferItemByIndex(s_ecomInterface, &options, &outItem); Dictionary ret; ret["result_code"] = static_cast(res); ret["item"] = eosg_ecom_catalog_item_to_dict_and_release(outItem); return ret; } Dictionary IEOS::ecom_interface_copy_transaction_by_id(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString transaction_id = VARIANT_TO_CHARSTRING(p_options->get("transaction_id")); EOS_Ecom_CopyTransactionByIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYTRANSACTIONBYID_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TransactionId = transaction_id.get_data(); EOS_Ecom_HTransaction outTransaction = nullptr; EOS_EResult res = EOS_Ecom_CopyTransactionById(s_ecomInterface, &options, &outTransaction); Dictionary ret; ret["result_code"] = static_cast(res); ret["transaction"] = eosg_ecom_transaction_to_wrapper(outTransaction); return ret; } Dictionary IEOS::ecom_interface_copy_transaction_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int transaction_index = p_options->get("transaction_index"); EOS_Ecom_CopyTransactionByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYTRANSACTIONBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TransactionIndex = static_cast(transaction_index); EOS_Ecom_HTransaction outTransaction = nullptr; EOS_EResult res = EOS_Ecom_CopyTransactionByIndex(s_ecomInterface, &options, &outTransaction); Dictionary ret; ret["result_code"] = static_cast(res); ret["transaction"] = eosg_ecom_transaction_to_wrapper(outTransaction); return ret; } int IEOS::ecom_interface_get_entitlements_by_name_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString entitlement_name = VARIANT_TO_CHARSTRING(p_options->get("entitlement_name")); EOS_Ecom_GetEntitlementsByNameCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETENTITLEMENTSBYNAMECOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.EntitlementName = entitlement_name.get_data(); return static_cast(EOS_Ecom_GetEntitlementsByNameCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_entitlements_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_GetEntitlementsCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETENTITLEMENTSCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Ecom_GetEntitlementsCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_item_image_info_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString item_id = VARIANT_TO_CHARSTRING(p_options->get("item_id")); EOS_Ecom_GetItemImageInfoCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETITEMIMAGEINFOCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ItemId = item_id.get_data(); return static_cast(EOS_Ecom_GetItemImageInfoCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_item_release_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString item_id = VARIANT_TO_CHARSTRING(p_options->get("item_id")); EOS_Ecom_GetItemReleaseCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETITEMRELEASECOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ItemId = item_id.get_data(); return static_cast(EOS_Ecom_GetItemReleaseCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_offer_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_GetOfferCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETOFFERCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Ecom_GetOfferCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_offer_image_info_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString offer_id = VARIANT_TO_CHARSTRING(p_options->get("offer_id")); EOS_Ecom_GetOfferImageInfoCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETOFFERIMAGEINFOCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferId = offer_id.get_data(); return static_cast(EOS_Ecom_GetOfferImageInfoCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_offer_item_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString offer_id = VARIANT_TO_CHARSTRING(p_options->get("offer_id")); EOS_Ecom_GetOfferItemCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETOFFERITEMCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.OfferId = offer_id.get_data(); return static_cast(EOS_Ecom_GetOfferItemCount(s_ecomInterface, &options)); } int IEOS::ecom_interface_get_transaction_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_GetTransactionCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETTRANSACTIONCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Ecom_GetTransactionCount(s_ecomInterface, &options)); } void IEOS::ecom_interface_query_entitlements(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString override_catalog_namespace = VARIANT_TO_CHARSTRING(p_options->get("override_catalog_namespace")); EOS_Ecom_QueryEntitlementsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYENTITLEMENTS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); if (override_catalog_namespace.size() > 0){ options.OverrideCatalogNamespace = override_catalog_namespace.get_data(); } TypedArray p_entitlement_names = p_options->get("entitlement_names"); options.EntitlementNameCount = static_cast(p_entitlement_names.size()); EOS_Ecom_EntitlementName *entitlementNames = nullptr; if (options.EntitlementNameCount > 0) { entitlementNames = (EOS_Ecom_EntitlementName *)memalloc(sizeof(EOS_Ecom_EntitlementName) * p_entitlement_names.size()); for (int i = 0; i < options.EntitlementNameCount; i++) { entitlementNames[i] = VARIANT_TO_CHARSTRING(p_entitlement_names[i]).get_data(); } } options.EntitlementNames = entitlementNames; options.bIncludeRedeemed = VARIANT_TO_EOS_BOOL(p_options->get("include_redeemed")); p_options->reference(); EOS_Ecom_QueryEntitlements(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryEntitlementsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("ecom_interface_query_entitlements_callback", ret); }); } void IEOS::ecom_interface_query_offers(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString override_catalog_namespace = VARIANT_TO_CHARSTRING(p_options->get("override_catalog_namespace")); EOS_Ecom_QueryOffersOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYOFFERS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); if (override_catalog_namespace.length() > 0) { options.OverrideCatalogNamespace = override_catalog_namespace.get_data(); } p_options->reference(); EOS_Ecom_QueryOffers(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryOffersCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("ecom_interface_query_offers_callback", ret); }); } void IEOS::ecom_interface_query_ownership(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString catalog_namespace = VARIANT_TO_CHARSTRING(p_options->get("catalog_namespace")); EOS_Ecom_QueryOwnershipOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYOWNERSHIP_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); if (catalog_namespace.length() > 0) { options.CatalogNamespace = catalog_namespace.get_data(); } TypedArray p_catalog_item_ids = p_options->get("catalog_item_ids"); options.CatalogItemIdCount = static_cast(p_catalog_item_ids.size()); EOS_Ecom_CatalogItemId *catalog_item_ids = nullptr; if (options.CatalogItemIdCount > 0) { catalog_item_ids = (EOS_Ecom_CatalogItemId *)memalloc(sizeof(EOS_Ecom_CatalogItemId) * p_catalog_item_ids.size()); for (int i = 0; i < options.CatalogItemIdCount; i++) { catalog_item_ids[i] = VARIANT_TO_CHARSTRING(p_catalog_item_ids[i]).get_data(); } } options.CatalogItemIds = catalog_item_ids; p_options->reference(); EOS_Ecom_QueryOwnership(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryOwnershipCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); Array item_ownership_array = Array(); for (int i = 0; i < data->ItemOwnershipCount; i++) { Dictionary item_ownership; item_ownership["id"] = EOSG_GET_STRING(data->ItemOwnership[i].Id); item_ownership["ownership_status"] = static_cast(data->ItemOwnership[i].OwnershipStatus); item_ownership_array.append(item_ownership); } ret["item_ownership"] = item_ownership_array; IEOS::get_singleton()->emit_signal("ecom_interface_query_ownership_callback", ret); }); } void IEOS::ecom_interface_query_ownership_token(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString catalog_namespace = VARIANT_TO_CHARSTRING(p_options->get("catalog_namespace")); TypedArray p_catalog_item_ids = p_options->get("catalog_item_ids"); EOS_Ecom_CatalogItemId *catalog_item_ids = nullptr; EOS_Ecom_QueryOwnershipTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYOWNERSHIPTOKEN_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); if (catalog_namespace.length() > 0) { options.CatalogNamespace = catalog_namespace.get_data(); } options.CatalogItemIdCount = static_cast(p_catalog_item_ids.size()); if (options.CatalogItemIdCount > 0) { catalog_item_ids = (EOS_Ecom_CatalogItemId *)memalloc(sizeof(EOS_Ecom_CatalogItemId) * options.CatalogItemIdCount); for (int i = 0; i < options.CatalogItemIdCount; i++) { catalog_item_ids[i] = VARIANT_TO_CHARSTRING(p_catalog_item_ids[i]).get_data(); } } options.CatalogItemIds = catalog_item_ids; p_options->reference(); EOS_Ecom_QueryOwnershipToken(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryOwnershipTokenCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["ownership_token"] = EOSG_GET_STRING(data->OwnershipToken); IEOS::get_singleton()->emit_signal("ecom_interface_query_ownership_token_callback", ret); }); } void IEOS::ecom_interface_redeem_entitlements(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); TypedArray p_entitlement_ids = p_options->get("entitlement_ids"); EOS_Ecom_EntitlementId *entitlement_ids = nullptr; EOS_Ecom_RedeemEntitlementsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_REDEEMENTITLEMENTS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.EntitlementIdCount = static_cast(p_entitlement_ids.size()); if (options.EntitlementIdCount > 0) { entitlement_ids = (EOS_Ecom_EntitlementId *)memalloc(sizeof(EOS_Ecom_EntitlementId) * p_entitlement_ids.size()); for (int i = 0; i < options.EntitlementIdCount; i++) { entitlement_ids[i] = VARIANT_TO_CHARSTRING(p_entitlement_ids[i]).get_data(); } } options.EntitlementIds = entitlement_ids; p_options->reference(); EOS_Ecom_RedeemEntitlements(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_RedeemEntitlementsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["redeemed_entitlement_ids_count"] = data->RedeemedEntitlementIdsCount; IEOS::get_singleton()->emit_signal("ecom_interface_redeem_entitlements_callback", ret); }); } int IEOS::ecom_interface_get_last_redeemed_entitlements_count(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_GetLastRedeemedEntitlementsCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_GETLASTREDEEMEDENTITLEMENTSCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Ecom_GetLastRedeemedEntitlementsCount(s_ecomInterface, &options)); } Dictionary IEOS::ecom_interface_copy_last_redeemed_entitlement_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_ecomInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_CopyLastRedeemedEntitlementByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_COPYLASTREDEEMEDENTITLEMENTBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.RedeemedEntitlementIndex = static_cast(p_options->get("redeemed_entitlement_index")); char *outEntitlementId = (char *)memalloc(EOS_ECOM_ENTITLEMENTID_MAX_LENGTH + 1); int outEntitlementIdLength = EOS_ECOM_ENTITLEMENTID_MAX_LENGTH + 1; EOS_EResult res = EOS_Ecom_CopyLastRedeemedEntitlementByIndex(s_ecomInterface, &options, outEntitlementId, &outEntitlementIdLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["entitlement_id"] = EOSG_GET_STRING(outEntitlementId); return ret; } void IEOS::ecom_interface_query_entitlement_token(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_QueryEntitlementTokenOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYENTITLEMENTTOKEN_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); TypedArray p_entitlement_names = p_options->get("entitlement_names"); options.EntitlementNameCount = static_cast(p_entitlement_names.size()); EOS_Ecom_EntitlementName *entitlementNames = nullptr; if (options.EntitlementNameCount > 0) { entitlementNames = (EOS_Ecom_EntitlementName *)memalloc(sizeof(EOS_Ecom_EntitlementName) * p_entitlement_names.size()); for (int i = 0; i < options.EntitlementNameCount; i++) { entitlementNames[i] = VARIANT_TO_CHARSTRING(p_entitlement_names[i]).get_data(); } } options.EntitlementNames = entitlementNames; p_options->reference(); EOS_Ecom_QueryEntitlementToken(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryEntitlementTokenCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["entitlement_token"] = EOSG_GET_STRING(data->EntitlementToken); IEOS::get_singleton()->emit_signal("ecom_interface_query_entitlement_token_callback", ret); }); } void IEOS::ecom_interface_query_ownership_by_sandbox_ids(Ref p_options) { ERR_FAIL_NULL(s_ecomInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Ecom_QueryOwnershipBySandboxIdsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_QUERYOWNERSHIPBYSANDBOXIDSOPTIONS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); TypedArray p_sandbox_ids = p_options->get("sandbox_ids"); options.SandboxIdsCount = static_cast(p_sandbox_ids.size()); EOS_Ecom_SandboxId *sandbox_ids = nullptr; if (options.SandboxIdsCount > 0) { sandbox_ids = (EOS_Ecom_SandboxId *)memalloc(sizeof(EOS_Ecom_SandboxId) * p_sandbox_ids.size()); for (int i = 0; i < options.SandboxIdsCount; i++) { sandbox_ids[i] = VARIANT_TO_CHARSTRING(p_sandbox_ids[i]).get_data(); } } options.SandboxIds = sandbox_ids; p_options->reference(); EOS_Ecom_QueryOwnershipBySandboxIds(s_ecomInterface, &options, (void *)*p_options, [](const EOS_Ecom_QueryOwnershipBySandboxIdsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); Array sandbox_id_item_ownership_array = Array(); for (int i = 0; i < data->SandboxIdItemOwnershipsCount; i++) { Dictionary sandbox_id_item_ownership; sandbox_id_item_ownership["sandbox_id"] = EOSG_GET_STRING(data->SandboxIdItemOwnerships[i].SandboxId); Array owned_catalog_item_ids_array = Array(); for (int j = 0; j < data->SandboxIdItemOwnerships[i].OwnedCatalogItemIdsCount; j++) { owned_catalog_item_ids_array.append(EOSG_GET_STRING(data->SandboxIdItemOwnerships[i].OwnedCatalogItemIds[j])); } sandbox_id_item_ownership["owned_catalog_item_ids"] = owned_catalog_item_ids_array; sandbox_id_item_ownership_array.append(sandbox_id_item_ownership); } ret["sandbox_id_item_ownership"] = sandbox_id_item_ownership_array; IEOS::get_singleton()->emit_signal("ecom_interface_query_ownership_by_sandbox_ids_callback", ret); }); } ================================================ FILE: src/eosg_active_session.cpp ================================================ #include "eosg_active_session.h" #include "utils.h" using namespace godot; void EOSGActiveSession::_bind_methods() { ClassDB::bind_method(D_METHOD("copy_info"), &EOSGActiveSession::copy_info); ClassDB::bind_method(D_METHOD("get_registered_player_count"), &EOSGActiveSession::get_registered_player_count); ClassDB::bind_method(D_METHOD("get_registered_player_by_index", "player_index"), &EOSGActiveSession::get_registered_player_by_index); } Dictionary EOSGActiveSession::copy_info() { ERR_FAIL_NULL_V(m_internal, {}); EOS_ActiveSession_CopyInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACTIVESESSION_COPYINFO_API_LATEST; EOS_ActiveSession_Info *outInfo = nullptr; EOS_EResult res = EOS_ActiveSession_CopyInfo(m_internal, &options, &outInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["info"] = eosg_sessions_active_session_info_to_dict_and_release(outInfo); return ret; } int EOSGActiveSession::get_registered_player_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_ActiveSession_GetRegisteredPlayerCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACTIVESESSION_GETREGISTEREDPLAYERCOUNT_API_LATEST; return static_cast(EOS_ActiveSession_GetRegisteredPlayerCount(m_internal, &options)); } String EOSGActiveSession::get_registered_player_by_index(int p_player_index) { ERR_FAIL_NULL_V(m_internal, ""); EOS_ActiveSession_GetRegisteredPlayerByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ACTIVESESSION_GETREGISTEREDPLAYERBYINDEX_API_LATEST; options.PlayerIndex = static_cast(p_player_index); EOS_ProductUserId userId = EOS_ActiveSession_GetRegisteredPlayerByIndex(m_internal, &options); return eosg_product_user_id_to_string(userId); } ================================================ FILE: src/eosg_active_session.h ================================================ #pragma once #include "eos_sessions.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGActiveSession : public RefCounted { GDCLASS(EOSGActiveSession, RefCounted) private: EOS_HActiveSession m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_info(); int get_registered_player_count(); String get_registered_player_by_index(int index); EOSGActiveSession(){}; ~EOSGActiveSession() { if (m_internal != nullptr) { EOS_ActiveSession_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HActiveSession p_internal) { m_internal = p_internal; } EOS_HActiveSession get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_continuance_token.h ================================================ #pragma once #include "eos_common.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGContinuanceToken : public RefCounted { GDCLASS(EOSGContinuanceToken, RefCounted) private: EOS_ContinuanceToken m_internal = nullptr; static void _bind_methods(){}; public: EOSGContinuanceToken(){}; ~EOSGContinuanceToken(){}; void set_internal(EOS_ContinuanceToken p_internal) { m_internal = p_internal; } EOS_ContinuanceToken get_internal() { return m_internal; } String to_string() { int32_t outBufferLength = 0; EOS_ContinuanceToken_ToString(m_internal, nullptr, &outBufferLength); // outBufferLength is set to required length char *outBuffer = (char *)(memalloc(outBufferLength + 1)); EOS_EResult res = EOS_ContinuanceToken_ToString(m_internal, outBuffer, &outBufferLength); if (res == EOS_EResult::EOS_Success) { return String("ContinuanceToken[") + String(outBuffer) + String("]"); } return "ContinuanceToken[to_string failed]"; } }; } // namespace godot ================================================ FILE: src/eosg_file_transfer_request.h ================================================ #pragma once #include "eosg_file_transfer_request.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGFileTransferRequest : public RefCounted { GDCLASS(EOSGFileTransferRequest, RefCounted) private: static void _bind_methods() { BIND_VIRTUAL_METHOD(EOSGFileTransferRequest, get_file_request_state); BIND_VIRTUAL_METHOD(EOSGFileTransferRequest, get_filename); BIND_VIRTUAL_METHOD(EOSGFileTransferRequest, cancel_request); }; public: // TODO: make these methods pure virtual virtual int get_file_request_state() { return -1; } virtual Dictionary get_filename() { return Dictionary(); } virtual int cancel_request() { return -1; } EOSGFileTransferRequest(){}; ~EOSGFileTransferRequest(){}; }; } // namespace godot ================================================ FILE: src/eosg_lobby_details.cpp ================================================ #include "eosg_lobby_details.h" #include "utils.h" using namespace godot; void EOSGLobbyDetails::_bind_methods() { ClassDB::bind_method(D_METHOD("get_lobby_owner"), &EOSGLobbyDetails::get_lobby_owner); ClassDB::bind_method(D_METHOD("copy_info"), &EOSGLobbyDetails::copy_info); ClassDB::bind_method(D_METHOD("get_attribute_count"), &EOSGLobbyDetails::get_attribute_count); ClassDB::bind_method(D_METHOD("copy_attribute_by_index", "index"), &EOSGLobbyDetails::copy_attribute_by_index); ClassDB::bind_method(D_METHOD("copy_attribute_by_key", "key"), &EOSGLobbyDetails::copy_attribute_by_key); ClassDB::bind_method(D_METHOD("get_member_count"), &EOSGLobbyDetails::get_member_count); ClassDB::bind_method(D_METHOD("get_member_by_index", "index"), &EOSGLobbyDetails::get_member_by_index); ClassDB::bind_method(D_METHOD("get_member_attribute_count", "target_user_id"), &EOSGLobbyDetails::get_member_attribute_count); ClassDB::bind_method(D_METHOD("copy_member_info", "target_user_id"), &EOSGLobbyDetails::copy_member_info); ClassDB::bind_method(D_METHOD("copy_member_attribute_by_index", "target_user_id", "index"), &EOSGLobbyDetails::copy_member_attribute_by_index); ClassDB::bind_method(D_METHOD("copy_member_attribute_by_key", "target_user_id", "key"), &EOSGLobbyDetails::copy_member_attribute_by_key); } String EOSGLobbyDetails::get_lobby_owner() { ERR_FAIL_NULL_V(m_internal, ""); EOS_LobbyDetails_GetLobbyOwnerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_GETLOBBYOWNER_API_LATEST; return eosg_product_user_id_to_string(EOS_LobbyDetails_GetLobbyOwner(m_internal, &options)); } Dictionary EOSGLobbyDetails::copy_info() { ERR_FAIL_NULL_V(m_internal, {}); EOS_LobbyDetails_CopyInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYINFO_API_LATEST; EOS_LobbyDetails_Info *outLobbyDetails = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyInfo(m_internal, &options, &outLobbyDetails); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_details"] = eosg_lobby_details_info_to_dict_and_release(outLobbyDetails); return ret; } int EOSGLobbyDetails::get_attribute_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_LobbyDetails_GetAttributeCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_GETATTRIBUTECOUNT_API_LATEST; return static_cast(EOS_LobbyDetails_GetAttributeCount(m_internal, &options)); } Dictionary EOSGLobbyDetails::copy_attribute_by_index(int p_index) { ERR_FAIL_NULL_V(m_internal, {}); EOS_LobbyDetails_CopyAttributeByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYATTRIBUTEBYINDEX_API_LATEST; options.AttrIndex = static_cast(p_index); EOS_Lobby_Attribute *outAttr = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyAttributeByIndex(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_lobby_attribute_to_dict_and_release(outAttr); return ret; } Dictionary EOSGLobbyDetails::copy_attribute_by_key(const String &p_key) { ERR_FAIL_NULL_V(m_internal, {}); CharString key = p_key.utf8(); EOS_LobbyDetails_CopyAttributeByKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYATTRIBUTEBYKEY_API_LATEST; options.AttrKey = key.get_data(); EOS_Lobby_Attribute *outAttr = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyAttributeByKey(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_lobby_attribute_to_dict_and_release(outAttr); return ret; } int EOSGLobbyDetails::get_member_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_LobbyDetails_GetMemberCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERCOUNT_API_LATEST; return static_cast(EOS_LobbyDetails_GetMemberCount(m_internal, &options)); } String EOSGLobbyDetails::get_member_by_index(int p_index) { ERR_FAIL_NULL_V(m_internal, ""); EOS_LobbyDetails_GetMemberByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERBYINDEX_API_LATEST; options.MemberIndex = static_cast(p_index); return eosg_product_user_id_to_string(EOS_LobbyDetails_GetMemberByIndex(m_internal, &options)); } int EOSGLobbyDetails::get_member_attribute_count(const String &p_target_user_id) { ERR_FAIL_NULL_V(m_internal, 0); CharString targetUserId = p_target_user_id.utf8(); EOS_LobbyDetails_GetMemberAttributeCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_GETMEMBERATTRIBUTECOUNT_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(targetUserId.get_data()); return static_cast(EOS_LobbyDetails_GetMemberAttributeCount(m_internal, &options)); } Dictionary EOSGLobbyDetails::copy_member_attribute_by_index(const String &p_target_user_id, int p_index) { ERR_FAIL_NULL_V(m_internal, {}); CharString targetUserId = p_target_user_id.utf8(); EOS_LobbyDetails_CopyMemberAttributeByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYMEMBERATTRIBUTEBYINDEX_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(targetUserId.get_data()); options.AttrIndex = static_cast(p_index); EOS_Lobby_Attribute *outAttr = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyMemberAttributeByIndex(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_lobby_attribute_to_dict_and_release(outAttr); return ret; } Dictionary EOSGLobbyDetails::copy_member_attribute_by_key(const String &p_target_user_id, const String &p_key) { ERR_FAIL_NULL_V(m_internal, {}); CharString targetUserId = p_target_user_id.utf8(); CharString key = p_key.utf8(); EOS_LobbyDetails_CopyMemberAttributeByKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYMEMBERATTRIBUTEBYKEY_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(targetUserId.get_data()); options.AttrKey = key.get_data(); EOS_Lobby_Attribute *outAttr = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyMemberAttributeByKey(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_lobby_attribute_to_dict_and_release(outAttr); return ret; } Dictionary EOSGLobbyDetails::copy_member_info(const String &p_target_user_id) { ERR_FAIL_NULL_V(m_internal, {}); CharString targetUserId = p_target_user_id.utf8(); EOS_LobbyDetails_CopyMemberInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYDETAILS_COPYMEMBERINFO_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(targetUserId.get_data()); EOS_LobbyDetails_MemberInfo *outMemberInfo = nullptr; EOS_EResult res = EOS_LobbyDetails_CopyMemberInfo(m_internal, &options, &outMemberInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["member_info"] = eosg_lobby_details_member_info_to_dict_and_release(outMemberInfo); return ret; } ================================================ FILE: src/eosg_lobby_details.h ================================================ #pragma once #include "eos_lobby.h" #include "godot_cpp/classes/ref_counted.hpp" #include "godot_cpp/variant/dictionary.hpp" namespace godot { class EOSGLobbyDetails : public RefCounted { GDCLASS(EOSGLobbyDetails, RefCounted) private: EOS_HLobbyDetails m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_attribute_by_index(int index); Dictionary copy_attribute_by_key(const String &key); Dictionary copy_info(); Dictionary copy_member_attribute_by_index(const String &target_user_id, int index); Dictionary copy_member_attribute_by_key(const String &target_user_id, const String &key); Dictionary copy_member_info(const String &target_user_id); int get_attribute_count(); int get_member_attribute_count(const String &target_user_id); int get_member_count(); String get_lobby_owner(); String get_member_by_index(int index); EOSGLobbyDetails(){}; ~EOSGLobbyDetails() { if (m_internal != nullptr) { EOS_LobbyDetails_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HLobbyDetails p_internal) { m_internal = p_internal; } EOS_HLobbyDetails get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_lobby_modification.cpp ================================================ #include "eosg_lobby_modification.h" #include "utils.h" using namespace godot; void EOSGLobbyModification::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bucket_id", "bucket_id"), &EOSGLobbyModification::set_bucket_id); ClassDB::bind_method(D_METHOD("set_permission_level", "permission_level"), &EOSGLobbyModification::set_permission_level); ClassDB::bind_method(D_METHOD("set_max_members", "max_members"), &EOSGLobbyModification::set_max_members); ClassDB::bind_method(D_METHOD("set_invites_allowed", "invites_allowed"), &EOSGLobbyModification::set_invites_allowed); ClassDB::bind_method(D_METHOD("add_attribute", "key", "value", "visibility"), &EOSGLobbyModification::add_attribute); ClassDB::bind_method(D_METHOD("remove_attribute", "key"), &EOSGLobbyModification::remove_attribute); ClassDB::bind_method(D_METHOD("add_member_attribute", "key", "value", "visibility"), &EOSGLobbyModification::add_member_attribute); ClassDB::bind_method(D_METHOD("remove_member_attribute", "key"), &EOSGLobbyModification::remove_member_attribute); ClassDB::bind_method(D_METHOD("set_allowed_platform_ids", "allowed_platform_ids"), &EOSGLobbyModification::set_allowed_platform_ids); } int EOSGLobbyModification::set_bucket_id(const String &p_bucket_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString bucket_id = p_bucket_id.utf8(); EOS_LobbyModification_SetBucketIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_SETBUCKETID_API_LATEST; options.BucketId = bucket_id.get_data(); return static_cast(EOS_LobbyModification_SetBucketId(m_internal, &options)); } int EOSGLobbyModification::set_permission_level(int p_permission_level) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_LobbyModification_SetPermissionLevelOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_SETPERMISSIONLEVEL_API_LATEST; options.PermissionLevel = static_cast(p_permission_level); return static_cast(EOS_LobbyModification_SetPermissionLevel(m_internal, &options)); } int EOSGLobbyModification::set_max_members(int p_max_members) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_LobbyModification_SetMaxMembersOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_SETMAXMEMBERS_API_LATEST; options.MaxMembers = static_cast(p_max_members); return static_cast(EOS_LobbyModification_SetMaxMembers(m_internal, &options)); } int EOSGLobbyModification::set_invites_allowed(bool p_invites_allowed) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_LobbyModification_SetInvitesAllowedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_SETINVITESALLOWED_API_LATEST; options.bInvitesAllowed = p_invites_allowed ? EOS_TRUE : EOS_FALSE; return static_cast(EOS_LobbyModification_SetInvitesAllowed(m_internal, &options)); } int EOSGLobbyModification::add_attribute(const String &p_key, Variant p_value, int p_visibility) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); CharString value; EOS_Lobby_AttributeData attributeData; memset(&attributeData, 0, sizeof(attributeData)); attributeData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; attributeData.Key = key.get_data(); if (p_value.get_type() == Variant::Type::BOOL) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_BOOLEAN; attributeData.Value.AsBool = p_value; } else if (p_value.get_type() == Variant::Type::INT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; attributeData.Value.AsInt64 = p_value; } else if (p_value.get_type() == Variant::Type::FLOAT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_DOUBLE; attributeData.Value.AsDouble = p_value; } else if (p_value.get_type() == Variant::Type::STRING) { value = VARIANT_TO_CHARSTRING(p_value); attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; attributeData.Value.AsUtf8 = value.get_data(); } EOS_LobbyModification_AddAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_ADDATTRIBUTE_API_LATEST; options.Attribute = &attributeData; options.Visibility = static_cast(p_visibility); return static_cast(EOS_LobbyModification_AddAttribute(m_internal, &options)); } int EOSGLobbyModification::remove_attribute(const String &p_key) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_LobbyModification_RemoveAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_REMOVEATTRIBUTE_API_LATEST; options.Key = key.get_data(); return static_cast(EOS_LobbyModification_RemoveAttribute(m_internal, &options)); } int EOSGLobbyModification::add_member_attribute(const String &p_key, Variant p_value, int p_visibility) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_Lobby_AttributeData attributeData; memset(&attributeData, 0, sizeof(attributeData)); attributeData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; attributeData.Key = key.get_data(); CharString value; if (p_value.get_type() == Variant::Type::BOOL) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_BOOLEAN; attributeData.Value.AsBool = p_value; } else if (p_value.get_type() == Variant::Type::INT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; attributeData.Value.AsInt64 = p_value; } else if (p_value.get_type() == Variant::Type::FLOAT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_DOUBLE; attributeData.Value.AsDouble = p_value; } else if (p_value.get_type() == Variant::Type::STRING) { value = VARIANT_TO_CHARSTRING(p_value); attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; attributeData.Value.AsUtf8 = value.get_data(); } EOS_LobbyModification_AddMemberAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_ADDMEMBERATTRIBUTE_API_LATEST; options.Attribute = &attributeData; options.Visibility = static_cast(p_visibility); return static_cast(EOS_LobbyModification_AddMemberAttribute(m_internal, &options)); } int EOSGLobbyModification::remove_member_attribute(const String &p_key) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_LobbyModification_RemoveMemberAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_REMOVEMEMBERATTRIBUTE_API_LATEST; options.Key = key.get_data(); return static_cast(EOS_LobbyModification_RemoveMemberAttribute(m_internal, &options)); } int EOSGLobbyModification::set_allowed_platform_ids(const TypedArray &p_platform_ids) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_LobbyModification_SetAllowedPlatformIdsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYMODIFICATION_SETALLOWEDPLATFORMIDS_API_LATEST; options.AllowedPlatformIdsCount = p_platform_ids.size(); uint32_t *platform_ids_array = (uint32_t *)memalloc(sizeof(uint32_t) * options.AllowedPlatformIdsCount); for (int i = 0; i < options.AllowedPlatformIdsCount; i++) { platform_ids_array[i] = static_cast(p_platform_ids[i]); } options.AllowedPlatformIds = platform_ids_array; int result = static_cast(EOS_LobbyModification_SetAllowedPlatformIds(m_internal, &options)); memfree(platform_ids_array); return result; } ================================================ FILE: src/eosg_lobby_modification.h ================================================ #pragma once #include "eos_lobby.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGLobbyModification : public RefCounted { GDCLASS(EOSGLobbyModification, RefCounted) private: EOS_HLobbyModification m_internal = nullptr; static void _bind_methods(); public: int add_attribute(const String &key, Variant value, int visibility); int add_member_attribute(const String &key, Variant value, int visibility); int remove_attribute(const String &key); int remove_member_attribute(const String &key); int set_allowed_platform_ids(const TypedArray &platform_ids); int set_bucket_id(const String &bucket_id); int set_invites_allowed(bool invites_allowed); int set_max_members(int max_members); int set_permission_level(int permission_level); EOSGLobbyModification(){}; ~EOSGLobbyModification() { if (m_internal != nullptr) { EOS_LobbyModification_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HLobbyModification p_internal) { m_internal = p_internal; } EOS_HLobbyModification get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_lobby_search.cpp ================================================ #include "eosg_lobby_search.h" #include "utils.h" using namespace godot; void EOSGLobbySearch::_bind_methods() { ClassDB::bind_method(D_METHOD("find", "local_user_id"), &EOSGLobbySearch::find); ClassDB::bind_method(D_METHOD("set_lobby_id", "lobby_id"), &EOSGLobbySearch::set_lobby_id); ClassDB::bind_method(D_METHOD("set_target_user_id", "target_user_id"), &EOSGLobbySearch::set_target_user_id); ClassDB::bind_method(D_METHOD("set_parameter", "key", "value", "comparison_op"), &EOSGLobbySearch::set_parameter); ClassDB::bind_method(D_METHOD("remove_parameter", "key", "comparison_op"), &EOSGLobbySearch::remove_parameter); ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &EOSGLobbySearch::set_max_results); ClassDB::bind_method(D_METHOD("get_search_result_count"), &EOSGLobbySearch::get_search_result_count); ClassDB::bind_method(D_METHOD("copy_search_result_by_index", "index"), &EOSGLobbySearch::copy_search_result_by_index); } void EOSGLobbySearch::find(const String &p_local_user_id) { ERR_FAIL_NULL(m_internal); CharString local_user_id = p_local_user_id.utf8(); EOS_LobbySearch_FindOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_FIND_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); // TODO: Handle passing ClientData if needed EOS_LobbySearch_Find(m_internal, &options, nullptr, [](const EOS_LobbySearch_FindCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); ret["client_data"] = Variant(); IEOS::get_singleton()->emit_signal("lobby_search_find_callback", ret); }); } int EOSGLobbySearch::set_lobby_id(const String &p_lobby_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString lobby_id = p_lobby_id.utf8(); EOS_LobbySearch_SetLobbyIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_SETLOBBYID_API_LATEST; options.LobbyId = lobby_id.get_data(); return static_cast(EOS_LobbySearch_SetLobbyId(m_internal, &options)); } int EOSGLobbySearch::set_target_user_id(const String &p_target_user_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString target_user_id = p_target_user_id.utf8(); EOS_LobbySearch_SetTargetUserIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_SETTARGETUSERID_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); return static_cast(EOS_LobbySearch_SetTargetUserId(m_internal, &options)); } int EOSGLobbySearch::set_parameter(const String &p_key, Variant p_value, int p_comparison_op) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_Lobby_AttributeData attributeData; memset(&attributeData, 0, sizeof(attributeData)); attributeData.ApiVersion = EOS_LOBBY_ATTRIBUTEDATA_API_LATEST; attributeData.Key = key.get_data(); CharString value; if (p_value.get_type() == Variant::Type::BOOL) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_BOOLEAN; attributeData.Value.AsBool = p_value; } else if (p_value.get_type() == Variant::Type::INT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_INT64; attributeData.Value.AsInt64 = p_value; } else if (p_value.get_type() == Variant::Type::FLOAT) { attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_DOUBLE; attributeData.Value.AsDouble = p_value; } else if (p_value.get_type() == Variant::Type::STRING) { value = VARIANT_TO_CHARSTRING(p_value); attributeData.ValueType = EOS_ELobbyAttributeType::EOS_AT_STRING; attributeData.Value.AsUtf8 = value.get_data(); } EOS_LobbySearch_SetParameterOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_SETPARAMETER_API_LATEST; options.ComparisonOp = static_cast(p_comparison_op); options.Parameter = &attributeData; return static_cast(EOS_LobbySearch_SetParameter(m_internal, &options)); } int EOSGLobbySearch::remove_parameter(const String &p_key, int p_comparison_op) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_LobbySearch_RemoveParameterOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_REMOVEPARAMETER_API_LATEST; options.Key = key.get_data(); options.ComparisonOp = static_cast(p_comparison_op); return static_cast(EOS_LobbySearch_RemoveParameter(m_internal, &options)); } int EOSGLobbySearch::set_max_results(int p_max_results) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_LobbySearch_SetMaxResultsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_SETMAXRESULTS_API_LATEST; options.MaxResults = static_cast(p_max_results); return static_cast(EOS_LobbySearch_SetMaxResults(m_internal, &options)); } int EOSGLobbySearch::get_search_result_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_LobbySearch_GetSearchResultCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_GETSEARCHRESULTCOUNT_API_LATEST; return static_cast(EOS_LobbySearch_GetSearchResultCount(m_internal, &options)); } Dictionary EOSGLobbySearch::copy_search_result_by_index(int p_index) { ERR_FAIL_NULL_V(m_internal, {}); EOS_LobbySearch_CopySearchResultByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBYSEARCH_COPYSEARCHRESULTBYINDEX_API_LATEST; options.LobbyIndex = static_cast(p_index); EOS_HLobbyDetails outLobbyDetails = nullptr; EOS_EResult res = EOS_LobbySearch_CopySearchResultByIndex(m_internal, &options, &outLobbyDetails); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_details"] = eosg_lobby_lobby_details_to_wrapper(outLobbyDetails); return ret; } ================================================ FILE: src/eosg_lobby_search.h ================================================ #pragma once #include "eos_lobby.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGLobbySearch : public RefCounted { GDCLASS(EOSGLobbySearch, RefCounted) private: EOS_HLobbySearch m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_search_result_by_index(int index); int get_search_result_count(); int remove_parameter(const String &key, int comparison_op); int set_lobby_id(const String &lobby_id); int set_max_results(int max_results); int set_parameter(const String &key, Variant value, int comparison_op); int set_target_user_id(const String &target_user_id); void find(const String &local_user_id); EOSGLobbySearch(){}; ~EOSGLobbySearch() { if (m_internal != nullptr) { EOS_LobbySearch_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HLobbySearch p_internal) { m_internal = p_internal; } EOS_HLobbySearch get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_multiplayer_peer.cpp ================================================ /**************************************** * EOSGMultiplayerPeer * Author: Dallin Lovin aka LowFire * Description: Multiplayer peer that uses the EOS P2P Interface to allow users to * establish direct peer-to-peer connections with other players over the internet. * NAT punchthrough is handled automatically by the P2P interface and a relay server * is used if direct connection is not possible. EOSGMultiplayerPeer allows users to * create either a client, server, or mesh instance. A server opens a socket and actively listens * for incoming connection requests. When a connection request is received, a server can either * accept or reject the connection request. Servers auto accept connection requests by default. * Clients can only connect to a server instance and cannot accept connection requests * from anyone. Clients are only allowed to be connected to a single peer and that peer * must be a server. Mesh instances can arbitrarily connect to each other using a common * socket id. They can also connect to a server instance. The socket id must be the same on * all mesh instances if they are to connect to each other. Once connected, mesh instances can * send packets to each other just like a server and clients, but those packets will not be relayed. A mesh instance can only send * packets to peers they are directly connected to. ****************************************/ #include "eosg_multiplayer_peer.h" #include "eosg_packet_peer_mediator.h" #include "ieos.h" using namespace godot; EOS_ProductUserId EOSGMultiplayerPeer::s_local_user_id = nullptr; void EOSGMultiplayerPeer::_bind_methods() { ClassDB::bind_static_method("EOSGMultiplayerPeer", D_METHOD("get_local_user_id"), &EOSGMultiplayerPeer::get_local_user_id); ClassDB::bind_method(D_METHOD("create_server", "socket_id"), &EOSGMultiplayerPeer::create_server); ClassDB::bind_method(D_METHOD("create_client", "socket_id", "remote_user_id"), &EOSGMultiplayerPeer::create_client); ClassDB::bind_method(D_METHOD("create_mesh", "socket_id"), &EOSGMultiplayerPeer::create_mesh); ClassDB::bind_method(D_METHOD("add_mesh_peer", "remote_user_id"), &EOSGMultiplayerPeer::add_mesh_peer); ClassDB::bind_method(D_METHOD("get_socket"), &EOSGMultiplayerPeer::get_socket); ClassDB::bind_method(D_METHOD("get_peer_id", "remote_user_id"), &EOSGMultiplayerPeer::get_peer_id); ClassDB::bind_method(D_METHOD("set_allow_delayed_delivery", "allow"), &EOSGMultiplayerPeer::set_allow_delayed_delivery); ClassDB::bind_method(D_METHOD("is_allowing_delayed_delivery"), &EOSGMultiplayerPeer::is_allowing_delayed_delivery); ClassDB::bind_method(D_METHOD("is_auto_accepting_connection_requests"), &EOSGMultiplayerPeer::is_auto_accepting_connection_requests); ClassDB::bind_method(D_METHOD("set_auto_accept_connection_requests", "enable"), &EOSGMultiplayerPeer::set_auto_accept_connection_requests); ClassDB::bind_method(D_METHOD("get_all_connection_requests"), &EOSGMultiplayerPeer::get_all_connection_requests); ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &EOSGMultiplayerPeer::has_peer); ClassDB::bind_method(D_METHOD("has_user_id", "remote_user_id"), &EOSGMultiplayerPeer::has_user_id); ClassDB::bind_method(D_METHOD("accept_connection_request", "remote_user_id"), &EOSGMultiplayerPeer::accept_connection_request); ClassDB::bind_method(D_METHOD("deny_connection_request", "remote_user_id"), &EOSGMultiplayerPeer::deny_connection_request); ClassDB::bind_method(D_METHOD("accept_all_connection_requests"), &EOSGMultiplayerPeer::accept_all_connection_requests); ClassDB::bind_method(D_METHOD("deny_all_connection_requests"), &EOSGMultiplayerPeer::deny_all_connection_requests); ClassDB::bind_method(D_METHOD("get_active_mode"), &EOSGMultiplayerPeer::get_active_mode); ClassDB::bind_method(D_METHOD("get_all_peers"), &EOSGMultiplayerPeer::get_all_peers); ClassDB::bind_method(D_METHOD("get_peer_user_id", "peer_id"), &EOSGMultiplayerPeer::get_peer_user_id); ClassDB::bind_method(D_METHOD("is_polling"), &EOSGMultiplayerPeer::is_polling); ClassDB::bind_method(D_METHOD("set_is_polling", "polling"), &EOSGMultiplayerPeer::set_is_polling); ADD_SIGNAL(MethodInfo("peer_connection_established", PropertyInfo(Variant::DICTIONARY, "callback_data"))); ADD_SIGNAL(MethodInfo("peer_connection_interrupted", PropertyInfo(Variant::DICTIONARY, "callback_data"))); ADD_SIGNAL(MethodInfo("peer_connection_closed", PropertyInfo(Variant::DICTIONARY, "callback_data"))); ADD_SIGNAL(MethodInfo("incoming_connection_request", PropertyInfo(Variant::DICTIONARY, "callback_data"))); } /**************************************** * create_server * Parameters: * socket_id - The socket id the server uses to listen for connection requests. * Description: Creates a server instance using the given socket id. ****************************************/ Error EOSGMultiplayerPeer::create_server(const String &socket_id) { ERR_FAIL_NULL_V_MSG(s_local_user_id, ERR_UNCONFIGURED, "Failed to create server. Local user id has not been set."); ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "Failed to create server. Multiplayer instance is already active."); unique_id = 1; active_mode = MODE_SERVER; connection_status = CONNECTION_CONNECTED; polling = true; set_refuse_new_connections(false); //Create a socket where we will be listening for connections socket = EOSGSocket(socket_id); if (socket.get_name().is_empty()) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create server."); } bool success = EOSGPacketPeerMediator::get_singleton()->register_peer(this); if (!success) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create server."); } return OK; } /**************************************** * create_client * Parameters: * socket_id - The socket id of the server the client is trying to connect to. * remote_user_id - The user id of the server the client is trying to connect to. * Description: Creates a client instance and sends a connection request to the server using * the socket id and remote user id. Clients are only allowed to connect to servers. If the * remote user is another client or a mesh instance, connection will fail. ****************************************/ Error EOSGMultiplayerPeer::create_client(const String &socket_id, const String &remote_user_id) { ERR_FAIL_NULL_V_MSG(s_local_user_id, ERR_UNCONFIGURED, "Failed to create client. Local user id has not been set."); ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "Failed to create client. Multiplayer instance is already active."); polling = true; set_refuse_new_connections(false); //Create the socket we are trying to connect to socket = EOSGSocket(socket_id); if (socket.get_name().is_empty()) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create client."); } bool success = EOSGPacketPeerMediator::get_singleton()->register_peer(this); if (!success) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create client."); } unique_id = generate_unique_id(); active_mode = MODE_CLIENT; connection_status = CONNECTION_CONNECTING; //send connection request to server EOSGPacket packet; packet.set_event(EVENT_RECIEVE_PEER_ID); packet.set_channel(CH_RELIABLE); packet.set_reliability(EOS_EPacketReliability::EOS_PR_ReliableUnordered); packet.set_sender(unique_id); packet.prepare(); //send peer id to the server Error result = _send_to(eosg_string_to_product_user_id(remote_user_id.utf8()), packet); if (result != OK) { _close(); return result; } return OK; } /**************************************** * create_mesh * Parameters: * socket_id - The socket id the mesh instance uses to listen for connection requests. * Description: Creates a mesh instance using the given socket id. Other mesh instances can send * connection requests to this instance using the socket id. ****************************************/ Error EOSGMultiplayerPeer::create_mesh(const String &socket_id) { ERR_FAIL_NULL_V_MSG(s_local_user_id, ERR_UNCONFIGURED, "Failed to create mesh. Local user id has not been set."); ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "Failed to create mesh. Multiplayer instance is already active."); unique_id = generate_unique_id(); active_mode = MODE_MESH; connection_status = CONNECTION_CONNECTED; polling = true; //Create a socket where we will be listening for connections socket = EOSGSocket(socket_id); if (socket.get_name().is_empty()) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create mesh."); } bool success = EOSGPacketPeerMediator::get_singleton()->register_peer(this); if (!success) { _close(); ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Failed to create mesh."); } return OK; } /**************************************** * add_mesh_peer * Parameters: * remote_user_id - The remote user id of the other mesh instance this mesh instance is trying to * connect to. * Description: Sends a connection request to another mesh instance using the remote user id. If the connection * request is accepted, the other instance is added as a peer. ****************************************/ Error EOSGMultiplayerPeer::add_mesh_peer(const String &remote_user_id) { ERR_FAIL_NULL_V_MSG(s_local_user_id, ERR_UNCONFIGURED, "Failed to add mesh peer. Local user id has not been set."); ERR_FAIL_COND_V_MSG(active_mode != MODE_MESH, ERR_UNCONFIGURED, "Failed to add mesh peer. Multiplayer instance is not in mesh mode."); ERR_FAIL_COND_V_MSG(has_user_id(remote_user_id), ERR_ALREADY_EXISTS, "Failed to add mesh peer. Already connected to peer"); //send connection request to peer EOSGPacket packet; packet.set_event(EVENT_MESH_CONNECTION_REQUEST); packet.set_channel(CH_RELIABLE); packet.set_reliability(EOS_EPacketReliability::EOS_PR_ReliableUnordered); packet.set_sender(unique_id); packet.prepare(); _send_to(eosg_string_to_product_user_id(remote_user_id.utf8()), packet); return OK; } /**************************************** * get_socket * Description: Returns the socket id of this multiplayer instance. ****************************************/ String EOSGMultiplayerPeer::get_socket() const { return socket.get_name(); } /**************************************** * get_all_connection_requests * Description: Returns all connection requests currently pending. Returns an empty * array if there are none. ****************************************/ Array EOSGMultiplayerPeer::get_all_connection_requests() { Array ret = Array(); for (const EOS_ProductUserId &remote_user : pending_connection_requests) { ret.push_back(eosg_product_user_id_to_string(remote_user)); } return ret; } /**************************************** * get_peer_user_id * Parameters: * p_id - The peer id of the peer to return the user id of. * Description: Returns the remote user id of the given peer. Returns an empty string * if the peer could not be found. ****************************************/ String EOSGMultiplayerPeer::get_peer_user_id(int p_id) { String ret = ""; if (!peers.has(p_id)) { return ret; } EOS_ProductUserId user_id = peers.get(p_id); ret = eosg_product_user_id_to_string(user_id); return ret; } /**************************************** * get_peer_id * Parameters: * remote_user_id - The remote user id used to find a peer. * Description: Retrieves the peer id of a peer using their remote user id. Returns 0 * if no peer with a matching remote user id could be found. ****************************************/ int EOSGMultiplayerPeer::get_peer_id(const String &remote_user_id) { for (KeyValue &E : peers) { EOS_ProductUserId user_id = E.value; String user_id_str = eosg_product_user_id_to_string(user_id); if (remote_user_id == user_id_str) { return E.key; } } return 0; } /**************************************** * get_peer_id * Parameters: * peer_id - The peer id of the peer to find. * Description: Returns whether or not this multiplayer instance has the given * peer. ****************************************/ bool EOSGMultiplayerPeer::has_peer(int peer_id) { return peers.has(peer_id); } /**************************************** * has_user_id * Parameters: * remote_user_id - The remote user id to look for. * Description: Returns whether or not this multiplayer instance has a peer with the given * remote user id. ****************************************/ bool EOSGMultiplayerPeer::has_user_id(const String &remote_user_id) { for (KeyValue &E : peers) { String peer_user_id = eosg_product_user_id_to_string(E.value); if (remote_user_id != peer_user_id) continue; return true; } return false; } /**************************************** * _clear_peer_packet_queue * Parameters: * peer_id - The peer id of the peer to clear the packet from. * Description: Clears all packets sent by the given peer. ****************************************/ void EOSGMultiplayerPeer::_clear_peer_packet_queue(int p_id) { ERR_FAIL_COND_MSG(!peers.has(p_id), "Failed to clear packet queue for peer. Peer was not found."); socket.clear_packets_from_peer(p_id); String remote_user_id = eosg_product_user_id_to_string(peers[p_id]); EOSGPacketPeerMediator::get_singleton()->clear_packets_from_remote_user(socket.get_name(), remote_user_id); } /**************************************** * get_all_peers * Description: Gets all connected peers. Returns a dictionary, using the peer id as key * and the peer's remote user id as value. ****************************************/ Dictionary EOSGMultiplayerPeer::get_all_peers() { Dictionary ret; for (KeyValue &E : peers) { EOS_ProductUserId user_id = E.value; ret[E.key] = eosg_product_user_id_to_string(user_id); } return ret; } /**************************************** * set_allowed_delayed_delivery * Parameters: * allow - bool used to set allowed delivery. * Description: Set whether or not delayed delivery should be allowed on all sent packets. * This means that packets will be delayed in their delivery if a connenetion with the other * peer has not been estalished yet, otherwise the packets will be dropped. ****************************************/ void EOSGMultiplayerPeer::set_allow_delayed_delivery(bool allow) { allow_delayed_delivery = allow; } /**************************************** * is_allowing_delayed_delivery * Description: Returns whether or not delayed deliver is turned on. ****************************************/ bool EOSGMultiplayerPeer::is_allowing_delayed_delivery() { return allow_delayed_delivery; } /**************************************** * set_auto_accept_connection_requests * Parameters: * enable - bool used to set auto accept connection requests. * Description: Set whether or not to auto accept connection requests when they are * recieved. If this is off, connection requests are put into a list to be accepted * or denied later if desired. ****************************************/ void EOSGMultiplayerPeer::set_auto_accept_connection_requests(bool enable) { auto_accept_connection_requests = enable; } /**************************************** * is_auto_accepting_connection_requests * Description: Returns whether or not auto accepting connection requests is on. ****************************************/ bool EOSGMultiplayerPeer::is_auto_accepting_connection_requests() { return auto_accept_connection_requests; } /**************************************** * accept_connection_request * Parameters: * remote_user - The remote user id of the peer to accept a connection request from. * Description: Accepts a connection request from the given peer. If the connection request is accepted, * the peer will establish a connection with this multiplayer instance and peer id's will be exchanged. * The method does nothing if no connection request could be found using the given remote user id. ****************************************/ void EOSGMultiplayerPeer::accept_connection_request(const String &remote_user) { ERR_FAIL_COND_MSG(active_mode == MODE_NONE, "Cannot accept connection requests when multiplayer instance is not active."); ERR_FAIL_COND_MSG(active_mode == MODE_CLIENT, "Clients are not allowed to accept connection requests."); ERR_FAIL_NULL_MSG(s_local_user_id, "Cannot accept connection requests. Local user id has not been set."); EOS_ProductUserId remote_user_id; if (!_find_connection_request(remote_user, remote_user_id)) return; EOS_P2P_AcceptConnectionOptions options; options.ApiVersion = EOS_P2P_ACCEPTCONNECTION_API_LATEST; options.LocalUserId = s_local_user_id; options.RemoteUserId = remote_user_id; options.SocketId = socket.get_id(); EOS_EResult result = IEOS::get_singleton()->_p2p_accept_connection(&options); ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_InvalidParameters, "Failed to accept connection request! Invalid parameters."); pending_connection_requests.erase(remote_user_id); } /**************************************** * deny_connection_request * Parameters: * remote_user - The remote user id of the peer to deny a connection request from. * Description: Denies a connection request from the given peer. The connection request is removed * from the list of pending connection requests when denied and the connection is closed with the peer. ****************************************/ void EOSGMultiplayerPeer::deny_connection_request(const String &remote_user) { ERR_FAIL_NULL_MSG(s_local_user_id, "Failed to deny connection request. Local user id not set"); if (active_mode == MODE_NONE || active_mode == MODE_CLIENT) return; EOS_ProductUserId remote_user_id; if (!_find_connection_request(remote_user, remote_user_id)) return; EOS_P2P_CloseConnectionOptions options; options.ApiVersion = EOS_P2P_CLOSECONNECTION_API_LATEST; options.LocalUserId = s_local_user_id; options.RemoteUserId = remote_user_id; options.SocketId = socket.get_id(); EOS_EResult result = IEOS::get_singleton()->_p2p_close_connection(&options); ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_InvalidParameters, "Failed to deny connection. Invalid parameters."); pending_connection_requests.erase(remote_user_id); } /**************************************** * accept_all_connection_requests * Description: Accepts all connection requests currently pending. ****************************************/ void EOSGMultiplayerPeer::accept_all_connection_requests() { if (pending_connection_requests.size() == 0) return; ERR_FAIL_COND_MSG(active_mode == MODE_NONE, "Cannot accept connection requests when multiplayer instance is not active."); ERR_FAIL_COND_MSG(active_mode == MODE_CLIENT, "Clients are not allowed to accept connection requests."); ERR_FAIL_NULL_MSG(s_local_user_id, "Cannot accept connection requests. Local user id has not been set."); for (const EOS_ProductUserId &remote_user_id : pending_connection_requests) { String remote_user_id_str = eosg_product_user_id_to_string(remote_user_id); accept_connection_request(remote_user_id_str); } } /**************************************** * deny_all_connection_requests * Description: Denies all connection requests currently pending. ****************************************/ void EOSGMultiplayerPeer::deny_all_connection_requests() { ERR_FAIL_NULL_MSG(s_local_user_id, "Failed to deny connection requests. Local user id not set"); if (active_mode == MODE_NONE || active_mode == MODE_CLIENT) return; for (const EOS_ProductUserId &remote_user_id : pending_connection_requests) { String remote_user_id_str = eosg_product_user_id_to_string(remote_user_id); deny_connection_request(remote_user_id_str); } pending_connection_requests.clear(); } /**************************************** * get_active_mode * Description: Returns the active mode of the multiplayer peer, which could be either be MODE_CLIENT, * MODE_SERVER, MODE_MESH, or MODE_NONE. MODE_NONE is returned if the multiplayer instance is not * currently active. ****************************************/ int EOSGMultiplayerPeer::get_active_mode() { return static_cast(active_mode); } /**************************************** * _get_packet * Parameters: * r_buffer - out parameter that returns packet's raw byte data. * r_buffer_size - out parameter that returns the packet's total size in bytes. * Description: This method is called by the MultiplayerAPI when it tries to poll the next available packet * from this multiplayer instance. The packet data and the packet size is returned to the MultiplayerAPI using * the r_buffer and r_buffer_size out parameters. ****************************************/ Error EOSGMultiplayerPeer::_get_packet(const uint8_t **r_buffer, int32_t *r_buffer_size) { current_packet = socket.pop_packet(); *r_buffer = current_packet.get_payload(); *r_buffer_size = current_packet.payload_size(); return OK; } /**************************************** * _put_packet * Parameters: * r_buffer - The packet's raw byte data being sent by the MultiplayerAPI * r_buffer_size - The size in bytes of the packet being sent. * Description: This method is called by the MultiplayerAPI when it tries to send a packet to * a connected peer. The packet's event type, transfer mode, and sender's peer id are put into * a packet header before sending. The payload, which is the data being sent by the MultiplayerAPI, * is appended after the packet header. Once the packet has been prepared, it is sent to the intended * peer. ****************************************/ Error EOSGMultiplayerPeer::_put_packet(const uint8_t *p_buffer, int32_t p_buffer_size) { ERR_FAIL_NULL_V_MSG(s_local_user_id, ERR_UNCONFIGURED, "Local user id has not been set."); ERR_FAIL_COND_V_MSG(!_is_active(), ERR_UNCONFIGURED, "The multiplayer instance isn't currently active."); ERR_FAIL_COND_V_MSG(connection_status != CONNECTION_CONNECTED, ERR_UNCONFIGURED, "The multiplayer instance isn't currently connected"); ERR_FAIL_COND_V_MSG(target_peer != 0 && !peers.has(ABS(target_peer)), ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer)); ERR_FAIL_COND_V_MSG(p_buffer_size > _get_max_packet_size(), ERR_UNAVAILABLE, "Failed to send packet. Packet size exceeds limits."); ERR_FAIL_COND_V(active_mode == MODE_CLIENT && !peers.has(1), ERR_BUG); uint8_t channel; uint8_t tr_channel = _get_transfer_channel(); channel = CH_RELIABLE; EOS_EPacketReliability reliability = EOS_EPacketReliability::EOS_PR_UnreliableUnordered; /*IMPORTANT NOTE: EOS does not support an unreliable ordered transfer mode, so it has been left out. EOSGMultiplayerPeer prints a warning and automatically sets the transfer mode to TRANSFER_MODE_RELIABLE if the user tries to set to unreliable ordred. See _set_transer_mode()*/ switch (_get_transfer_mode()) { case TRANSFER_MODE_UNRELIABLE: { reliability = EOS_EPacketReliability::EOS_PR_UnreliableUnordered; channel = CH_UNRELIABLE; } break; case TRANSFER_MODE_RELIABLE: { reliability = EOS_EPacketReliability::EOS_PR_ReliableOrdered; channel = CH_RELIABLE; } break; case TRANSFER_MODE_UNRELIABLE_ORDERED: { } break; } if (tr_channel > 0) { channel = CH_MAX + tr_channel - 1; } EOSGPacket packet; packet.set_sender(unique_id); packet.set_reliability(reliability); packet.set_channel(channel); packet.set_event(EVENT_STORE_PACKET); packet.store_payload(p_buffer, p_buffer_size); packet.prepare(); Error result; if (_is_server() || active_mode == MODE_MESH) { if (target_peer == 0) { result = _broadcast(packet); } else if (target_peer < 0) { int exclude = ABS(target_peer); result = _broadcast(packet, exclude); } else { result = _send_to(peers[target_peer], packet); } } else { //We're a client result = _send_to(peers[1], packet); //send to the server } return result; } /**************************************** * _get_available_packet_count * Description: Called by MultiplayerAPI to query how * many packets are currently queued. The MultiplayerAPI calls this method * every time it polls to determine if there are packets to recieve. ****************************************/ int32_t EOSGMultiplayerPeer::_get_available_packet_count() const { return socket.get_packet_count(); } /**************************************** * _get_max_packet_size * Description: Called by MultiplayerAPI to query how * large packets are allowed to be. ****************************************/ int32_t EOSGMultiplayerPeer::_get_max_packet_size() const { return EOS_P2P_MAX_PACKET_SIZE; } /**************************************** * _get_packet_channel * Description: Called by MultiplayerAPI to get the channel of next * avaialble packet in the packet queue. ****************************************/ int32_t EOSGMultiplayerPeer::_get_packet_channel() const { return socket.get_packet_channel(); } /**************************************** * _get_packet_mode * Description: Called by MultiplayerAPI to get the transfer mode of next * avaialble packet in the packet queue. ****************************************/ MultiplayerPeer::TransferMode EOSGMultiplayerPeer::_get_packet_mode() const { return _convert_eos_reliability_to_transfer_mode(socket.get_packet_reliability()); } /**************************************** * _set_transfer_channel * Parameters: * p_channel - The channel to set to. * Description: Called by MultiplayerAPI to set the transfer channel of this * multiplayer instance. ****************************************/ void EOSGMultiplayerPeer::_set_transfer_channel(int32_t p_channel) { transfer_channel = p_channel; } /**************************************** * _get_transfer_channel * Description: Called by MultiplayerAPI to get the transfer channel of * this multiplayer instance. ****************************************/ int32_t EOSGMultiplayerPeer::_get_transfer_channel() const { return transfer_channel; } /**************************************** * _set_transfer_mode * Parameters: * p_mode - The transfer mode to set to. * Description: Called by MultiplayerAPI to set the transfer mode of * this multiplayer instance. Unreliable ordered is not supported so the transfer mode * is set to reliable in that case. ****************************************/ void EOSGMultiplayerPeer::_set_transfer_mode(MultiplayerPeer::TransferMode p_mode) { if (p_mode == TRANSFER_MODE_UNRELIABLE_ORDERED) { WARN_PRINT("EOSGMultiplayerPeer does not support unreliable ordered. Setting to reliable instead."); transfer_mode = TRANSFER_MODE_RELIABLE; return; } transfer_mode = p_mode; } /**************************************** * _get_transfer_mode * Description: Called by MultiplayerAPI to get the transfer mode of * this multiplayer instance. ****************************************/ MultiplayerPeer::TransferMode EOSGMultiplayerPeer::_get_transfer_mode() const { return transfer_mode; } /**************************************** * _set_target_peer * Parameters: * p_peer - The target peer * Description: Called by MultiplayerAPI to set the target peer to * send packets to. If this is set to 0, packets are broadcasted to all connected to peers. * If the value is negative, packets are broadcasted to all peers except for the peer identified * by the absolute value of the negative id. ****************************************/ void EOSGMultiplayerPeer::_set_target_peer(int32_t p_peer) { target_peer = p_peer; } /**************************************** * _get_packet_peer * Description: Called by MultiplayerAPI to get the peer id of the peer * who sent the next queued packet. ****************************************/ int32_t EOSGMultiplayerPeer::_get_packet_peer() const { return socket.get_packet_peer(); } /**************************************** * _is_server * Description: Called by MultiplayerAPI to determine if this instance is a server. ****************************************/ bool EOSGMultiplayerPeer::_is_server() const { return active_mode == MODE_SERVER; } /**************************************** * _poll * Description: Called by MultiplayerAPI every network frame to poll queued packets inside EOSGPacketPeerMediator. * Packets are recieved first from EOSGPacketPeerMediator. Once polled, the event type of each packet * are retrieved from the first byte of the packet. If the event type is EVENT_RECIEVED_PEER_ID, * the sender peer id contained inside the packet header is added to the list of connected peers * for this mutliplayer instance. This usually happens only once when peers first establish a connection. * If the event is EVENT_STORE_PACKET, the packet is queued into the socket's packet queue to be * retrieved by the MultiplayerAPI later (See _get_packet()). If the event is EVENT_MESH_CONNECTION_REQUEST, * then do nothing. This event is just to notify the multiplayer instance that the packet has been used to * establish a connection between two mesh instances and nothing needs to be done with the packet. ****************************************/ void EOSGMultiplayerPeer::_poll() { ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active."); if (!polling && !EOSGPacketPeerMediator::get_singleton()->next_packet_is_peer_id_packet(socket.get_name())) return; if (EOSGPacketPeerMediator::get_singleton()->get_packet_count_for_socket(socket.get_name()) == 0) return; //No packets available List packets; PacketData next_packet; if (!polling) { //The next packet should be a peer id packet if we're at this point while (EOSGPacketPeerMediator::get_singleton()->next_packet_is_peer_id_packet(socket.get_name())) { EOSGPacketPeerMediator::get_singleton()->poll_next_packet(socket.get_name(), &next_packet); packets.push_back(next_packet); } } else { while (EOSGPacketPeerMediator::get_singleton()->poll_next_packet(socket.get_name(), &next_packet)) { packets.push_back(next_packet); } } //process all the packets for (PacketData &packet_data : packets) { PackedByteArray *data_ptr = packet_data.get_data(); Event event = static_cast(data_ptr->ptrw()[INDEX_EVENT_TYPE]); switch (event) { case Event::EVENT_STORE_PACKET: { uint32_t peer_id = *reinterpret_cast(data_ptr->ptrw() + INDEX_PEER_ID); if (!peers.has(peer_id)) { return; //ignore the packet if we don't have the peer } EOS_EPacketReliability reliability = static_cast(data_ptr->ptrw()[INDEX_TRANSFER_MODE]); EOSGPacket packet; packet.store_payload(data_ptr->ptrw() + INDEX_PAYLOAD_DATA, packet_data.size() - EOSGPacket::PACKET_HEADER_SIZE); packet.set_event(event); packet.set_sender(peer_id); packet.set_reliability(reliability); packet.set_channel(packet_data.get_channel()); socket.push_packet(packet); break; } case Event::EVENT_RECIEVE_PEER_ID: { ERR_FAIL_COND_MSG(active_mode == MODE_CLIENT && connection_status == CONNECTION_CONNECTED, "Client has recieved a EVENT_RECIEVE_PEER_ID packet when already connected. This shouldn't have happened!"); uint32_t peer_id = *reinterpret_cast(data_ptr->ptrw() + INDEX_PEER_ID); if (active_mode == MODE_CLIENT && peer_id != 1) { _close(); ERR_FAIL_MSG("Failed to connect. Instance is not a server."); } EOS_ProductUserId remote_user = eosg_string_to_product_user_id(packet_data.get_sender().utf8()); if (peer_id < 1 || peers.has(peer_id) || unique_id == peer_id) { _disconnect_remote_user(remote_user); //Invalid peer id. reject the peer. break; } if (has_user_id(packet_data.get_sender())) { //Peer may have reconnected with a new multiplayer instance. Remove their old peer id. int old_id = get_peer_id(packet_data.get_sender()); peers.erase(old_id); emit_signal("peer_disconnected", old_id); } peers.insert(peer_id, remote_user); if (active_mode == MODE_CLIENT) { connection_status = CONNECTION_CONNECTED; } emit_signal("peer_connected", peer_id); break; } case Event::EVENT_MESH_CONNECTION_REQUEST: break; } } } /**************************************** * _close * Description: Called when the multiplayer instance closes. Cleans everything up, closes connections with all peers, * and resets the state of the multiplayer instance. ****************************************/ void EOSGMultiplayerPeer::_close() { if (!_is_active()) { return; } polling = false; //To prevent any further packets from coming in when we close the socket. socket.close(); active_mode = MODE_NONE; connection_status = CONNECTION_DISCONNECTED; pending_connection_requests.clear(); unique_id = 0; set_refuse_new_connections(false); if (peers.is_empty()) { //Go ahead and unregister if there were no peers connected EOSGPacketPeerMediator::get_singleton()->unregister_peer(this); } } /**************************************** * _disconnect_peer * Parameters: * p_peer - The peer to disconnect * p_force - Whether or not we should force disconnection with the peer. * Description: Disconnects the given peer. The peer is removed from the list of connected peers * when the connection closed. If p_force is set to true, the peer is removed from the list immediately * instead of waiting for a confirmed disconnection. ****************************************/ void EOSGMultiplayerPeer::_disconnect_peer(int32_t p_peer, bool p_force) { ERR_FAIL_COND(!_is_active() || !peers.has(p_peer)); ERR_FAIL_NULL_MSG(s_local_user_id, "Cannot close connection. Local user id is not set"); EOS_ProductUserId user_id = peers.get(p_peer); _disconnect_remote_user(user_id); if (p_force && peers.has(p_peer)) { peers.erase(p_peer); emit_signal("peer_disconnected", p_peer); } } /**************************************** * _get_unique_id * Description: returns the peer id of this multiplayer instance. ****************************************/ int32_t EOSGMultiplayerPeer::_get_unique_id() const { return unique_id; } /**************************************** * _set_refuse_new_connections * Parameters: * p_enable - bool that sets refusing new connections. * Description: Sets whether or not new connections are refused. Setting this to true means * that all connection requests are automatically denied. This overrides auto accepting connection requests. ****************************************/ void EOSGMultiplayerPeer::_set_refuse_new_connections(bool p_enable) { refusing_connections = p_enable; } /**************************************** * _is_refuse_new_connections * Description: Returns whether or not new connections are refused. ****************************************/ bool EOSGMultiplayerPeer::_is_refusing_new_connections() const { return refusing_connections; } /**************************************** * _is_server_relayed_supported * Description: Called by MultiplayerAPI to determine if if this multiplayer instance * supports packet relaying. If the instance is either a server or a connected client, then * relaying is supported. This is not the case for mesh instances. ****************************************/ bool EOSGMultiplayerPeer::_is_server_relay_supported() const { return active_mode == MODE_SERVER || active_mode == MODE_CLIENT; } /**************************************** * _get_connection_status * Description: Returns the connection status of this multiplayer instance. Can be either * CONNECTION_DISCONNECTED, CONNECTION_CONNECTING, or CONNECTION_CONNECTED. ****************************************/ MultiplayerPeer::ConnectionStatus EOSGMultiplayerPeer::_get_connection_status() const { return connection_status; } /**************************************** * set_local_user_id * Description: Static method that sets the local user id for the game. This is called when * players first log into the connect interface. This needs to be done for the multiplayer instance * to work. ****************************************/ void EOSGMultiplayerPeer::set_local_user_id(const String &p_local_user_id) { CharString local_user_id = p_local_user_id.utf8(); s_local_user_id = eosg_string_to_product_user_id(local_user_id); } /**************************************** * get_local_user_id * Description: Returns the currently set local user id. Returns an empty string if it was not set. ****************************************/ String EOSGMultiplayerPeer::get_local_user_id() { if (s_local_user_id == nullptr) return ""; String local_user_id = eosg_product_user_id_to_string(s_local_user_id); return local_user_id; } /**************************************** * _broadcast * Parameters: * packet - The packet to be broadcasted. * exclude - The peer that is excluded from receiving the packet. * Description: Broadcast the given packet to all connected peers, except for the peer given by the * exclude parameter. ****************************************/ Error EOSGMultiplayerPeer::_broadcast(const EOSGPacket &packet, int exclude) { ERR_FAIL_COND_V_MSG(packet.packet_size() > _get_max_packet_size(), ERR_OUT_OF_MEMORY, "Failed to send packet. Packet exceeds max size limits."); EOS_P2P_SendPacketOptions options; options.ApiVersion = EOS_P2P_SENDPACKET_API_LATEST; options.LocalUserId = s_local_user_id; options.Channel = packet.get_channel(); options.DataLengthBytes = packet.packet_size(); options.Data = packet.get_packet(); options.bAllowDelayedDelivery = allow_delayed_delivery; options.Reliability = packet.get_reliability(); options.bDisableAutoAcceptConnection = EOS_FALSE; options.SocketId = socket.get_id(); for (KeyValue &E : peers) { if (E.key == exclude) { continue; } options.RemoteUserId = E.value; EOS_EResult result = IEOS::get_singleton()->_p2p_send_packet(&options); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_InvalidParameters, ERR_INVALID_PARAMETER, "Failed to send packet! Invalid parameters."); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_LimitExceeded, FAILED, "Failed to send packet! Packet is either too large or outgoing packet queue is full."); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_NoConnection, ERR_CANT_CONNECT, "Failed to send packet! No connection."); } return OK; } /**************************************** * _send_to * Parameters: * remote_peer - The peer to send the packet to. * packet - The packet being sent. * Description: Sends a packet to the given peer identified by the product user id. ****************************************/ Error EOSGMultiplayerPeer::_send_to(const EOS_ProductUserId &remote_peer, const EOSGPacket &packet) { ERR_FAIL_COND_V_MSG(packet.packet_size() > _get_max_packet_size(), ERR_OUT_OF_MEMORY, "Failed to send packet. Packet exceeds max size limits."); EOS_P2P_SendPacketOptions options; options.ApiVersion = EOS_P2P_SENDPACKET_API_LATEST; options.LocalUserId = s_local_user_id; options.RemoteUserId = remote_peer; options.SocketId = socket.get_id(); options.Channel = packet.get_channel(); options.DataLengthBytes = packet.packet_size(); options.Data = packet.get_packet(); options.bAllowDelayedDelivery = allow_delayed_delivery; options.Reliability = packet.get_reliability(); options.bDisableAutoAcceptConnection = EOS_FALSE; EOS_EResult result = IEOS::get_singleton()->_p2p_send_packet(&options); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_InvalidParameters, ERR_INVALID_PARAMETER, "Failed to send packet! Invalid parameters."); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_LimitExceeded, FAILED, "Failed to send packet! Packet is either too large or outgoing packet queue is full."); ERR_FAIL_COND_V_MSG(result == EOS_EResult::EOS_NoConnection, ERR_CANT_CONNECT, "Failed to send packet! No connection."); return OK; } /**************************************** * _find_connection_request * Parameters: * remote_user - The user to look for a connection request. * out_request - An out parameter that returns the found connection request. * Description: Looks for a connection request in the pending connection request list for the given remote user. * Method returns true if a connection request was found for the user. False otherwise. ****************************************/ bool EOSGMultiplayerPeer::_find_connection_request(const String &remote_user, EOS_ProductUserId &out_request) { for (const EOS_ProductUserId &remote_user_id : pending_connection_requests) { String remote_user_id_str = eosg_product_user_id_to_string(remote_user_id); if (remote_user == remote_user_id_str) { out_request = remote_user_id; return true; } } return false; } /**************************************** * _convert_transfer_mode_to_eos_reliability * Parameters: * mode - The transfer mode to convert. * Description: A helper function that converts the given transfer mode to matching packet reliability. * If TRANSFER_MODE_UNRELIABLE_ORDED is passed, the function returns EOS_PR_ReliableOrdered because EOS * does not support unreliable ordered ****************************************/ EOS_EPacketReliability EOSGMultiplayerPeer::_convert_transfer_mode_to_eos_reliability(TransferMode mode) const { switch (mode) { case TRANSFER_MODE_UNRELIABLE: return EOS_EPacketReliability::EOS_PR_UnreliableUnordered; case TRANSFER_MODE_UNRELIABLE_ORDERED: return EOS_EPacketReliability::EOS_PR_ReliableOrdered; case TRANSFER_MODE_RELIABLE: return EOS_EPacketReliability::EOS_PR_ReliableOrdered; default: return EOS_EPacketReliability::EOS_PR_UnreliableUnordered; } } /**************************************** * _convert_eos_reliability_to_transfer_mode * Parameters: * reliability - The packet reliability to convert. * Description: A helper function that converts the given packet reliability to a matching transfer mode. ****************************************/ MultiplayerPeer::TransferMode EOSGMultiplayerPeer::_convert_eos_reliability_to_transfer_mode(EOS_EPacketReliability reliability) const { switch (reliability) { case EOS_EPacketReliability::EOS_PR_UnreliableUnordered: return TRANSFER_MODE_UNRELIABLE; case EOS_EPacketReliability::EOS_PR_ReliableOrdered: return TRANSFER_MODE_RELIABLE; default: return TRANSFER_MODE_UNRELIABLE; } } /**************************************** * _disconnect_remote_user * Parameters: * remote_user - The remote user id of the user to close the connection. * Description: Closes the connection with the given remote user. This is done on the EOS side * and is called right before the peer with the remote user id is removed from the multiplayer * instance (see _disconnect_peer()). ****************************************/ void EOSGMultiplayerPeer::_disconnect_remote_user(const EOS_ProductUserId &remote_user) { EOS_P2P_CloseConnectionOptions options; options.ApiVersion = EOS_P2P_CLOSECONNECTION_API_LATEST; options.LocalUserId = s_local_user_id; options.RemoteUserId = remote_user; options.SocketId = socket.get_id(); EOS_EResult result = IEOS::get_singleton()->_p2p_close_connection(&options); ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_InvalidParameters, "Failed to close peer connection. Invalid parameters."); } /**************************************** * _peer_connection_established_callback * Parameters: * data - data retrieved from the connection established callback. * Description: A callback that is called by EOSGPacketPeerMediator when it receives a peer connection * established notification. The data contains the local user id of this instance, the remote user id of * peer who just connected, the socket id that the remote peer connected to, the connection type (which tells * you whether it was a new connection or a re-connection), and the network type (which tells you if it is a direct * connection or a relay server is being used.) When a connection is established for the first time and the instance * is not a client, a packet will be sent back to the newly connected peer with the EVENT_RECIEVE_PEER_ID event. * This is how servers and mesh instances peers exchange their peer ids with newly connected peers. ****************************************/ void EOSGMultiplayerPeer::peer_connection_established_callback(const EOS_P2P_OnPeerConnectionEstablishedInfo *data) { if (data->ConnectionType == EOS_EConnectionEstablishedType::EOS_CET_NewConnection && active_mode != MODE_CLIENT) { //We're either a server or mesh //Send peer id to connected peer EOSGPacket packet; packet.set_event(EVENT_RECIEVE_PEER_ID); packet.set_channel(CH_RELIABLE); packet.set_reliability(EOS_EPacketReliability::EOS_PR_ReliableUnordered); packet.set_sender(unique_id); packet.prepare(); Error result = _send_to(data->RemoteUserId, packet); } String local_user_id_str = eosg_product_user_id_to_string(data->LocalUserId); String remote_user_id_str = eosg_product_user_id_to_string(data->RemoteUserId); int connection_type = static_cast(data->ConnectionType); int network_type = static_cast(data->NetworkType); Dictionary ret; ret["local_user_id"] = local_user_id_str; ret["remote_user_id"] = remote_user_id_str; ret["socket"] = data->SocketId->SocketName; ret["connection_type"] = connection_type; ret["network_type"] = network_type; emit_signal("peer_connection_established", ret); } /**************************************** * remote_connection_closed_callback * Parameters: * data - data retrieved from the connection closed callback. * Description: A callback that is called by EOSGPacketPeerMediator when it receives a remote connection * closed notification. The data contains the local user id of this instance, the remote user id of * peer whose connection has closed, the socket id that the remote peer was previously connected to, * and the reason for the disconnection (Which could be a LOT of different reasons, like timeouts, failed * connections, remotely disconnected, etc.). When a connection is closed, the instance checks if there * was a connection request associated with the connection. If there was, then remove the connection request * from the pending connection requests. If the peer was already previously connected, remove the peer * from the instance. If this instance is a client when the callback is called it either means the client * has disconnected locally or connection with the server has failed. ****************************************/ void EOSGMultiplayerPeer::remote_connection_closed_callback(const EOS_P2P_OnRemoteConnectionClosedInfo *data) { String local_user_id_str = eosg_product_user_id_to_string(data->LocalUserId); String remote_user_id_str = eosg_product_user_id_to_string(data->RemoteUserId); int reason = static_cast(data->Reason); //attempt to remove connection request if there was one. EOS_ProductUserId remote_user_id; if (_find_connection_request(remote_user_id_str, remote_user_id)) { pending_connection_requests.erase(remote_user_id); } //Attempt to remove peer; int peer_id = get_peer_id(remote_user_id_str); if (peer_id != 0) { _clear_peer_packet_queue(peer_id); peers.erase(peer_id); emit_signal("peer_disconnected", peer_id); } //If this callback is called when we're a client and the reason isn't due to the local user closing the connection, //it probably means the connection to the server has failed. Close the peer in this case. if (active_mode == MODE_CLIENT && data->Reason != EOS_EConnectionClosedReason::EOS_CCR_ClosedByLocalUser) { EOSGPacketPeerMediator::get_singleton()->unregister_peer(this); _close(); } //The peer has closed their connection. Once all peers have been removed, unregister from the mediator if (connection_status == CONNECTION_DISCONNECTED && peers.is_empty()) { EOSGPacketPeerMediator::get_singleton()->unregister_peer(this); } Dictionary ret; ret["local_user_id"] = local_user_id_str; ret["remote_user_id"] = remote_user_id_str; ret["socket"] = data->SocketId->SocketName; ret["reason"] = reason; emit_signal("peer_connection_closed", ret); } /**************************************** * peer_connection_interrupted_callback * Parameters: * data - data retrieved from the connection interrupted callback. * Description: A callback that is called by EOSGPacketPeerMediator when it receives a peer connection * interrupted notification. Callback only emits a signal containing the data when it is called. ****************************************/ void EOSGMultiplayerPeer::peer_connection_interrupted_callback(const EOS_P2P_OnPeerConnectionInterruptedInfo *data) { String local_user_id_str = eosg_product_user_id_to_string(data->LocalUserId); String remote_user_id_str = eosg_product_user_id_to_string(data->RemoteUserId); Dictionary ret; ret["local_user_id"] = local_user_id_str; ret["remote_user_id"] = remote_user_id_str; ret["socket"] = data->SocketId->SocketName; emit_signal("peer_connection_interrupted", ret); } /**************************************** * connection_request_callback * Parameters: * data - Contains data about the connection request received. * Description: A callback that is called by EOSGPacketPeerMediator when it receives a connection request * for this multiplayer instance. If this instance is a server or a mesh, the connection request is pushed * to the list of pending connection requests. If this instance is a client, the connection request is ignored. * If auto-accepting connection requests is set to true, the connection request is accepted immediately. ****************************************/ void EOSGMultiplayerPeer::connection_request_callback(const ConnectionRequestData &data) { if (active_mode == MODE_CLIENT || is_refusing_new_connections()) return; EOS_ProductUserId remote_user_id = eosg_string_to_product_user_id(data.remote_user_id.utf8()); pending_connection_requests.push_back(remote_user_id); Dictionary ret; ret["local_user_id"] = data.local_user_id; ret["remote_user_id"] = data.remote_user_id; ret["socket"] = String(data.socket_name); emit_signal("incoming_connection_request", ret); if (!is_auto_accepting_connection_requests()) return; accept_connection_request(data.remote_user_id); } /**************************************** * ~EOSGMultiplayerPeer * Description: Destructor. Closes the multiplayer instance if it is still active. ****************************************/ EOSGMultiplayerPeer::~EOSGMultiplayerPeer() { if (active_mode != MODE_NONE) { _close(); } } /**************************************** * EOSGPacket::prepare * Description: Prepares the packet for sending. Allocates memory for the packet if not done so already. * Sets the packet header. ****************************************/ void EOSGMultiplayerPeer::EOSGPacket::prepare() { if (packet == nullptr) { _alloc_packet(); } packet->ptrw()[INDEX_EVENT_TYPE] = static_cast(event); packet->ptrw()[INDEX_TRANSFER_MODE] = static_cast(reliability); memcpy(packet->ptrw() + INDEX_PEER_ID, &from, sizeof(int)); } /**************************************** * EOSGPacket::store_payload * Parameters: * payload_data - pointer to the payload data to be stored in the packet. * payload_size_bytes - size of the payload in bytes. * Description: Stores the passed payload data into the packet. Allocates the packet to the correct * size so that it can fit the payload. ****************************************/ void EOSGMultiplayerPeer::EOSGPacket::store_payload(const uint8_t *payload_data, const uint32_t payload_size_bytes) { if (packet == nullptr) { _alloc_packet(payload_size_bytes + PACKET_HEADER_SIZE); } if (packet->size() != payload_size_bytes + PACKET_HEADER_SIZE) { size_bytes = payload_size_bytes + PACKET_HEADER_SIZE; packet->resize(size_bytes); } memcpy(packet->ptrw() + INDEX_PAYLOAD_DATA, payload_data, payload_size_bytes); } /**************************************** * EOSGSocket::close * Description: Closes the socket. Closes all connections with peers who are currently * connected. Clears the packet queue. ****************************************/ void EOSGMultiplayerPeer::EOSGSocket::close() { clear_packet_queue(); EOS_P2P_CloseConnectionsOptions options; options.ApiVersion = EOS_P2P_CLOSECONNECTIONS_API_LATEST; options.LocalUserId = s_local_user_id; options.SocketId = &socket; IEOS::get_singleton()->_p2p_close_all_connections(&options); } /**************************************** * EOSGSocket::close * Description: Closes the socket. Closes all connections with peers who are currently * connected. Clears the packet queue. ****************************************/ void EOSGMultiplayerPeer::EOSGSocket::clear_packets_from_peer(int p_peer) { List::Element *> del; for (List::Element *e = incoming_packets.front(); e != nullptr; e = e->next()) { if (e->get().get_sender() != p_peer) continue; del.push_back(e); } for (List::Element *e : del) { e->erase(); } } /**************************************** * EOSGSocket::_socket_id_is_valid * Parameters: * socket_id: The socket id to validate. * Description: Validates a socket id. Checks if there are any invalid characters. * Also checks if the socket id is empty and if it meets the length requirements. * Socket id's can only contain alpha-numeric characters. If there * are any invalid characters in the socket id or if it's empty or exceeds the * character length limit, the method returns false. ****************************************/ bool EOSGMultiplayerPeer::EOSGSocket::_socket_id_is_valid(const String &socket_id) { if (socket_id.is_empty()) return false; //socket id should not be empty if (socket_id.length() >= EOS_P2P_SOCKETID_SOCKETNAME_SIZE) { return false; //socket id should not be longer than EOS_P2P_SOCKETID_SOCKETNAME_SIZE - 1 characters } //The ranges of certain characters in the ascii table. int numeric_range_min = 48; int numeric_range_max = 57; int alpha_capitalized_range_min = 65; int alpha_capitalized_range_max = 90; int alpha_lowercase_range_min = 97; int alpha_lowercase_range_max = 122; CharString str = socket_id.ascii(); for (int i = 0; i < str.length(); i++) { if (str[i] >= numeric_range_min && str[i] <= numeric_range_max) { continue; } else if (str[i] >= alpha_capitalized_range_min && str[i] <= alpha_capitalized_range_max) { continue; } else if (str[i] >= alpha_lowercase_range_min && str[i] <= alpha_lowercase_range_max) { continue; } else { return false; //Invalid character found } } return true; } ================================================ FILE: src/eosg_multiplayer_peer.h ================================================ #pragma once #include "eos_p2p.h" #include "godot_cpp/classes/multiplayer_peer_extension.hpp" #include "godot_cpp/templates/hash_map.hpp" #include "utils.h" #include namespace godot { struct ConnectionRequestData { String socket_name; String remote_user_id; String local_user_id; }; class EOSGMultiplayerPeer : public MultiplayerPeerExtension { GDCLASS(EOSGMultiplayerPeer, MultiplayerPeerExtension) private: enum Event { EVENT_STORE_PACKET, EVENT_RECIEVE_PEER_ID, EVENT_MESH_CONNECTION_REQUEST }; enum { INDEX_EVENT_TYPE = 0, INDEX_TRANSFER_MODE = 1, INDEX_PEER_ID = 2, INDEX_PAYLOAD_DATA = 6, }; enum { CH_RELIABLE = 0, CH_UNRELIABLE = 1, CH_MAX = 2, }; enum Mode { MODE_NONE, MODE_SERVER, MODE_CLIENT, MODE_MESH, }; class EOSGPacket { private: std::shared_ptr packet; uint8_t channel = 0; int32_t size_bytes = 0; EOS_EPacketReliability reliability; Event event; int from = 0; void _alloc_packet(int size_bytes = PACKET_HEADER_SIZE) { packet = std::make_shared(); packet->resize(size_bytes); this->size_bytes = size_bytes; } public: static const int PACKET_HEADER_SIZE = 6; void prepare(); void store_payload(const uint8_t *payload_data, const uint32_t payload_size_bytes); int payload_size() const { return size_bytes - PACKET_HEADER_SIZE; } int packet_size() const { return size_bytes; } uint8_t *get_payload() const { if (size_bytes == 0 || size_bytes == PACKET_HEADER_SIZE) { return nullptr; //Return nullptr if there's no payload. } return packet->ptrw() + INDEX_PAYLOAD_DATA; } uint8_t *get_packet() const { if (packet.get() == nullptr) { return nullptr; //Return nullptr if the packed has not been allocated } return packet->ptrw(); } EOS_EPacketReliability get_reliability() const { return reliability; } void set_reliability(EOS_EPacketReliability reliability) { this->reliability = reliability; } uint8_t get_channel() const { return channel; } void set_channel(uint8_t channel) { this->channel = channel; } Event get_event() const { return event; } void set_event(Event event) { this->event = event; } int get_sender() const { return from; } void set_sender(int p_id) { from = p_id; } }; class EOSGSocket { private: EOS_P2P_SocketId socket; List incoming_packets; public: const EOS_P2P_SocketId *get_id() const { return &socket; } String get_name() const { return socket.SocketName; } void push_packet(EOSGPacket packet) { incoming_packets.push_back(packet); } EOSGPacket pop_packet() { EOSGPacket ret = incoming_packets.front()->get(); incoming_packets.pop_front(); return ret; } void clear_packet_queue() { incoming_packets.clear(); } bool has_packet() const { return incoming_packets.size() != 0; } int get_packet_count() const { return incoming_packets.size(); } EOS_EPacketReliability get_packet_reliability() const { EOSGPacket packet = incoming_packets.front()->get(); return packet.get_reliability(); } int32_t get_packet_channel() const { EOSGPacket packet = incoming_packets.front()->get(); return packet.get_channel(); } int32_t get_packet_peer() const { EOSGPacket packet = incoming_packets.front()->get(); return packet.get_sender(); } void close(); void clear_packets_from_peer(int p_peer); bool _socket_id_is_valid(const String &socket_id); EOSGSocket() {} EOSGSocket(const EOS_P2P_SocketId &socket) { this->socket = socket; } EOSGSocket(const String &socket_name) { memset(socket.SocketName, 0, sizeof(socket.SocketName)); ERR_FAIL_COND_MSG(!_socket_id_is_valid(socket_name), "Failed to create socket. Socket id is not valid.\nNOTE: Socket id cannot be empty, must only have alpha-numeric characters, and must not be longer than 32 characters"); socket.ApiVersion = EOS_P2P_SOCKETID_API_LATEST; STRNCPY_S(socket.SocketName, EOS_P2P_SOCKETID_SOCKETNAME_SIZE, socket_name.utf8(), socket_name.length()); } }; _FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; } Error _broadcast(const EOSGPacket &packet, int exclude = 0); Error _send_to(const EOS_ProductUserId &remote_peer, const EOSGPacket &packet); bool _find_connection_request(const String &remote_user, EOS_ProductUserId &out_request); EOS_EPacketReliability _convert_transfer_mode_to_eos_reliability(TransferMode mode) const; TransferMode _convert_eos_reliability_to_transfer_mode(EOS_EPacketReliability reliability) const; void _disconnect_remote_user(const EOS_ProductUserId &remote_user); void _clear_peer_packet_queue(int p_id); static EOS_ProductUserId s_local_user_id; EOSGPacket current_packet; uint32_t unique_id; int target_peer = 0; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; Mode active_mode = MODE_NONE; EOS_Bool allow_delayed_delivery = EOS_TRUE; bool auto_accept_connection_requests = true; TransferMode transfer_mode = TransferMode::TRANSFER_MODE_RELIABLE; uint32_t transfer_channel = CH_RELIABLE; bool refusing_connections = false; bool polling = false; HashMap peers; EOSGSocket socket; List pending_connection_requests; static void _bind_methods(); public: static void set_local_user_id(const String &p_local_user_id); static String get_local_user_id(); void peer_connection_established_callback(const EOS_P2P_OnPeerConnectionEstablishedInfo *data); void remote_connection_closed_callback(const EOS_P2P_OnRemoteConnectionClosedInfo *data); void peer_connection_interrupted_callback(const EOS_P2P_OnPeerConnectionInterruptedInfo *data); void connection_request_callback(const ConnectionRequestData &data); Error create_server(const String &socket_id); Error create_client(const String &socket_id, const String &remote_user_id); Error create_mesh(const String &socket_id); Error add_mesh_peer(const String &remote_user); String get_socket() const; Array get_all_connection_requests(); String get_peer_user_id(int p_id); int get_peer_id(const String &user_id); bool has_peer(int peer_id); bool has_user_id(const String &remote_user_id); Dictionary get_all_peers(); void set_allow_delayed_delivery(bool allow); bool is_allowing_delayed_delivery(); void set_auto_accept_connection_requests(bool enable); bool is_auto_accepting_connection_requests(); void accept_connection_request(const String &remote_user); void deny_connection_request(const String &remote_user); void accept_all_connection_requests(); void deny_all_connection_requests(); int get_active_mode(); bool is_polling() { return polling; } void set_is_polling(bool polling) { this->polling = polling; } virtual Error _get_packet(const uint8_t **r_buffer, int32_t *r_buffer_size) override; virtual Error _put_packet(const uint8_t *p_buffer, int32_t p_buffer_size) override; virtual int32_t _get_available_packet_count() const override; virtual int32_t _get_max_packet_size() const override; virtual int32_t _get_packet_channel() const override; virtual MultiplayerPeer::TransferMode _get_packet_mode() const override; virtual void _set_transfer_channel(int32_t p_channel) override; virtual int32_t _get_transfer_channel() const override; virtual void _set_transfer_mode(MultiplayerPeer::TransferMode p_mode) override; virtual MultiplayerPeer::TransferMode _get_transfer_mode() const override; virtual void _set_target_peer(int32_t p_peer) override; virtual int32_t _get_packet_peer() const override; virtual bool _is_server() const override; virtual void _poll() override; virtual void _close() override; virtual void _disconnect_peer(int32_t p_peer, bool p_force = false) override; virtual int32_t _get_unique_id() const override; virtual void _set_refuse_new_connections(bool p_enable) override; virtual bool _is_refusing_new_connections() const override; virtual bool _is_server_relay_supported() const override; virtual MultiplayerPeer::ConnectionStatus _get_connection_status() const override; EOSGMultiplayerPeer(){}; ~EOSGMultiplayerPeer(); }; } //namespace godot ================================================ FILE: src/eosg_packet_peer_mediator.cpp ================================================ /**************************************** * EOSGPacketPeerMediator * Author: Dallin Lovin aka LowFire * Description: Manages EOSG multiplayer instances when they are active. * Multiplayer instances register their socket id with the mediator when * they become active and unregister their socket id when they close. * The mediator receives packets from the EOS P2P interface every process * frame and sorts those packets according to their destination socket so * that the appropriate multiplayer instance can poll them later. The mediator * receives EOS notifications and fowards it to the appropriate multiplayer * instance according to the socket the notification was received from. * Mediator manages incoming connection requests and forwards them to the * appropriate multiplayer instance according to the socket id of the * connection request. If there is no matching socket from any of the active * multiplayer instances, the mediator will hold onto the connection request * until either a multiplayer instance opens with a matching socket or until * the connection request times out. ****************************************/ #include "eosg_packet_peer_mediator.h" #include "godot_cpp/classes/rendering_server.hpp" #include "utils.h" EOSGPacketPeerMediator *EOSGPacketPeerMediator::singleton = nullptr; void EOSGPacketPeerMediator::_bind_methods() { ClassDB::bind_method(D_METHOD("_on_process_frame"), &EOSGPacketPeerMediator::_on_process_frame); ClassDB::bind_method(D_METHOD("_on_connect_interface_login"), &EOSGPacketPeerMediator::_on_connect_interface_login); ClassDB::bind_method(D_METHOD("get_total_packet_count"), &EOSGPacketPeerMediator::get_total_packet_count); ClassDB::bind_method(D_METHOD("get_sockets"), &EOSGPacketPeerMediator::get_sockets); ClassDB::bind_method(D_METHOD("get_packet_count_for_socket", "socket_id"), &EOSGPacketPeerMediator::get_packet_count_for_socket); ClassDB::bind_method(D_METHOD("has_socket"), &EOSGPacketPeerMediator::has_socket); ClassDB::bind_method(D_METHOD("get_packet_count_from_remote_user"), &EOSGPacketPeerMediator::get_packet_count_from_remote_user); ClassDB::bind_method(D_METHOD("get_queue_size_limit"), &EOSGPacketPeerMediator::get_queue_size_limit); ClassDB::bind_method(D_METHOD("set_queue_size_limit", "limit"), &EOSGPacketPeerMediator::set_queue_size_limit); ClassDB::bind_method(D_METHOD("get_connection_request_count"), &EOSGPacketPeerMediator::get_connection_request_count); ADD_SIGNAL(MethodInfo("packet_queue_full")); ADD_SIGNAL(MethodInfo("connection_request_received", PropertyInfo(Variant::DICTIONARY, "callback_data"))); ADD_SIGNAL(MethodInfo("connection_request_removed", PropertyInfo(Variant::DICTIONARY, "callback_data"))); } /**************************************** * _on_process_frame * Description: Method is connected to the game main loop's process signal so * that it can execute every process frame (see _init()). Checks if there are * any packets available from the incoming packet queue. If there are, receives * the packet and sorts it into separate queues according to it's destination socket. Packets that * are peer id packets (packets with EVENT_RECIEVE_PEER_ID) and pushed to the front. Packets will * stop being polled if the queue size limit is reached. ****************************************/ void EOSGPacketPeerMediator::_on_process_frame() { if (EOSGMultiplayerPeer::get_local_user_id().is_empty()) return; if (socket_packet_queues.size() == 0) return; if (get_total_packet_count() >= max_queue_size) return; String local_user_id_str = EOSGMultiplayerPeer::get_local_user_id(); EOS_ProductUserId local_user_id = eosg_string_to_product_user_id(local_user_id_str.utf8()); EOS_P2P_GetNextReceivedPacketSizeOptions packet_size_options; packet_size_options.ApiVersion = EOS_P2P_GETNEXTRECEIVEDPACKETSIZE_API_LATEST; packet_size_options.LocalUserId = local_user_id; packet_size_options.RequestedChannel = nullptr; uint32_t max_packet_size; EOS_P2P_ReceivePacketOptions recieve_packet_options; recieve_packet_options.ApiVersion = EOS_P2P_RECEIVEPACKET_API_LATEST; recieve_packet_options.LocalUserId = local_user_id; recieve_packet_options.MaxDataSizeBytes = EOS_P2P_MAX_PACKET_SIZE; recieve_packet_options.RequestedChannel = nullptr; bool next_packet_available = true; EOS_EResult result = EOS_EResult::EOS_Success; do { result = IEOS::get_singleton()->_p2p_get_next_packet_size(&packet_size_options, &max_packet_size); ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_InvalidParameters, "Failed to get packet size! Invalid parameters."); if (result == EOS_EResult::EOS_Success) { next_packet_available = true; } else { next_packet_available = false; } if (next_packet_available) { PackedByteArray packet_data; packet_data.resize(max_packet_size); uint32_t buffer_size; uint8_t channel; EOS_P2P_SocketId socket; EOS_ProductUserId remote_user; result = IEOS::get_singleton()->_p2p_receive_packet(&recieve_packet_options, packet_data.ptrw(), &buffer_size, &channel, &remote_user, &socket); String socket_id_str = socket.SocketName; ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_InvalidParameters, "Failed to get packet! Invalid parameters."); ERR_FAIL_COND_MSG(result == EOS_EResult::EOS_NotFound, "Failed to get packet! Packet is too large. This should not have happened."); if (!socket_packet_queues.has(socket_id_str)) return; //invalid socket. Drop the packet. PacketData packet; packet.store(packet_data.ptrw(), max_packet_size); packet.set_channel(channel); packet.set_sender(remote_user); uint8_t event = packet.get_data()->ptrw()[0]; if (event == 1) { socket_packet_queues[socket_id_str].push_front(packet); } else { socket_packet_queues[socket_id_str].push_back(packet); } if (get_total_packet_count() >= max_queue_size) { emit_signal("packet_queue_full"); break; } } } while (next_packet_available); } /**************************************** * poll_next_packet * Parameters: * socket_id - The socket to poll a packet from. * out_packet - An out parameter that returns the polled packet. * Description: Polls the next packet available for the given socket. * Returns true if a packet has been successfully polled. False otherwise. ****************************************/ bool EOSGPacketPeerMediator::poll_next_packet(const String &socket_id, PacketData *out_packet) { if (!socket_packet_queues.has(socket_id)) return false; if (socket_packet_queues[socket_id].size() == 0) return false; PacketData next_packet = socket_packet_queues[socket_id].front()->get(); *out_packet = next_packet; socket_packet_queues[socket_id].pop_front(); return true; } /**************************************** * register_peer * Parameters: * peer - The peer to be registered with the mediator. * Description: Registers a peer and it's socket with the mediator. * Once registered, a peer can receive packets, EOS notifications, and connection requests. ****************************************/ bool EOSGPacketPeerMediator::register_peer(EOSGMultiplayerPeer *peer) { ERR_FAIL_COND_V_MSG(!initialized, false, "Failed to register peer. EOSGPacketPeerMediator has not been initialized. Call EOSGPacketPeerMediator.init() before starting a multiplayer instance."); ERR_FAIL_COND_V_MSG(peer->get_socket().is_empty(), false, "Failed to register peer. Peer is not active."); ERR_FAIL_COND_V_MSG(active_peers.has(peer->get_socket()), false, "Failed to register peer. This peer has already been registered."); active_peers.insert(peer->get_socket(), peer); socket_packet_queues.insert(peer->get_socket(), List()); _forward_pending_connection_requests(peer); return true; } /**************************************** * unregister_peer * Parameters: * peer - The peer to be unregistered with the mediator. * Description: Unregisteres a peer and it's socket with the mediator. * Peers can no longer receive packets, notifications, or connection requests once this is done. * unregistration usually happens when a peer closes. ****************************************/ void EOSGPacketPeerMediator::unregister_peer(EOSGMultiplayerPeer *peer) { if (!active_peers.has(peer->get_socket())) return; clear_packet_queue(peer->get_socket()); socket_packet_queues.erase(peer->get_socket()); active_peers.erase(peer->get_socket()); } /**************************************** * clear_packet_queue * Parameters: * socket_id - The socket to clear packets from. * Description: Removes all packets queued for the given socket. ****************************************/ void EOSGPacketPeerMediator::clear_packet_queue(const String &socket_id) { ERR_FAIL_COND_MSG(!socket_packet_queues.has(socket_id), vformat("Failed to clear packet queue for socket \"%s\". Socket was not registered.", socket_id)); socket_packet_queues[socket_id].clear(); } /**************************************** * clear_packets_from_remote_user * Parameters: * socket_id - The socket to clear packets from. * remote_user_id - The user to remove packets from. * Description: Removes all packets queued for the given socket and from the given remote user. * This is usually called when a peer disconnects. All packets from that peer are removed. ****************************************/ void EOSGPacketPeerMediator::clear_packets_from_remote_user(const String &socket_id, const String &remote_user_id) { ERR_FAIL_COND_MSG(!socket_packet_queues.has(socket_id), vformat("Failed to clear packet queue for socket \"%s\". Socket was not registered.", socket_id)); List::Element *> del; for (List::Element *e = socket_packet_queues[socket_id].front(); e != nullptr; e = e->next()) { if (e->get().get_sender() != remote_user_id) continue; del.push_back(e); } for (List::Element *e : del) { e->erase(); } } /**************************************** * _init * Description: Initialized EOSGPacketPeerMediator. Connects _on_process_frame to the * main loop's process signal. Adds EOS callbacks so that it can receive notifications. ****************************************/ void EOSGPacketPeerMediator::_init() { ERR_FAIL_COND_MSG(EOSGMultiplayerPeer::get_local_user_id().is_empty(), "Failed to initialize EOSGPacketPeerMediator. Local user id has not been set."); if (initialized) return; MainLoop *main_loop = Engine::get_singleton()->get_main_loop(); ERR_FAIL_COND_MSG(!main_loop->has_signal("process_frame"), "Failed to initialize EOSGPacketPeerMediator. Main loop does not have the process_frame() signal."); main_loop->connect("process_frame", process_frame_callback); //Register callbacks _add_connection_closed_callback(); _add_connection_established_callback(); _add_connection_interrupted_callback(); _add_connection_request_callback(); initialized = true; } /**************************************** * _terminate * Description: Terminates EOSGPacketPeerMediator. Disconnects from the * main loop's process signal. Removes all EOS callbacks. ****************************************/ void EOSGPacketPeerMediator::_terminate() { if (!initialized) return; MainLoop *main_loop = Engine::get_singleton()->get_main_loop(); main_loop->disconnect("process_frame", process_frame_callback); //Unregister callbacks IEOS::get_singleton()->_p2p_remove_notify_peer_connection_established(connection_established_callback_id); IEOS::get_singleton()->_p2p_remove_notify_peer_connection_interrupted(connection_interrupted_callback_id); IEOS::get_singleton()->_p2p_remove_notify_peer_connection_closed(connection_closed_callback_id); IEOS::get_singleton()->_p2p_remove_notify_peer_connection_request(connection_request_callback_id); initialized = false; } /**************************************** * get_packet_count_from_remote_user * Parameters: * remote_user - The user to count packets for. * socket_id - The socket to count packets for. * Description: Counts the number of packets from the given remote user and for * the given socket. Returns the packet count. ****************************************/ int EOSGPacketPeerMediator::get_packet_count_from_remote_user(const String &remote_user, const String &socket_id) { ERR_FAIL_COND_V_MSG(!socket_packet_queues.has(socket_id), 0, vformat("Failed to get packet count for remote user. Socket \"%s\" does not exist", socket_id)); int ret = 0; for (PacketData &data : socket_packet_queues[socket_id]) { if (data.get_sender() == remote_user) { ret++; } } return ret; } /**************************************** * next_packet_is_peer_id_packet * Parameters: * socket_id - The socket to check * Description: Checks if there is a peer id packet queued for the given socket. * Returns true if there is, false otherwise. ****************************************/ bool EOSGPacketPeerMediator::next_packet_is_peer_id_packet(const String &socket_id) { ERR_FAIL_COND_V_MSG(!socket_packet_queues.has(socket_id), false, "Failed to check next packet. Socket \"%s\" does not exist."); if (socket_packet_queues[socket_id].size() == 0) return false; PacketData packet = socket_packet_queues[socket_id][0]; uint8_t event = packet.get_data()->ptrw()[0]; if (event == 1) return true; return false; } /**************************************** * _on_peer_connection_established * Parameters: * data - Data returned from the notification * Description: An EOS callback that is called when a connection is established with a peer. * Forwards the data to the appropriate multiplayer instance using the socket id provided in the data. ****************************************/ void EOS_CALL EOSGPacketPeerMediator::_on_peer_connection_established(const EOS_P2P_OnPeerConnectionEstablishedInfo *data) { String socket_id = data->SocketId->SocketName; if (!singleton->active_peers.has(socket_id)) return; singleton->active_peers[socket_id]->peer_connection_established_callback(data); } /**************************************** * _on_peer_connection_interrupted * Parameters: * data - Data returned from the notification * Description: An EOS callback that is called when the connection with a peer is interrupted. * Forwards the data to the appropriate multiplayer instance using the socket id provided in the data. ****************************************/ void EOS_CALL EOSGPacketPeerMediator::_on_peer_connection_interrupted(const EOS_P2P_OnPeerConnectionInterruptedInfo *data) { String socket_id = data->SocketId->SocketName; if (!singleton->active_peers.has(socket_id)) return; singleton->active_peers[socket_id]->peer_connection_interrupted_callback(data); } /**************************************** * _on_remote_connection_closed * Parameters: * data - Data returned from the notification * Description: An EOS callback that is called when the connection with a peer is closed. * Checks to see if there were any connection requests associated with the closed connection. * If so, it removes that connection request. Forwards the data to the appropriate multiplayer instance using * the socket id provided in the data. ****************************************/ void EOS_CALL EOSGPacketPeerMediator::_on_remote_connection_closed(const EOS_P2P_OnRemoteConnectionClosedInfo *data) { String socket_name = data->SocketId->SocketName; //Check if any connection requests need to be removed. List::Element *e = singleton->pending_connection_requests.front(); for (; e != nullptr; e = e->next()) { String request_remote_user_id = e->get().remote_user_id; String closed_remote_user_id = eosg_product_user_id_to_string(data->RemoteUserId); String request_socket_name = e->get().socket_name; if (request_remote_user_id == closed_remote_user_id && socket_name == request_socket_name) { Dictionary ret; ret["local_user_id"] = e->get().local_user_id; ret["remote_user_id"] = e->get().remote_user_id; ret["socket_id"] = e->get().socket_name; singleton->emit_signal("connection_request_removed", ret); e->erase(); break; } } if (!singleton->active_peers.has(socket_name)) return; singleton->active_peers[socket_name]->remote_connection_closed_callback(data); } /**************************************** * _on_incoming_connection_request * Parameters: * data - Data returned from the notification * Description: An EOS callback that is called when a connection request is received. * Checks if there are any peers available to receive the connection request using * the destination socket id. If there isn't, stores the connection request for later. * If there is, forward the connection request to that multiplayer instance. ****************************************/ void EOS_CALL EOSGPacketPeerMediator::_on_incoming_connection_request(const EOS_P2P_OnIncomingConnectionRequestInfo *data) { ConnectionRequestData request_data; request_data.local_user_id = eosg_product_user_id_to_string(data->LocalUserId); request_data.remote_user_id = eosg_product_user_id_to_string(data->RemoteUserId); request_data.socket_name = data->SocketId->SocketName; if (!singleton->active_peers.has(request_data.socket_name)) { //Hold onto the connection request just in case a socket does get opened with this socket id singleton->pending_connection_requests.push_back(request_data); Dictionary ret; ret["local_user_id"] = request_data.local_user_id; ret["remote_user_id"] = request_data.remote_user_id; ret["socket_id"] = request_data.socket_name; singleton->emit_signal("connection_request_received", ret); return; } singleton->active_peers[request_data.socket_name]->connection_request_callback(request_data); } /**************************************** * _on_connect_interface_login * Parameters: * data - Contains info about the login. * Description: Called when the user logs into the connect interface. Sets the * local user id received from the login and initialized EOSGPacketPeerMediator. ****************************************/ void EOSGPacketPeerMediator::_on_connect_interface_login(Dictionary data) { int result_code = data["result_code"]; if (static_cast(result_code) != EOS_EResult::EOS_Success) { return; } String local_user_id = data["local_user_id"]; if (local_user_id != "") { EOSGMultiplayerPeer::set_local_user_id(local_user_id); EOSGPacketPeerMediator::get_singleton()->_init(); } else { ERR_FAIL_MSG("Local user id was not set on connect interface login."); } IEOS::get_singleton()->disconnect("connect_interface_login_callback", connect_interface_login_callback); } /**************************************** * _add_connection_established_callback * Parameters: * Description: Adds the peer connection established callback. This is called * in _init() ****************************************/ bool EOSGPacketPeerMediator::_add_connection_established_callback() { String local_user_id_str = EOSGMultiplayerPeer::get_local_user_id(); EOS_ProductUserId local_user_id = eosg_string_to_product_user_id(local_user_id_str.utf8()); EOS_P2P_AddNotifyPeerConnectionEstablishedOptions connection_established_options; connection_established_options.ApiVersion = EOS_P2P_ADDNOTIFYPEERCONNECTIONESTABLISHED_API_LATEST; connection_established_options.LocalUserId = local_user_id; connection_established_options.SocketId = nullptr; connection_established_callback_id = IEOS::get_singleton()->_p2p_add_notify_peer_connection_established(&connection_established_options, _on_peer_connection_established); ERR_FAIL_COND_V_MSG(connection_established_callback_id == EOS_INVALID_NOTIFICATIONID, false, "Failed to add connection established callback."); return true; } /**************************************** * _add_connection_interrupted_callback * Description: Adds the peer connection interrupted callback. This is called * in _init() ****************************************/ bool EOSGPacketPeerMediator::_add_connection_interrupted_callback() { String local_user_id_str = EOSGMultiplayerPeer::get_local_user_id(); EOS_ProductUserId local_user_id = eosg_string_to_product_user_id(local_user_id_str.utf8()); EOS_P2P_AddNotifyPeerConnectionInterruptedOptions connection_interrupted_options; connection_interrupted_options.ApiVersion = EOS_P2P_ADDNOTIFYPEERCONNECTIONINTERRUPTED_API_LATEST; connection_interrupted_options.LocalUserId = local_user_id; connection_interrupted_options.SocketId = nullptr; connection_interrupted_callback_id = IEOS::get_singleton()->_p2p_add_notify_peer_connection_interrupted(&connection_interrupted_options, _on_peer_connection_interrupted); ERR_FAIL_COND_V_MSG(connection_interrupted_callback_id == EOS_INVALID_NOTIFICATIONID, false, "Failed to add connection interrupted callback."); return true; } /**************************************** * _add_connection_closed_callback * Description: Adds the peer connection closed callback. This is called * in _init() ****************************************/ bool EOSGPacketPeerMediator::_add_connection_closed_callback() { String local_user_id_str = EOSGMultiplayerPeer::get_local_user_id(); EOS_ProductUserId local_user_id = eosg_string_to_product_user_id(local_user_id_str.utf8()); EOS_P2P_AddNotifyPeerConnectionClosedOptions connection_closed_options; connection_closed_options.ApiVersion = EOS_P2P_ADDNOTIFYPEERCONNECTIONCLOSED_API_LATEST; connection_closed_options.LocalUserId = local_user_id; connection_closed_options.SocketId = nullptr; connection_closed_callback_id = IEOS::get_singleton()->_p2p_add_notify_peer_connection_closed(&connection_closed_options, _on_remote_connection_closed); ERR_FAIL_COND_V_MSG(connection_closed_callback_id == EOS_INVALID_NOTIFICATIONID, false, "Failed to add connection closed callback."); return true; } /**************************************** * _add_connection_request_callback * Description: Adds the peer connection request callback. This is called * in _init() ****************************************/ bool EOSGPacketPeerMediator::_add_connection_request_callback() { String local_user_id_str = EOSGMultiplayerPeer::get_local_user_id(); EOS_ProductUserId local_user_id = eosg_string_to_product_user_id(local_user_id_str.utf8()); EOS_P2P_AddNotifyPeerConnectionRequestOptions connection_request_options; connection_request_options.ApiVersion = EOS_P2P_ADDNOTIFYPEERCONNECTIONREQUEST_API_LATEST; connection_request_options.LocalUserId = local_user_id; connection_request_options.SocketId = nullptr; connection_request_callback_id = IEOS::get_singleton()->_p2p_add_notify_peer_connection_request(&connection_request_options, _on_incoming_connection_request); ERR_FAIL_COND_V_MSG(connection_request_callback_id == EOS_INVALID_NOTIFICATIONID, false, "Failed to add connection request callback."); return true; } /**************************************** * _forward_pending_connection_requests * Parameters: * peer - The peer to forward connection requests to. * Description: Attempts to forward any pending connection requests to the given multiplayer instance. * If none of the pending requests match the multiplayer instance's socket, then no connection requests * are forwarded. ****************************************/ void EOSGPacketPeerMediator::_forward_pending_connection_requests(EOSGMultiplayerPeer *peer) { List::Element *e = pending_connection_requests.front(); List::Element *> del; for (; e != nullptr; e = e->next()) { if (peer->get_socket() != e->get().socket_name) continue; peer->connection_request_callback(e->get()); del.push_back(e); } for (List::Element *e : del) { Dictionary ret; ret["local_user_id"] = e->get().local_user_id; ret["remote_user_id"] = e->get().remote_user_id; ret["socket_id"] = e->get().socket_name; emit_signal("connection_request_removed", ret); e->erase(); } } /**************************************** * EOSGPacketPeerMediator * Description: Default constructor. Sets the singleton. Connects * to the connect interface login callback so that the class knows * when to initialize. ****************************************/ EOSGPacketPeerMediator::EOSGPacketPeerMediator() { ERR_FAIL_COND_MSG(singleton != nullptr, "EOSGPacketPeerMediator already initialized"); singleton = this; process_frame_callback = Callable(this, "_on_process_frame"); connect_interface_login_callback = Callable(this, "_on_connect_interface_login"); IEOS::get_singleton()->connect("connect_interface_login_callback", connect_interface_login_callback); } /**************************************** * !EOSGPacketPeerMediator * Description: Destructor. Sets singleton to null. ****************************************/ EOSGPacketPeerMediator::~EOSGPacketPeerMediator() { if (singleton != this) return; singleton = nullptr; } ================================================ FILE: src/eosg_packet_peer_mediator.h ================================================ #include "eosg_multiplayer_peer.h" #include "godot_cpp/classes/engine.hpp" #include "godot_cpp/classes/ref_counted.hpp" #include "godot_cpp/classes/scene_tree.hpp" namespace godot { struct PacketData { private: std::shared_ptr data; String remote_user_id; int size_bytes = 0; int channel = 0; public: void store(uint8_t *packet, int size_bytes) { if (data == nullptr) { data = std::make_shared(); } this->size_bytes = size_bytes; data->resize(size_bytes); memcpy(data->ptrw(), packet, size_bytes); } int size() { return size_bytes; } int get_channel() { return channel; } String get_sender() { return remote_user_id; } void set_channel(int channel) { this->channel = channel; } void set_sender(EOS_ProductUserId sender) { remote_user_id = eosg_product_user_id_to_string(sender); } PackedByteArray *get_data() { return data.get(); } }; class EOSGPacketPeerMediator : public Object { GDCLASS(EOSGPacketPeerMediator, Object) private: static EOSGPacketPeerMediator *singleton; Callable process_frame_callback; Callable connect_interface_login_callback; static void _bind_methods(); HashMap active_peers; HashMap> socket_packet_queues; List pending_connection_requests; int max_queue_size = 5000; bool initialized = false; void _on_process_frame(); void _init(); void _terminate(); static void EOS_CALL _on_peer_connection_established(const EOS_P2P_OnPeerConnectionEstablishedInfo *data); static void EOS_CALL _on_peer_connection_interrupted(const EOS_P2P_OnPeerConnectionInterruptedInfo *data); static void EOS_CALL _on_remote_connection_closed(const EOS_P2P_OnRemoteConnectionClosedInfo *data); static void EOS_CALL _on_incoming_connection_request(const EOS_P2P_OnIncomingConnectionRequestInfo *data); void _on_connect_interface_login(Dictionary data); bool _add_connection_established_callback(); bool _add_connection_closed_callback(); bool _add_connection_interrupted_callback(); bool _add_connection_request_callback(); void _forward_pending_connection_requests(EOSGMultiplayerPeer *peer); EOS_NotificationId connection_established_callback_id = EOS_INVALID_NOTIFICATIONID; EOS_NotificationId connection_interrupted_callback_id = EOS_INVALID_NOTIFICATIONID; EOS_NotificationId connection_closed_callback_id = EOS_INVALID_NOTIFICATIONID; EOS_NotificationId connection_request_callback_id = EOS_INVALID_NOTIFICATIONID; public: static EOSGPacketPeerMediator *get_singleton() { return singleton; } int get_total_packet_count() { int ret = 0; for (KeyValue> &E : socket_packet_queues) { ret += E.value.size(); } return ret; } int get_packet_count_for_socket(const String &socket_id) { ERR_FAIL_COND_V_MSG(!socket_packet_queues.has(socket_id), 0, "Failed to get packet count for socket \"%s\". Socket does not exist."); return socket_packet_queues[socket_id].size(); } Array get_sockets() { Array ret; for (KeyValue> &E : socket_packet_queues) { ret.push_back(E.key); } return ret; } bool has_socket(const String &socket_id) { return socket_packet_queues.has(socket_id); } int get_queue_size_limit() { return max_queue_size; } void set_queue_size_limit(int limit) { ERR_FAIL_COND_MSG(limit < 1, "Cannot set queue size limit. Limit must be greater than 0"); max_queue_size = limit; } int get_connection_request_count() { return pending_connection_requests.size(); } int get_packet_count_from_remote_user(const String &remote_user, const String &socket_id); bool poll_next_packet(const String &socket_id, PacketData *out_packet); bool next_packet_is_peer_id_packet(const String &socket_id); bool register_peer(EOSGMultiplayerPeer *peer); void unregister_peer(EOSGMultiplayerPeer *peer); void clear_packet_queue(const String &socket_id); void clear_packets_from_remote_user(const String &socket_id, const String &remote_user_id); EOSGPacketPeerMediator(); ~EOSGPacketPeerMediator(); }; } //namespace godot ================================================ FILE: src/eosg_playerdatastorage_file_transfer_request.cpp ================================================ #include "eosg_playerdatastorage_file_transfer_request.h" #include "eos_playerdatastorage.h" #include "utils.h" using namespace godot; void EOSGPlayerDataStorageFileTransferRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file_request_state"), &EOSGPlayerDataStorageFileTransferRequest::get_file_request_state); ClassDB::bind_method(D_METHOD("get_filename"), &EOSGPlayerDataStorageFileTransferRequest::get_filename); ClassDB::bind_method(D_METHOD("cancel_request"), &EOSGPlayerDataStorageFileTransferRequest::cancel_request); ADD_SIGNAL(MethodInfo("file_transfer_progress_callback", PropertyInfo(Variant::DICTIONARY, "callback_data"))); } int EOSGPlayerDataStorageFileTransferRequest::get_file_request_state() { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); return static_cast(EOS_PlayerDataStorageFileTransferRequest_GetFileRequestState(m_internal)); } Dictionary EOSGPlayerDataStorageFileTransferRequest::get_filename() { ERR_FAIL_NULL_V(m_internal, {}); Dictionary ret; if (m_internal == nullptr) { ret["result_code"] = static_cast(EOS_EResult::EOS_InvalidState); ret["filename"] = ""; return ret; } char *outBuffer = (char *)(memalloc(EOS_PLAYERDATASTORAGE_FILENAME_MAX_LENGTH_BYTES + 1)); int outBufferLength = EOS_PLAYERDATASTORAGE_FILENAME_MAX_LENGTH_BYTES + 1; EOS_EResult result = EOS_PlayerDataStorageFileTransferRequest_GetFilename(m_internal, EOS_PLAYERDATASTORAGE_FILENAME_MAX_LENGTH_BYTES + 1, outBuffer, &outBufferLength); ret["result_code"] = static_cast(result); ret["filename"] = EOSG_GET_STRING(outBuffer); memfree(outBuffer); return ret; } int EOSGPlayerDataStorageFileTransferRequest::cancel_request() { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); return static_cast(EOS_PlayerDataStorageFileTransferRequest_CancelRequest(m_internal)); } ================================================ FILE: src/eosg_playerdatastorage_file_transfer_request.h ================================================ #pragma once #include "eos_playerdatastorage.h" #include "eosg_file_transfer_request.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGPlayerDataStorageFileTransferRequest : public EOSGFileTransferRequest { GDCLASS(EOSGPlayerDataStorageFileTransferRequest, EOSGFileTransferRequest) private: EOS_HPlayerDataStorageFileTransferRequest m_internal = nullptr; static void _bind_methods(); public: Dictionary get_filename() override; int cancel_request() override; int get_file_request_state() override; EOSGPlayerDataStorageFileTransferRequest(){}; ~EOSGPlayerDataStorageFileTransferRequest() { if (m_internal != nullptr) { EOS_PlayerDataStorageFileTransferRequest_Release(m_internal); m_internal = nullptr; } } void set_internal(EOS_HPlayerDataStorageFileTransferRequest p_internal) { m_internal = p_internal; } EOS_HPlayerDataStorageFileTransferRequest get_internal() { return m_internal; } }; } //namespace godot ================================================ FILE: src/eosg_presence_modification.cpp ================================================ #include "eosg_presence_modification.h" #include "utils.h" using namespace godot; void EOSGPresenceModification::_bind_methods() { ClassDB::bind_method(D_METHOD("set_status", "new_status"), &EOSGPresenceModification::set_status); ClassDB::bind_method(D_METHOD("set_raw_rich_text", "new_raw_rich_text"), &EOSGPresenceModification::set_raw_rich_text); ClassDB::bind_method(D_METHOD("set_data", "new_data"), &EOSGPresenceModification::set_data); ClassDB::bind_method(D_METHOD("delete_data", "keys"), &EOSGPresenceModification::delete_data); ClassDB::bind_method(D_METHOD("set_join_info", "new_join_info"), &EOSGPresenceModification::set_join_info); } int EOSGPresenceModification::set_status(int new_status) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_PresenceModification_SetStatusOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_SETPRESENCE_API_LATEST; options.Status = static_cast(new_status); return static_cast(EOS_PresenceModification_SetStatus(m_internal, &options)); } int EOSGPresenceModification::set_raw_rich_text(const String &p_raw_rich_text) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString new_raw_rich_text = VARIANT_TO_CHARSTRING(p_raw_rich_text); EOS_PresenceModification_SetRawRichTextOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_SETRAWRICHTEXT_API_LATEST; options.RichText = new_raw_rich_text.get_data(); return static_cast(EOS_PresenceModification_SetRawRichText(m_internal, &options)); } int EOSGPresenceModification::set_data(Dictionary p_data) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); int records_count = p_data.keys().size(); EOS_Presence_DataRecord *records = (EOS_Presence_DataRecord *)memalloc(sizeof(EOS_Presence_DataRecord) * records_count); PERSISTENT_CHAR_ARRAY_CREATE(data_keys, data_keys_charstrings, records_count); PERSISTENT_CHAR_ARRAY_CREATE(data_values, data_values_charstrings, records_count); for (int i = 0; i < records_count; i++) { PERSISTENT_CHAR_ARRAY_SET(data_keys, data_keys_charstrings, i, p_data.keys()[i]); PERSISTENT_CHAR_ARRAY_SET(data_values, data_values_charstrings, i, p_data[p_data.keys()[i]]); records[i].ApiVersion = EOS_PRESENCE_DATARECORD_API_LATEST; records[i].Key = data_keys[i]; records[i].Value = data_values[i]; } EOS_PresenceModification_SetDataOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCEMODIFICATION_SETDATA_API_LATEST; options.RecordsCount = static_cast(records_count); options.Records = records; return static_cast(EOS_PresenceModification_SetData(m_internal, &options)); } int EOSGPresenceModification::delete_data(Array p_keys) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); int records_count = p_keys.size(); EOS_PresenceModification_DataRecordId *records = (EOS_PresenceModification_DataRecordId *)memalloc(sizeof(EOS_PresenceModification_DataRecordId) * records_count); PERSISTENT_CHAR_ARRAY_CREATE(data_keys, data_keys_charstrings, records_count) for (int i = 0; i < records_count; i++) { PERSISTENT_CHAR_ARRAY_SET(data_keys, data_keys_charstrings, i, p_keys[i]); records[i].ApiVersion = EOS_PRESENCEMODIFICATION_DATARECORDID_API_LATEST; records[i].Key = data_keys[i]; } EOS_PresenceModification_DeleteDataOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_DELETEDATA_API_LATEST; options.RecordsCount = records_count; options.Records = records; return static_cast(EOS_PresenceModification_DeleteData(m_internal, &options)); } int EOSGPresenceModification::set_join_info(const String &p_join_info) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString new_join_info = VARIANT_TO_CHARSTRING(p_join_info); EOS_PresenceModification_SetJoinInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCEMODIFICATION_SETJOININFO_API_LATEST; options.JoinInfo = new_join_info.get_data(); return static_cast(EOS_PresenceModification_SetJoinInfo(m_internal, &options)); } ================================================ FILE: src/eosg_presence_modification.h ================================================ #pragma once #include "eos_presence.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGPresenceModification : public RefCounted { GDCLASS(EOSGPresenceModification, RefCounted) protected: EOS_HPresenceModification m_internal = nullptr; static void _bind_methods(); public: int delete_data(Array p_keys); int set_data(Dictionary p_data); int set_join_info(const String &p_join_info); int set_raw_rich_text(const String &p_raw_rich_text); int set_status(int new_status); EOSGPresenceModification(){}; ~EOSGPresenceModification() { if (m_internal != nullptr) { EOS_PresenceModification_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HPresenceModification p_internal) { m_internal = p_internal; } EOS_HPresenceModification get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_session_details.cpp ================================================ #include "eosg_session_details.h" #include "utils.h" using namespace godot; void EOSGSessionDetails::_bind_methods() { ClassDB::bind_method(D_METHOD("copy_info"), &EOSGSessionDetails::copy_info); ClassDB::bind_method(D_METHOD("copy_session_attribute_by_index", "attribute_index"), &EOSGSessionDetails::copy_session_attribute_by_index); ClassDB::bind_method(D_METHOD("copy_session_attribute_by_key", "attribute_key"), &EOSGSessionDetails::copy_session_attribute_by_key); ClassDB::bind_method(D_METHOD("get_session_attribute_count"), &EOSGSessionDetails::get_session_attribute_count); } Dictionary EOSGSessionDetails::copy_info() { ERR_FAIL_NULL_V(m_internal, {}); EOS_SessionDetails_CopyInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONDETAILS_COPYINFO_API_LATEST; EOS_SessionDetails_Info *outInfo = nullptr; EOS_EResult res = EOS_SessionDetails_CopyInfo(m_internal, &options, &outInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["info"] = eosg_sessions_session_details_info_to_dict_and_release(outInfo); return ret; } Dictionary EOSGSessionDetails::copy_session_attribute_by_index(int p_attribute_index) { ERR_FAIL_NULL_V(m_internal, {}); EOS_SessionDetails_CopySessionAttributeByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONDETAILS_COPYSESSIONATTRIBUTEBYINDEX_API_LATEST; options.AttrIndex = static_cast(p_attribute_index); EOS_SessionDetails_Attribute *outAttr = nullptr; EOS_EResult res = EOS_SessionDetails_CopySessionAttributeByIndex(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_sessions_session_details_attribute_to_dict_and_release(outAttr); return ret; } Dictionary EOSGSessionDetails::copy_session_attribute_by_key(const String &p_attribute_key) { ERR_FAIL_NULL_V(m_internal, {}); CharString attribute_key = p_attribute_key.utf8(); EOS_SessionDetails_CopySessionAttributeByKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONDETAILS_COPYSESSIONATTRIBUTEBYKEY_API_LATEST; options.AttrKey = attribute_key.get_data(); EOS_SessionDetails_Attribute *outAttr = nullptr; EOS_EResult res = EOS_SessionDetails_CopySessionAttributeByKey(m_internal, &options, &outAttr); Dictionary ret; ret["result_code"] = static_cast(res); ret["attribute"] = eosg_sessions_session_details_attribute_to_dict_and_release(outAttr); return ret; } int EOSGSessionDetails::get_session_attribute_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_SessionDetails_GetSessionAttributeCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONDETAILS_GETSESSIONATTRIBUTECOUNT_API_LATEST; return static_cast(EOS_SessionDetails_GetSessionAttributeCount(m_internal, &options)); } ================================================ FILE: src/eosg_session_details.h ================================================ #pragma once #include "eos_sessions.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGSessionDetails : public RefCounted { GDCLASS(EOSGSessionDetails, RefCounted) private: EOS_HSessionDetails m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_info(); Dictionary copy_session_attribute_by_index(int attribute_index); Dictionary copy_session_attribute_by_key(const String &attribute_key); int get_session_attribute_count(); EOSGSessionDetails(){}; ~EOSGSessionDetails() { if (m_internal != nullptr) { EOS_SessionDetails_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HSessionDetails p_internal) { m_internal = p_internal; } EOS_HSessionDetails get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_session_modification.cpp ================================================ #include "eosg_session_modification.h" #include "utils.h" using namespace godot; void EOSGSessionModification::_bind_methods() { ClassDB::bind_method(D_METHOD("add_attribute", "key", "value", "advertisement_type"), &EOSGSessionModification::add_attribute); ClassDB::bind_method(D_METHOD("remove_attribute", "key"), &EOSGSessionModification::remove_attribute); ClassDB::bind_method(D_METHOD("set_allowed_platform_ids", "allowed_platform_ids"), &EOSGSessionModification::set_allowed_platform_ids); ClassDB::bind_method(D_METHOD("set_bucket_id", "bucket_id"), &EOSGSessionModification::set_bucket_id); ClassDB::bind_method(D_METHOD("set_host_address", "host_address"), &EOSGSessionModification::set_host_address); ClassDB::bind_method(D_METHOD("set_invites_allowed", "invites_allowed"), &EOSGSessionModification::set_invites_allowed); ClassDB::bind_method(D_METHOD("set_max_players", "max_players"), &EOSGSessionModification::set_max_players); ClassDB::bind_method(D_METHOD("set_join_in_progress_allowed", "join_in_progress_allowed"), &EOSGSessionModification::set_join_in_progress_allowed); ClassDB::bind_method(D_METHOD("set_permission_level", "permission_level"), &EOSGSessionModification::set_permission_level); } int EOSGSessionModification::add_attribute(const String &p_key, Variant p_value, int p_advertisement_type) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_Sessions_AttributeData sessionAttribute; memset(&sessionAttribute, 0, sizeof(sessionAttribute)); sessionAttribute.ApiVersion = EOS_SESSIONS_ATTRIBUTEDATA_API_LATEST; sessionAttribute.Key = key.get_data(); CharString value; if (p_value.get_type() == Variant::Type::BOOL) { sessionAttribute.ValueType = EOS_ESessionAttributeType::EOS_AT_BOOLEAN; sessionAttribute.Value.AsBool = p_value; } else if (p_value.get_type() == Variant::Type::INT) { sessionAttribute.ValueType = EOS_ESessionAttributeType::EOS_AT_INT64; sessionAttribute.Value.AsInt64 = p_value; } else if (p_value.get_type() == Variant::Type::FLOAT) { sessionAttribute.ValueType = EOS_ESessionAttributeType::EOS_AT_DOUBLE; sessionAttribute.Value.AsDouble = p_value; } else if (p_value.get_type() == Variant::Type::STRING) { value = VARIANT_TO_CHARSTRING(p_value); sessionAttribute.ValueType = EOS_ESessionAttributeType::EOS_AT_STRING; sessionAttribute.Value.AsUtf8 = value.get_data(); } EOS_SessionModification_AddAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_ADDATTRIBUTE_API_LATEST; options.SessionAttribute = &sessionAttribute; options.AdvertisementType = static_cast(p_advertisement_type); return static_cast(EOS_SessionModification_AddAttribute(m_internal, &options)); } int EOSGSessionModification::remove_attribute(const String &p_key) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_SessionModification_RemoveAttributeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_REMOVEATTRIBUTE_API_LATEST; options.Key = key.get_data(); return static_cast(EOS_SessionModification_RemoveAttribute(m_internal, &options)); } int EOSGSessionModification::set_allowed_platform_ids(const TypedArray &p_platform_ids) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionModification_SetAllowedPlatformIdsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETALLOWEDPLATFORMIDS_API_LATEST; options.AllowedPlatformIdsCount = p_platform_ids.size(); uint32_t *platform_ids_array = (uint32_t *)memalloc(sizeof(uint32_t) * options.AllowedPlatformIdsCount); for (int i = 0; i < options.AllowedPlatformIdsCount; i++) { platform_ids_array[i] = static_cast(p_platform_ids[i]); } options.AllowedPlatformIds = platform_ids_array; int result = static_cast(EOS_SessionModification_SetAllowedPlatformIds(m_internal, &options)); memfree(platform_ids_array); return result; } int EOSGSessionModification::set_bucket_id(const String &p_bucket_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString bucket_id = p_bucket_id.utf8(); EOS_SessionModification_SetBucketIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETBUCKETID_API_LATEST; options.BucketId = bucket_id.get_data(); return static_cast(EOS_SessionModification_SetBucketId(m_internal, &options)); } int EOSGSessionModification::set_host_address(const String &p_host_address) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString host_address = p_host_address.utf8(); EOS_SessionModification_SetHostAddressOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETHOSTADDRESS_API_LATEST; options.HostAddress = host_address.get_data(); return static_cast(EOS_SessionModification_SetHostAddress(m_internal, &options)); } int EOSGSessionModification::set_invites_allowed(bool p_invites_allowed) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionModification_SetInvitesAllowedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETINVITESALLOWED_API_LATEST; options.bInvitesAllowed = p_invites_allowed ? EOS_TRUE : EOS_FALSE; return static_cast(EOS_SessionModification_SetInvitesAllowed(m_internal, &options)); } int EOSGSessionModification::set_max_players(int p_max_players) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionModification_SetMaxPlayersOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETMAXPLAYERS_API_LATEST; options.MaxPlayers = static_cast(p_max_players); return static_cast(EOS_SessionModification_SetMaxPlayers(m_internal, &options)); } int EOSGSessionModification::set_join_in_progress_allowed(bool p_join_in_progress_allowed) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionModification_SetJoinInProgressAllowedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETJOININPROGRESSALLOWED_API_LATEST; options.bAllowJoinInProgress = p_join_in_progress_allowed ? EOS_TRUE : EOS_FALSE; return static_cast(EOS_SessionModification_SetJoinInProgressAllowed(m_internal, &options)); } int EOSGSessionModification::set_permission_level(int p_permission_level) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionModification_SetPermissionLevelOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONMODIFICATION_SETPERMISSIONLEVEL_API_LATEST; options.PermissionLevel = static_cast(p_permission_level); return static_cast(EOS_SessionModification_SetPermissionLevel(m_internal, &options)); } ================================================ FILE: src/eosg_session_modification.h ================================================ #pragma once #include "eos_sessions.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGSessionModification : public RefCounted { GDCLASS(EOSGSessionModification, RefCounted) private: EOS_HSessionModification m_internal = nullptr; static void _bind_methods(); public: int add_attribute(const String &key, Variant value, int advertisement_type); int remove_attribute(const String &key); int set_allowed_platform_ids(const TypedArray &p_platform_ids); int set_bucket_id(const String &bucket_id); int set_host_address(const String &host_address); int set_invites_allowed(bool invites_allowed); int set_join_in_progress_allowed(bool join_in_progress_allowed); int set_max_players(int max_players); int set_permission_level(int permission_level); EOSGSessionModification() {}; ~EOSGSessionModification() { if (m_internal != nullptr) { EOS_SessionModification_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HSessionModification p_internal) { m_internal = p_internal; } EOS_HSessionModification get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_session_search.cpp ================================================ #include "eosg_session_search.h" #include "utils.h" using namespace godot; void EOSGSessionSearch::_bind_methods() { ClassDB::bind_method(D_METHOD("copy_search_result_by_index", "index"), &EOSGSessionSearch::copy_search_result_by_index); ClassDB::bind_method(D_METHOD("get_search_result_count"), &EOSGSessionSearch::get_search_result_count); ClassDB::bind_method(D_METHOD("remove_parameter", "key", "comparison_op"), &EOSGSessionSearch::remove_parameter); ClassDB::bind_method(D_METHOD("set_max_results", "max_results"), &EOSGSessionSearch::set_max_results); ClassDB::bind_method(D_METHOD("set_parameter", "key", "value", "comparison_op"), &EOSGSessionSearch::set_parameter); ClassDB::bind_method(D_METHOD("set_session_id", "session_id"), &EOSGSessionSearch::set_session_id); ClassDB::bind_method(D_METHOD("set_target_user_id", "target_user_id"), &EOSGSessionSearch::set_target_user_id); ClassDB::bind_method(D_METHOD("find", "local_user_id"), &EOSGSessionSearch::find); } Dictionary EOSGSessionSearch::copy_search_result_by_index(int p_index) { ERR_FAIL_NULL_V(m_internal, {}); EOS_SessionSearch_CopySearchResultByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_COPYSEARCHRESULTBYINDEX_API_LATEST; options.SessionIndex = static_cast(p_index); EOS_HSessionDetails outInfo = nullptr; EOS_EResult res = EOS_SessionSearch_CopySearchResultByIndex(m_internal, &options, &outInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_details"] = eosg_sessions_session_details_to_wrapper(outInfo); return ret; } int EOSGSessionSearch::get_search_result_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_SessionSearch_GetSearchResultCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_GETSEARCHRESULTCOUNT_API_LATEST; int ret = EOS_SessionSearch_GetSearchResultCount(m_internal, &options); return ret; } int EOSGSessionSearch::remove_parameter(const String &p_key, int p_comparison_op) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_SessionSearch_RemoveParameterOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_REMOVEPARAMETER_API_LATEST; options.ComparisonOp = static_cast(p_comparison_op); options.Key = key.get_data(); EOS_EResult ret = EOS_SessionSearch_RemoveParameter(m_internal, &options); return static_cast(ret); } int EOSGSessionSearch::set_max_results(int p_max_results) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); EOS_SessionSearch_SetMaxResultsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_SETMAXSEARCHRESULTS_API_LATEST; options.MaxSearchResults = static_cast(p_max_results); EOS_EResult ret = EOS_SessionSearch_SetMaxResults(m_internal, &options); return static_cast(ret); } int EOSGSessionSearch::set_parameter(const String &p_key, Variant p_value, int p_comparison_op) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = p_key.utf8(); EOS_Sessions_AttributeData attributeData; memset(&attributeData, 0, sizeof(attributeData)); attributeData.ApiVersion = EOS_SESSIONS_ATTRIBUTEDATA_API_LATEST; attributeData.Key = key.get_data(); CharString value; if (p_value.get_type() == Variant::Type::BOOL) { attributeData.ValueType = EOS_ESessionAttributeType::EOS_AT_BOOLEAN; attributeData.Value.AsBool = p_value; } else if (p_value.get_type() == Variant::Type::INT) { attributeData.ValueType = EOS_ESessionAttributeType::EOS_AT_INT64; attributeData.Value.AsInt64 = p_value; } else if (p_value.get_type() == Variant::Type::FLOAT) { attributeData.ValueType = EOS_ESessionAttributeType::EOS_AT_DOUBLE; attributeData.Value.AsDouble = p_value; } else if (p_value.get_type() == Variant::Type::STRING) { value = VARIANT_TO_CHARSTRING(p_value); attributeData.ValueType = EOS_ESessionAttributeType::EOS_AT_STRING; attributeData.Value.AsUtf8 = value.get_data(); } EOS_SessionSearch_SetParameterOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_SETPARAMETER_API_LATEST; options.ComparisonOp = static_cast(p_comparison_op); options.Parameter = &attributeData; return static_cast(EOS_SessionSearch_SetParameter(m_internal, &options)); } int EOSGSessionSearch::set_session_id(const String &p_session_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString session_id = p_session_id.utf8(); EOS_SessionSearch_SetSessionIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_SETSESSIONID_API_LATEST; options.SessionId = session_id.get_data(); EOS_EResult ret = EOS_SessionSearch_SetSessionId(m_internal, &options); return static_cast(ret); } int EOSGSessionSearch::set_target_user_id(const String &p_target_user_id) { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); CharString target_user_id = p_target_user_id.utf8(); EOS_SessionSearch_SetTargetUserIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_SETTARGETUSERID_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); EOS_EResult ret = EOS_SessionSearch_SetTargetUserId(m_internal, &options); return static_cast(ret); } void EOSGSessionSearch::find(const String &p_local_user_id) { ERR_FAIL_NULL(m_internal); CharString local_user_id = p_local_user_id.utf8(); EOS_SessionSearch_FindOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONSEARCH_FIND_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); // TODO: Handle passing ClientData if needed EOS_SessionSearch_Find(m_internal, &options, nullptr, [](const EOS_SessionSearch_FindCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); ret["client_data"] = Variant(); IEOS::get_singleton()->emit_signal("session_search_find_callback", ret); }); } ================================================ FILE: src/eosg_session_search.h ================================================ #pragma once #include "eos_sessions.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGSessionSearch : public RefCounted { GDCLASS(EOSGSessionSearch, RefCounted) private: EOS_HSessionSearch m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_search_result_by_index(int index); int get_search_result_count(); int remove_parameter(const String &key, int comparison_op); int set_max_results(int max_results); int set_parameter(const String &key, Variant value, int comparison_op); int set_session_id(const String &session_id); int set_target_user_id(const String &target_user_id); void find(const String &local_user_id); EOSGSessionSearch(){}; ~EOSGSessionSearch() { if (m_internal != nullptr) { EOS_SessionSearch_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_HSessionSearch p_internal) { m_internal = p_internal; } EOS_HSessionSearch get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/eosg_titlestorage_file_transfer_request.cpp ================================================ #include "eosg_titlestorage_file_transfer_request.h" #include "eos_titlestorage.h" #include "utils.h" using namespace godot; void EOSGTitleStorageFileTransferRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file_request_state"), &EOSGTitleStorageFileTransferRequest::get_file_request_state); ClassDB::bind_method(D_METHOD("get_filename"), &EOSGTitleStorageFileTransferRequest::get_filename); ClassDB::bind_method(D_METHOD("cancel_request"), &EOSGTitleStorageFileTransferRequest::cancel_request); ADD_SIGNAL(MethodInfo("file_transfer_progress_callback", PropertyInfo(Variant::DICTIONARY, "callback_data"))); } int EOSGTitleStorageFileTransferRequest::get_file_request_state() { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); return static_cast(EOS_TitleStorageFileTransferRequest_GetFileRequestState(m_internal)); } Dictionary EOSGTitleStorageFileTransferRequest::get_filename() { ERR_FAIL_NULL_V(m_internal, {}); char *outBuffer = (char *)(memalloc(EOS_TITLESTORAGE_FILENAME_MAX_LENGTH_BYTES + 1)); int outBufferLength = EOS_TITLESTORAGE_FILENAME_MAX_LENGTH_BYTES + 1; EOS_EResult result = EOS_TitleStorageFileTransferRequest_GetFilename(m_internal, EOS_TITLESTORAGE_FILENAME_MAX_LENGTH_BYTES + 1, outBuffer, &outBufferLength); Dictionary ret; ret["result_code"] = static_cast(result); ret["filename"] = EOSG_GET_STRING(outBuffer); memfree(outBuffer); return ret; } int EOSGTitleStorageFileTransferRequest::cancel_request() { ERR_FAIL_NULL_V(m_internal, static_cast(EOS_EResult::EOS_InvalidState)); return static_cast(EOS_TitleStorageFileTransferRequest_CancelRequest(m_internal)); } ================================================ FILE: src/eosg_titlestorage_file_transfer_request.h ================================================ #pragma once #include "eos_titlestorage.h" #include "eosg_file_transfer_request.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGTitleStorageFileTransferRequest : public EOSGFileTransferRequest { GDCLASS(EOSGTitleStorageFileTransferRequest, EOSGFileTransferRequest) private: EOS_HTitleStorageFileTransferRequest m_internal = nullptr; static void _bind_methods(); public: Dictionary get_filename() override; int cancel_request() override; int get_file_request_state() override; EOSGTitleStorageFileTransferRequest(){}; ~EOSGTitleStorageFileTransferRequest() { if (m_internal != nullptr) { EOS_TitleStorageFileTransferRequest_Release(m_internal); m_internal = nullptr; } } void set_internal(EOS_HTitleStorageFileTransferRequest p_internal) { m_internal = p_internal; } EOS_HTitleStorageFileTransferRequest get_internal() { return m_internal; } }; } //namespace godot ================================================ FILE: src/eosg_transaction.cpp ================================================ #include "eosg_transaction.h" #include "utils.h" using namespace godot; void EOSGTransaction::_bind_methods() { ClassDB::bind_method(D_METHOD("get_id"), &EOSGTransaction::get_id); ClassDB::bind_method(D_METHOD("get_entitlement_count"), &EOSGTransaction::get_entitlement_count); ClassDB::bind_method(D_METHOD("copy_entitlement_by_index", "entitlement_index"), &EOSGTransaction::copy_entitlement_by_index); }; String EOSGTransaction::get_id() { ERR_FAIL_NULL_V(m_internal, ""); char *outBuffer = (char *)memalloc(256); int32_t outLength = 0; EOS_Ecom_Transaction_GetTransactionId(m_internal, outBuffer, &outLength); return String(outBuffer); } int EOSGTransaction::get_entitlement_count() { ERR_FAIL_NULL_V(m_internal, 0); EOS_Ecom_Transaction_GetEntitlementsCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_TRANSACTION_GETENTITLEMENTSCOUNT_API_LATEST; return static_cast(EOS_Ecom_Transaction_GetEntitlementsCount(m_internal, &options)); } Dictionary EOSGTransaction::copy_entitlement_by_index(int p_entitlement_index) { ERR_FAIL_NULL_V(m_internal, {}); EOS_Ecom_Transaction_CopyEntitlementByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_ECOM_TRANSACTION_COPYENTITLEMENTBYINDEX_API_LATEST; options.EntitlementIndex = static_cast(p_entitlement_index); EOS_Ecom_Entitlement *outEntitlement; EOS_EResult res = EOS_Ecom_Transaction_CopyEntitlementByIndex(m_internal, &options, &outEntitlement); Dictionary ret; ret["result_code"] = static_cast(res); ret["entitlement"] = eosg_ecom_entitlement_to_dict_and_release(outEntitlement); return ret; } ================================================ FILE: src/eosg_transaction.h ================================================ #pragma once #include "eos_ecom.h" #include "godot_cpp/classes/ref_counted.hpp" namespace godot { class EOSGTransaction : public RefCounted { GDCLASS(EOSGTransaction, RefCounted) protected: EOS_Ecom_HTransaction m_internal = nullptr; static void _bind_methods(); public: Dictionary copy_entitlement_by_index(int p_entitlement_index); int get_entitlement_count(); String get_id(); EOSGTransaction(){}; ~EOSGTransaction() { if (m_internal != nullptr) { EOS_Ecom_Transaction_Release(m_internal); m_internal = nullptr; } }; void set_internal(EOS_Ecom_HTransaction p_internal) { m_internal = p_internal; } EOS_Ecom_HTransaction get_internal() { return m_internal; } }; } // namespace godot ================================================ FILE: src/friends_interface.cpp ================================================ #include "ieos.h" using namespace std; void IEOS::friends_interface_accept_invite(Ref p_options) { ERR_FAIL_NULL(s_friendsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Friends_AcceptInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_ACCEPTINVITE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_Friends_AcceptInvite(s_friendsInterface, &options, (void *)*p_options, [](const EOS_Friends_AcceptInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("friends_interface_accept_invite_callback", ret); }); } String IEOS::friends_interface_get_friend_at_index(Ref p_options) { ERR_FAIL_NULL_V(s_friendsInterface, ""); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Friends_GetFriendAtIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_GETFRIENDATINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Index = static_cast(p_options->get("index")); return eosg_epic_account_id_to_string(EOS_Friends_GetFriendAtIndex(s_friendsInterface, &options)); } int IEOS::friends_interface_get_friends_count(Ref p_options) { ERR_FAIL_NULL_V(s_friendsInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Friends_GetFriendsCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_GETFRIENDSCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Friends_GetFriendsCount(s_friendsInterface, &options)); } int IEOS::friends_interface_get_status(Ref p_options) { ERR_FAIL_NULL_V(s_friendsInterface, static_cast(EOS_EFriendsStatus::EOS_FS_NotFriends)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Friends_GetStatusOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_GETSTATUS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); return static_cast(EOS_Friends_GetStatus(s_friendsInterface, &options)); } void IEOS::friends_interface_query_friends(Ref p_options) { ERR_FAIL_NULL(s_friendsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Friends_QueryFriendsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_QUERYFRIENDS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); p_options->reference(); EOS_Friends_QueryFriends(s_friendsInterface, &options, (void *)*p_options, [](const EOS_Friends_QueryFriendsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("friends_interface_query_friends_callback", ret); }); } void IEOS::friends_interface_reject_invite(Ref p_options) { ERR_FAIL_NULL(s_friendsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Friends_RejectInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_REJECTINVITE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_Friends_RejectInvite(s_friendsInterface, &options, (void *)*p_options, [](const EOS_Friends_RejectInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("friends_interface_reject_invite_callback", ret); }); } void IEOS::friends_interface_send_invite(Ref p_options) { ERR_FAIL_NULL(s_friendsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Friends_SendInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_SENDINVITE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_Friends_SendInvite(s_friendsInterface, &options, (void *)*p_options, [](const EOS_Friends_SendInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("friends_interface_send_invite_callback", ret); }); } int IEOS::friends_interface_get_blocked_users_count(Ref p_options) { ERR_FAIL_NULL_V(s_friendsInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Friends_GetBlockedUsersCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_GETBLOCKEDUSERSCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return static_cast(EOS_Friends_GetBlockedUsersCount(s_friendsInterface, &options)); } String IEOS::friends_interface_get_blocked_user_at_index(Ref p_options) { ERR_FAIL_NULL_V(s_friendsInterface, ""); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int index = static_cast(p_options->get("index")); EOS_Friends_GetBlockedUserAtIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_FRIENDS_GETBLOCKEDUSERATINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Index = static_cast(index); return eosg_epic_account_id_to_string(EOS_Friends_GetBlockedUserAtIndex(s_friendsInterface, &options)); } ================================================ FILE: src/ieos.cpp ================================================ #include "ieos.h" using namespace godot; #define IEOS_BIND_METHOD(m_name) ClassDB::bind_method(D_METHOD(#m_name), &IEOS::m_name) #define IEOS_BIND_SIGNAL(m_name) ADD_SIGNAL(MethodInfo(#m_name, PropertyInfo(Variant::DICTIONARY, "callback_data"))) void IEOS::_bind_methods() { ClassDB::bind_static_method("IEOS", D_METHOD("tick"), &IEOS::tick); IEOS_BIND_METHOD(is_operation_complete); // EOS Methods IEOS_BIND_METHOD(achievements_interface_copy_achievement_definition_v2_by_achievement_id); IEOS_BIND_METHOD(achievements_interface_copy_achievement_definition_v2_by_index); IEOS_BIND_METHOD(achievements_interface_copy_player_achievement_by_achievement_id); IEOS_BIND_METHOD(achievements_interface_copy_player_achievement_by_index); IEOS_BIND_METHOD(achievements_interface_get_achievement_definition_count); IEOS_BIND_METHOD(achievements_interface_get_player_achievement_count); IEOS_BIND_METHOD(achievements_interface_query_definitions); IEOS_BIND_METHOD(achievements_interface_query_player_achievements); IEOS_BIND_METHOD(achievements_interface_unlock_achievements); IEOS_BIND_METHOD(auth_interface_copy_id_token); IEOS_BIND_METHOD(auth_interface_copy_user_auth_token); IEOS_BIND_METHOD(auth_interface_delete_persistent_auth); IEOS_BIND_METHOD(auth_interface_get_logged_in_account_by_index); IEOS_BIND_METHOD(auth_interface_get_logged_in_accounts_count); IEOS_BIND_METHOD(auth_interface_get_login_status); IEOS_BIND_METHOD(auth_interface_get_merged_account_by_index); IEOS_BIND_METHOD(auth_interface_get_merged_accounts_count); IEOS_BIND_METHOD(auth_interface_get_selected_account_id); IEOS_BIND_METHOD(auth_interface_link_account); IEOS_BIND_METHOD(auth_interface_login); IEOS_BIND_METHOD(auth_interface_logout); IEOS_BIND_METHOD(auth_interface_query_id_token); IEOS_BIND_METHOD(auth_interface_verify_id_token); IEOS_BIND_METHOD(auth_interface_verify_user_auth); IEOS_BIND_METHOD(connect_interface_copy_id_token); IEOS_BIND_METHOD(connect_interface_copy_product_user_external_account_by_account_id); IEOS_BIND_METHOD(connect_interface_copy_product_user_external_account_by_account_type); IEOS_BIND_METHOD(connect_interface_copy_product_user_external_account_by_index); IEOS_BIND_METHOD(connect_interface_copy_product_user_info); IEOS_BIND_METHOD(connect_interface_create_device_id); IEOS_BIND_METHOD(connect_interface_create_user); IEOS_BIND_METHOD(connect_interface_delete_device_id); IEOS_BIND_METHOD(connect_interface_get_external_account_mapping); IEOS_BIND_METHOD(connect_interface_get_logged_in_user_by_index); IEOS_BIND_METHOD(connect_interface_get_logged_in_users_count); IEOS_BIND_METHOD(connect_interface_get_login_status); IEOS_BIND_METHOD(connect_interface_get_product_user_external_account_count); IEOS_BIND_METHOD(connect_interface_get_product_user_id_mapping); IEOS_BIND_METHOD(connect_interface_link_account); IEOS_BIND_METHOD(connect_interface_login); IEOS_BIND_METHOD(connect_interface_logout); IEOS_BIND_METHOD(connect_interface_query_external_account_mappings); IEOS_BIND_METHOD(connect_interface_query_product_user_id_mappings); IEOS_BIND_METHOD(connect_interface_transfer_device_id_account); IEOS_BIND_METHOD(connect_interface_unlink_account); IEOS_BIND_METHOD(connect_interface_verify_id_token); IEOS_BIND_METHOD(custom_invites_interface_accept_request_to_join); IEOS_BIND_METHOD(custom_invites_interface_finalize_invite); IEOS_BIND_METHOD(custom_invites_interface_reject_request_to_join); IEOS_BIND_METHOD(custom_invites_interface_send_custom_invite); IEOS_BIND_METHOD(custom_invites_interface_send_request_to_join); IEOS_BIND_METHOD(custom_invites_interface_set_custom_invite); IEOS_BIND_METHOD(ecom_interface_checkout); IEOS_BIND_METHOD(ecom_interface_copy_entitlement_by_id); IEOS_BIND_METHOD(ecom_interface_copy_entitlement_by_index); IEOS_BIND_METHOD(ecom_interface_copy_entitlement_by_name_and_index); IEOS_BIND_METHOD(ecom_interface_copy_item_by_id); IEOS_BIND_METHOD(ecom_interface_copy_item_image_info_by_index); IEOS_BIND_METHOD(ecom_interface_copy_item_release_by_index); IEOS_BIND_METHOD(ecom_interface_copy_last_redeemed_entitlement_by_index); IEOS_BIND_METHOD(ecom_interface_copy_offer_by_id); IEOS_BIND_METHOD(ecom_interface_copy_offer_by_index); IEOS_BIND_METHOD(ecom_interface_copy_offer_image_info_by_index); IEOS_BIND_METHOD(ecom_interface_copy_offer_item_by_index); IEOS_BIND_METHOD(ecom_interface_copy_transaction_by_id); IEOS_BIND_METHOD(ecom_interface_copy_transaction_by_index); IEOS_BIND_METHOD(ecom_interface_get_entitlements_by_name_count); IEOS_BIND_METHOD(ecom_interface_get_entitlements_count); IEOS_BIND_METHOD(ecom_interface_get_item_image_info_count); IEOS_BIND_METHOD(ecom_interface_get_item_release_count); IEOS_BIND_METHOD(ecom_interface_get_last_redeemed_entitlements_count); IEOS_BIND_METHOD(ecom_interface_get_offer_count); IEOS_BIND_METHOD(ecom_interface_get_offer_image_info_count); IEOS_BIND_METHOD(ecom_interface_get_offer_item_count); IEOS_BIND_METHOD(ecom_interface_get_transaction_count); IEOS_BIND_METHOD(ecom_interface_query_entitlement_token); IEOS_BIND_METHOD(ecom_interface_query_entitlements); IEOS_BIND_METHOD(ecom_interface_query_offers); IEOS_BIND_METHOD(ecom_interface_query_ownership); IEOS_BIND_METHOD(ecom_interface_query_ownership_by_sandbox_ids); IEOS_BIND_METHOD(ecom_interface_query_ownership_token); IEOS_BIND_METHOD(ecom_interface_redeem_entitlements); IEOS_BIND_METHOD(friends_interface_accept_invite); IEOS_BIND_METHOD(friends_interface_get_blocked_user_at_index); IEOS_BIND_METHOD(friends_interface_get_blocked_users_count); IEOS_BIND_METHOD(friends_interface_get_friend_at_index); IEOS_BIND_METHOD(friends_interface_get_friends_count); IEOS_BIND_METHOD(friends_interface_get_status); IEOS_BIND_METHOD(friends_interface_query_friends); IEOS_BIND_METHOD(friends_interface_reject_invite); IEOS_BIND_METHOD(friends_interface_send_invite); IEOS_BIND_METHOD(kws_interface_copy_permission_by_index); IEOS_BIND_METHOD(kws_interface_create_user); IEOS_BIND_METHOD(kws_interface_get_permission_by_key); IEOS_BIND_METHOD(kws_interface_get_permissions_count); IEOS_BIND_METHOD(kws_interface_query_age_gate); IEOS_BIND_METHOD(kws_interface_query_permissions); IEOS_BIND_METHOD(kws_interface_request_permissions); IEOS_BIND_METHOD(kws_interface_update_parent_email); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_definition_by_index); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_definition_by_leaderboard_id); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_record_by_index); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_record_by_user_id); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_user_score_by_index); IEOS_BIND_METHOD(leaderboards_interface_copy_leaderboard_user_score_by_user_id); IEOS_BIND_METHOD(leaderboards_interface_get_leaderboard_definition_count); IEOS_BIND_METHOD(leaderboards_interface_get_leaderboard_record_count); IEOS_BIND_METHOD(leaderboards_interface_get_leaderboard_user_score_count); IEOS_BIND_METHOD(leaderboards_interface_query_leaderboard_definitions); IEOS_BIND_METHOD(leaderboards_interface_query_leaderboard_ranks); IEOS_BIND_METHOD(leaderboards_interface_query_leaderboard_user_scores); IEOS_BIND_METHOD(lobby_interface_copy_lobby_details); IEOS_BIND_METHOD(lobby_interface_copy_lobby_details_by_invite_id); IEOS_BIND_METHOD(lobby_interface_copy_lobby_details_by_ui_event_id); IEOS_BIND_METHOD(lobby_interface_create_lobby); IEOS_BIND_METHOD(lobby_interface_create_lobby_search); IEOS_BIND_METHOD(lobby_interface_destroy_lobby); IEOS_BIND_METHOD(lobby_interface_get_connect_string); IEOS_BIND_METHOD(lobby_interface_get_invite_count); IEOS_BIND_METHOD(lobby_interface_get_invite_id_by_index); IEOS_BIND_METHOD(lobby_interface_get_rtc_room_name); IEOS_BIND_METHOD(lobby_interface_hard_mute_member); IEOS_BIND_METHOD(lobby_interface_is_rtc_room_connected); IEOS_BIND_METHOD(lobby_interface_join_lobby); IEOS_BIND_METHOD(lobby_interface_join_lobby_by_id); IEOS_BIND_METHOD(lobby_interface_kick_member); IEOS_BIND_METHOD(lobby_interface_leave_lobby); IEOS_BIND_METHOD(lobby_interface_parse_connect_string); IEOS_BIND_METHOD(lobby_interface_promote_member); IEOS_BIND_METHOD(lobby_interface_query_invites); IEOS_BIND_METHOD(lobby_interface_reject_invite); IEOS_BIND_METHOD(lobby_interface_send_invite); IEOS_BIND_METHOD(lobby_interface_update_lobby); IEOS_BIND_METHOD(lobby_interface_update_lobby_modification); IEOS_BIND_METHOD(logging_interface_set_log_level); IEOS_BIND_METHOD(metrics_interface_begin_player_session); IEOS_BIND_METHOD(metrics_interface_end_player_session); IEOS_BIND_METHOD(mods_interface_copy_mod_info); IEOS_BIND_METHOD(mods_interface_enumerate_mods); IEOS_BIND_METHOD(mods_interface_install_mod); IEOS_BIND_METHOD(mods_interface_uninstall_mod); IEOS_BIND_METHOD(mods_interface_update_mod); IEOS_BIND_METHOD(p2p_interface_get_nat_type); IEOS_BIND_METHOD(p2p_interface_get_packet_queue_info); IEOS_BIND_METHOD(p2p_interface_get_port_range); IEOS_BIND_METHOD(p2p_interface_get_relay_control); IEOS_BIND_METHOD(p2p_interface_query_nat_type); IEOS_BIND_METHOD(p2p_interface_set_packet_queue_size); IEOS_BIND_METHOD(p2p_interface_set_port_range); IEOS_BIND_METHOD(p2p_interface_set_relay_control); IEOS_BIND_METHOD(platform_interface_check_for_launcher_and_restart); IEOS_BIND_METHOD(platform_interface_create); IEOS_BIND_METHOD(platform_interface_get_active_country_code); IEOS_BIND_METHOD(platform_interface_get_active_locale_code); IEOS_BIND_METHOD(platform_interface_get_application_status); IEOS_BIND_METHOD(platform_interface_get_desktop_crossplay_status_info); IEOS_BIND_METHOD(platform_interface_get_network_status); IEOS_BIND_METHOD(platform_interface_get_override_country_code); IEOS_BIND_METHOD(platform_interface_get_override_locale_code); IEOS_BIND_METHOD(platform_interface_initialize); IEOS_BIND_METHOD(platform_interface_release); IEOS_BIND_METHOD(platform_interface_set_application_status); IEOS_BIND_METHOD(platform_interface_set_network_status); IEOS_BIND_METHOD(platform_interface_set_override_country_code); IEOS_BIND_METHOD(platform_interface_set_override_locale_code); IEOS_BIND_METHOD(platform_interface_shutdown); IEOS_BIND_METHOD(playerdatastorage_interface_copy_file_metadata_at_index); IEOS_BIND_METHOD(playerdatastorage_interface_copy_file_metadata_by_filename); IEOS_BIND_METHOD(playerdatastorage_interface_delete_cache); IEOS_BIND_METHOD(playerdatastorage_interface_delete_file); IEOS_BIND_METHOD(playerdatastorage_interface_duplicate_file); IEOS_BIND_METHOD(playerdatastorage_interface_get_file_metadata_count); IEOS_BIND_METHOD(playerdatastorage_interface_query_file); IEOS_BIND_METHOD(playerdatastorage_interface_query_file_list); IEOS_BIND_METHOD(playerdatastorage_interface_read_file); IEOS_BIND_METHOD(playerdatastorage_interface_write_file); IEOS_BIND_METHOD(presence_interface_copy_presence); IEOS_BIND_METHOD(presence_interface_create_presence_modification); IEOS_BIND_METHOD(presence_interface_get_join_info); IEOS_BIND_METHOD(presence_interface_has_presence); IEOS_BIND_METHOD(presence_interface_query_presence); IEOS_BIND_METHOD(presence_interface_set_presence); IEOS_BIND_METHOD(progression_snapshot_interface_add_progression); IEOS_BIND_METHOD(progression_snapshot_interface_begin_snapshot); IEOS_BIND_METHOD(progression_snapshot_interface_delete_snapshot); IEOS_BIND_METHOD(progression_snapshot_interface_end_snapshot); IEOS_BIND_METHOD(progression_snapshot_interface_submit_snapshot); IEOS_BIND_METHOD(reports_interface_send_player_behavior_report); IEOS_BIND_METHOD(rtc_audio_interface_add_notify_audio_before_render); IEOS_BIND_METHOD(rtc_audio_interface_add_notify_audio_before_send); IEOS_BIND_METHOD(rtc_audio_interface_add_notify_audio_input_state); IEOS_BIND_METHOD(rtc_audio_interface_add_notify_audio_output_state); IEOS_BIND_METHOD(rtc_audio_interface_add_notify_participant_updated); IEOS_BIND_METHOD(rtc_audio_interface_copy_input_device_information_by_index); IEOS_BIND_METHOD(rtc_audio_interface_copy_output_device_information_by_index); IEOS_BIND_METHOD(rtc_audio_interface_get_input_devices_count); IEOS_BIND_METHOD(rtc_audio_interface_get_output_devices_count); IEOS_BIND_METHOD(rtc_audio_interface_query_input_devices_information); IEOS_BIND_METHOD(rtc_audio_interface_query_output_devices_information); IEOS_BIND_METHOD(rtc_audio_interface_register_platform_user); IEOS_BIND_METHOD(rtc_audio_interface_remove_notify_audio_before_render); IEOS_BIND_METHOD(rtc_audio_interface_remove_notify_audio_before_send); IEOS_BIND_METHOD(rtc_audio_interface_remove_notify_audio_input_state); IEOS_BIND_METHOD(rtc_audio_interface_remove_notify_audio_output_state); IEOS_BIND_METHOD(rtc_audio_interface_remove_notify_participant_updated); IEOS_BIND_METHOD(rtc_audio_interface_send_audio); IEOS_BIND_METHOD(rtc_audio_interface_set_input_device_settings); IEOS_BIND_METHOD(rtc_audio_interface_set_output_device_settings); IEOS_BIND_METHOD(rtc_audio_interface_unregister_platform_user); IEOS_BIND_METHOD(rtc_audio_interface_update_participant_volume); IEOS_BIND_METHOD(rtc_audio_interface_update_receiving); IEOS_BIND_METHOD(rtc_audio_interface_update_receiving_volume); IEOS_BIND_METHOD(rtc_audio_interface_update_sending); IEOS_BIND_METHOD(rtc_audio_interface_update_sending_volume); IEOS_BIND_METHOD(rtc_data_interface_add_notify_data_received); IEOS_BIND_METHOD(rtc_data_interface_add_notify_participant_updated); IEOS_BIND_METHOD(rtc_data_interface_send_data); IEOS_BIND_METHOD(rtc_data_interface_remove_notify_data_received); IEOS_BIND_METHOD(rtc_data_interface_remove_notify_participant_updated); IEOS_BIND_METHOD(rtc_data_interface_update_receiving); IEOS_BIND_METHOD(rtc_data_interface_update_sending); IEOS_BIND_METHOD(rtc_interface_add_notify_disconnected); IEOS_BIND_METHOD(rtc_interface_add_notify_participant_status_changed); IEOS_BIND_METHOD(rtc_interface_add_notify_room_statistics_updated); IEOS_BIND_METHOD(rtc_interface_block_participant); IEOS_BIND_METHOD(rtc_interface_join_room); IEOS_BIND_METHOD(rtc_interface_leave_room); IEOS_BIND_METHOD(rtc_interface_remove_notify_disconnected); IEOS_BIND_METHOD(rtc_interface_remove_notify_participant_status_changed); IEOS_BIND_METHOD(rtc_interface_remove_notify_room_statistics_updated); IEOS_BIND_METHOD(rtc_interface_set_room_setting); IEOS_BIND_METHOD(rtc_interface_set_setting); IEOS_BIND_METHOD(sanctions_interface_copy_player_sanction_by_index); IEOS_BIND_METHOD(sanctions_interface_get_player_sanction_count); IEOS_BIND_METHOD(sanctions_interface_query_active_player_sanctions); IEOS_BIND_METHOD(sanctions_interface_create_player_sanction_appeal); IEOS_BIND_METHOD(sessions_interface_copy_active_session_details); IEOS_BIND_METHOD(sessions_interface_copy_session_details_by_invite_id); IEOS_BIND_METHOD(sessions_interface_copy_session_details_by_ui_event_id); IEOS_BIND_METHOD(sessions_interface_copy_session_details_for_presence); IEOS_BIND_METHOD(sessions_interface_create_session_modification); IEOS_BIND_METHOD(sessions_interface_create_session_search); IEOS_BIND_METHOD(sessions_interface_destroy_session); IEOS_BIND_METHOD(sessions_interface_dump_session_state); IEOS_BIND_METHOD(sessions_interface_end_session); IEOS_BIND_METHOD(sessions_interface_get_invite_count); IEOS_BIND_METHOD(sessions_interface_get_invite_id_by_index); IEOS_BIND_METHOD(sessions_interface_is_user_in_session); IEOS_BIND_METHOD(sessions_interface_join_session); IEOS_BIND_METHOD(sessions_interface_query_invites); IEOS_BIND_METHOD(sessions_interface_register_players); IEOS_BIND_METHOD(sessions_interface_reject_invite); IEOS_BIND_METHOD(sessions_interface_send_invite); IEOS_BIND_METHOD(sessions_interface_start_session); IEOS_BIND_METHOD(sessions_interface_unregister_players); IEOS_BIND_METHOD(sessions_interface_update_session); IEOS_BIND_METHOD(sessions_interface_update_session_modification); IEOS_BIND_METHOD(stats_interface_copy_stat_by_index); IEOS_BIND_METHOD(stats_interface_copy_stat_by_name); IEOS_BIND_METHOD(stats_interface_get_stats_count); IEOS_BIND_METHOD(stats_interface_ingest_stat); IEOS_BIND_METHOD(stats_interface_query_stats); IEOS_BIND_METHOD(titlestorage_interface_copy_file_metadata_at_index); IEOS_BIND_METHOD(titlestorage_interface_copy_file_metadata_by_filename); IEOS_BIND_METHOD(titlestorage_interface_delete_cache); IEOS_BIND_METHOD(titlestorage_interface_get_file_metadata_count); IEOS_BIND_METHOD(titlestorage_interface_query_file); IEOS_BIND_METHOD(titlestorage_interface_query_file_list); IEOS_BIND_METHOD(titlestorage_interface_read_file); IEOS_BIND_METHOD(ui_interface_acknowledge_event_id); IEOS_BIND_METHOD(ui_interface_get_friends_exclusive_input); IEOS_BIND_METHOD(ui_interface_get_friends_visible); IEOS_BIND_METHOD(ui_interface_get_notification_location_preference); IEOS_BIND_METHOD(ui_interface_get_toggle_friends_button); IEOS_BIND_METHOD(ui_interface_get_toggle_friends_key); IEOS_BIND_METHOD(ui_interface_hide_friends); IEOS_BIND_METHOD(ui_interface_is_social_overlay_paused); IEOS_BIND_METHOD(ui_interface_is_valid_button_combination); IEOS_BIND_METHOD(ui_interface_is_valid_key_combination); IEOS_BIND_METHOD(ui_interface_pause_social_overlay); IEOS_BIND_METHOD(ui_interface_pre_present); IEOS_BIND_METHOD(ui_interface_report_input_state); IEOS_BIND_METHOD(ui_interface_set_display_preference); IEOS_BIND_METHOD(ui_interface_set_toggle_friends_button); IEOS_BIND_METHOD(ui_interface_set_toggle_friends_key); IEOS_BIND_METHOD(ui_interface_show_block_player); IEOS_BIND_METHOD(ui_interface_show_friends); IEOS_BIND_METHOD(ui_interface_show_native_profile); IEOS_BIND_METHOD(ui_interface_show_report_player); IEOS_BIND_METHOD(user_info_interface_copy_best_display_name); IEOS_BIND_METHOD(user_info_interface_copy_best_display_name_with_platform); IEOS_BIND_METHOD(user_info_interface_copy_external_user_info_by_account_id); IEOS_BIND_METHOD(user_info_interface_copy_external_user_info_by_account_type); IEOS_BIND_METHOD(user_info_interface_copy_external_user_info_by_index); IEOS_BIND_METHOD(user_info_interface_copy_user_info); IEOS_BIND_METHOD(user_info_interface_get_external_user_info_count); IEOS_BIND_METHOD(user_info_interface_get_local_platform_type); IEOS_BIND_METHOD(user_info_interface_query_user_info); IEOS_BIND_METHOD(user_info_interface_query_user_info_by_display_name); IEOS_BIND_METHOD(user_info_interface_query_user_info_by_external_account); IEOS_BIND_METHOD(version_interface_get_constants); IEOS_BIND_METHOD(version_interface_get_version); IEOS_BIND_METHOD(anticheat_server_interface_begin_session); IEOS_BIND_METHOD(anticheat_server_interface_end_session); IEOS_BIND_METHOD(anticheat_server_interface_register_client); IEOS_BIND_METHOD(anticheat_server_interface_unregister_client); IEOS_BIND_METHOD(anticheat_server_interface_receive_message_from_client); IEOS_BIND_METHOD(anticheat_server_interface_set_client_details); IEOS_BIND_METHOD(anticheat_server_interface_set_game_session_id); IEOS_BIND_METHOD(anticheat_server_interface_set_client_network_state); IEOS_BIND_METHOD(anticheat_server_interface_get_protect_message_output_length); IEOS_BIND_METHOD(anticheat_server_interface_protect_message); IEOS_BIND_METHOD(anticheat_server_interface_unprotect_message); IEOS_BIND_METHOD(anticheat_server_interface_register_event); IEOS_BIND_METHOD(anticheat_server_interface_log_event); IEOS_BIND_METHOD(anticheat_server_interface_log_game_round_start); IEOS_BIND_METHOD(anticheat_server_interface_log_game_round_end); IEOS_BIND_METHOD(anticheat_server_interface_log_player_spawn); IEOS_BIND_METHOD(anticheat_server_interface_log_player_despawn); IEOS_BIND_METHOD(anticheat_server_interface_log_player_revive); IEOS_BIND_METHOD(anticheat_server_interface_log_player_tick); IEOS_BIND_METHOD(anticheat_server_interface_log_player_use_weapon); IEOS_BIND_METHOD(anticheat_server_interface_log_player_use_ability); IEOS_BIND_METHOD(anticheat_server_interface_log_player_take_damage); IEOS_BIND_METHOD(anticheat_client_interface_begin_session); IEOS_BIND_METHOD(anticheat_client_interface_end_session); IEOS_BIND_METHOD(anticheat_client_interface_add_external_integrity_catalog); IEOS_BIND_METHOD(anticheat_client_interface_receive_message_from_server); IEOS_BIND_METHOD(anticheat_client_interface_get_protect_message_output_length); IEOS_BIND_METHOD(anticheat_client_interface_protect_message); IEOS_BIND_METHOD(anticheat_client_interface_unprotect_message); IEOS_BIND_METHOD(anticheat_client_interface_register_peer); IEOS_BIND_METHOD(anticheat_client_interface_unregister_peer); IEOS_BIND_METHOD(anticheat_client_interface_receive_message_from_peer); ADD_SIGNAL(MethodInfo("logging_interface_callback", PropertyInfo(Variant::DICTIONARY, "log_message"))); IEOS_BIND_SIGNAL(achievements_interface_achievements_unlocked_v2_callback); IEOS_BIND_SIGNAL(achievements_interface_query_definitions_callback); IEOS_BIND_SIGNAL(achievements_interface_query_player_achievements_callback); IEOS_BIND_SIGNAL(achievements_interface_unlock_achievements_callback); IEOS_BIND_SIGNAL(auth_interface_delete_persistent_auth_callback); IEOS_BIND_SIGNAL(auth_interface_link_account_callback); IEOS_BIND_SIGNAL(auth_interface_login_callback); IEOS_BIND_SIGNAL(auth_interface_login_status_changed); IEOS_BIND_SIGNAL(auth_interface_logout_callback); IEOS_BIND_SIGNAL(auth_interface_query_id_token_callback); IEOS_BIND_SIGNAL(auth_interface_verify_id_token_callback); IEOS_BIND_SIGNAL(auth_interface_verify_user_auth_callback); IEOS_BIND_SIGNAL(connect_interface_auth_expiration); IEOS_BIND_SIGNAL(connect_interface_create_device_id_callback); IEOS_BIND_SIGNAL(connect_interface_create_user_callback); IEOS_BIND_SIGNAL(connect_interface_delete_device_id_callback); IEOS_BIND_SIGNAL(connect_interface_link_account_callback); IEOS_BIND_SIGNAL(connect_interface_login_callback); IEOS_BIND_SIGNAL(connect_interface_login_status_changed); IEOS_BIND_SIGNAL(connect_interface_logout_callback); IEOS_BIND_SIGNAL(connect_interface_query_external_account_mappings_callback); IEOS_BIND_SIGNAL(connect_interface_query_product_user_id_mappings_callback); IEOS_BIND_SIGNAL(connect_interface_transfer_device_id_account_callback); IEOS_BIND_SIGNAL(connect_interface_unlink_account_callback); IEOS_BIND_SIGNAL(connect_interface_verify_id_token_callback); IEOS_BIND_SIGNAL(custom_invites_interface_accept_request_to_join_callback); IEOS_BIND_SIGNAL(custom_invites_interface_custom_invite_accepted_callback); IEOS_BIND_SIGNAL(custom_invites_interface_custom_invite_received_callback); IEOS_BIND_SIGNAL(custom_invites_interface_custom_invite_rejected_callback); IEOS_BIND_SIGNAL(custom_invites_interface_reject_request_to_join_callback); IEOS_BIND_SIGNAL(custom_invites_interface_request_to_join_accepted_callback); IEOS_BIND_SIGNAL(custom_invites_interface_request_to_join_received_callback); IEOS_BIND_SIGNAL(custom_invites_interface_request_to_join_rejected_callback); IEOS_BIND_SIGNAL(custom_invites_interface_request_to_join_response_received_callback); IEOS_BIND_SIGNAL(custom_invites_interface_send_custom_invite_callback); IEOS_BIND_SIGNAL(custom_invites_interface_send_request_to_join_callback); IEOS_BIND_SIGNAL(ecom_interface_checkout_callback); IEOS_BIND_SIGNAL(ecom_interface_query_entitlement_token_callback); IEOS_BIND_SIGNAL(ecom_interface_query_entitlements_callback); IEOS_BIND_SIGNAL(ecom_interface_query_offers_callback); IEOS_BIND_SIGNAL(ecom_interface_query_ownership_by_sandbox_ids_callback); IEOS_BIND_SIGNAL(ecom_interface_query_ownership_callback); IEOS_BIND_SIGNAL(ecom_interface_query_ownership_token_callback); IEOS_BIND_SIGNAL(ecom_interface_redeem_entitlements_callback); IEOS_BIND_SIGNAL(friends_interface_accept_invite_callback); IEOS_BIND_SIGNAL(friends_interface_blocked_users_update_callback); IEOS_BIND_SIGNAL(friends_interface_friends_update_callback); IEOS_BIND_SIGNAL(friends_interface_query_friends_callback); IEOS_BIND_SIGNAL(friends_interface_reject_invite_callback); IEOS_BIND_SIGNAL(friends_interface_send_invite_callback); IEOS_BIND_SIGNAL(kws_interface_create_user_callback); IEOS_BIND_SIGNAL(kws_interface_permissions_update_received_callback); IEOS_BIND_SIGNAL(kws_interface_query_age_gate_callback); IEOS_BIND_SIGNAL(kws_interface_query_permissions_callback); IEOS_BIND_SIGNAL(kws_interface_request_permissions_callback); IEOS_BIND_SIGNAL(kws_interface_update_parent_email_callback); IEOS_BIND_SIGNAL(leaderboards_interface_query_leaderboard_definitions_callback); IEOS_BIND_SIGNAL(leaderboards_interface_query_leaderboard_ranks_callback); IEOS_BIND_SIGNAL(leaderboards_interface_query_leaderboard_user_scores_callback); IEOS_BIND_SIGNAL(lobby_interface_create_lobby_callback); IEOS_BIND_SIGNAL(lobby_interface_destroy_lobby_callback); IEOS_BIND_SIGNAL(lobby_interface_join_rtc_room_callback); IEOS_BIND_SIGNAL(lobby_interface_leave_rtc_room_callback); IEOS_BIND_SIGNAL(lobby_interface_hard_mute_member_callback); IEOS_BIND_SIGNAL(lobby_interface_join_lobby_accepted_callback); IEOS_BIND_SIGNAL(lobby_interface_join_lobby_by_id_callback); IEOS_BIND_SIGNAL(lobby_interface_join_lobby_callback); IEOS_BIND_SIGNAL(lobby_interface_kick_member_callback); IEOS_BIND_SIGNAL(lobby_interface_leave_lobby_callback); IEOS_BIND_SIGNAL(lobby_interface_leave_lobby_requested_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_invite_accepted_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_invite_received_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_invite_rejected_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_member_status_received_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_member_update_received_callback); IEOS_BIND_SIGNAL(lobby_interface_lobby_update_received_callback); IEOS_BIND_SIGNAL(lobby_interface_promote_member_callback); IEOS_BIND_SIGNAL(lobby_interface_query_invites_callback); IEOS_BIND_SIGNAL(lobby_interface_reject_invite_callback); IEOS_BIND_SIGNAL(lobby_interface_rtc_room_connection_changed_callback); IEOS_BIND_SIGNAL(lobby_interface_send_invite_callback); IEOS_BIND_SIGNAL(lobby_interface_send_lobby_native_invite_requested_callback); IEOS_BIND_SIGNAL(lobby_interface_update_lobby_callback); IEOS_BIND_SIGNAL(lobby_search_find_callback); IEOS_BIND_SIGNAL(mods_interface_enumerate_mods_callback); IEOS_BIND_SIGNAL(mods_interface_install_mod_callback); IEOS_BIND_SIGNAL(mods_interface_uninstall_mod_callback); IEOS_BIND_SIGNAL(mods_interface_update_mod_callback); IEOS_BIND_SIGNAL(p2p_interface_query_nat_type_callback); IEOS_BIND_SIGNAL(p2p_interface_incoming_packet_queue_full); IEOS_BIND_SIGNAL(playerdatastorage_interface_delete_cache_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_delete_file_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_duplicate_file_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_file_transfer_progress_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_query_file_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_query_file_list_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_read_file_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_read_file_data_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_write_file_callback); IEOS_BIND_SIGNAL(playerdatastorage_interface_write_file_data_callback); IEOS_BIND_SIGNAL(presence_interface_join_game_accepted_callback); IEOS_BIND_SIGNAL(presence_interface_presence_changed_callback); IEOS_BIND_SIGNAL(presence_interface_query_presence_callback); IEOS_BIND_SIGNAL(presence_interface_set_presence_callback); IEOS_BIND_SIGNAL(progression_snapshot_interface_delete_snapshot_callback); IEOS_BIND_SIGNAL(progression_snapshot_interface_submit_snapshot_callback); IEOS_BIND_SIGNAL(reports_interface_send_player_behavior_report_callback); IEOS_BIND_SIGNAL(rtc_audio_audio_before_render); IEOS_BIND_SIGNAL(rtc_audio_audio_before_send); IEOS_BIND_SIGNAL(rtc_audio_audio_devices_changed); IEOS_BIND_SIGNAL(rtc_audio_audio_input_state); IEOS_BIND_SIGNAL(rtc_audio_audio_output_state); IEOS_BIND_SIGNAL(rtc_audio_interface_query_input_devices_information_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_query_output_devices_information_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_register_platform_user_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_set_input_device_settings_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_set_output_device_settings_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_unregister_platform_user_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_update_participant_volume_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_update_receiving_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_update_receiving_volume_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_update_sending_callback); IEOS_BIND_SIGNAL(rtc_audio_interface_update_sending_volume_callback); IEOS_BIND_SIGNAL(rtc_audio_participant_updated); IEOS_BIND_SIGNAL(rtc_data_data_received); IEOS_BIND_SIGNAL(rtc_data_participant_updated); IEOS_BIND_SIGNAL(rtc_data_interface_update_receiving_callback); IEOS_BIND_SIGNAL(rtc_data_interface_update_sending_callback); IEOS_BIND_SIGNAL(rtc_interface_block_participant_callback); IEOS_BIND_SIGNAL(rtc_interface_disconnected); IEOS_BIND_SIGNAL(rtc_interface_join_room_callback); IEOS_BIND_SIGNAL(rtc_interface_leave_room_callback); IEOS_BIND_SIGNAL(rtc_interface_participant_status_changed); IEOS_BIND_SIGNAL(rtc_interface_room_statistics_updated); IEOS_BIND_SIGNAL(sanctions_interface_query_active_player_sanctions_callback); IEOS_BIND_SIGNAL(sanctions_interface_create_player_sanction_appeal_callback); IEOS_BIND_SIGNAL(session_search_find_callback); IEOS_BIND_SIGNAL(sessions_interface_destroy_session_callback); IEOS_BIND_SIGNAL(sessions_interface_end_session_callback); IEOS_BIND_SIGNAL(sessions_interface_join_session_accepted_callback); IEOS_BIND_SIGNAL(sessions_interface_join_session_callback); IEOS_BIND_SIGNAL(sessions_interface_leave_session_requested_callback); IEOS_BIND_SIGNAL(sessions_interface_query_invites_callback); IEOS_BIND_SIGNAL(sessions_interface_register_players_callback); IEOS_BIND_SIGNAL(sessions_interface_reject_invite_callback); IEOS_BIND_SIGNAL(sessions_interface_send_invite_callback); IEOS_BIND_SIGNAL(sessions_interface_send_session_native_invite_requested_callback); IEOS_BIND_SIGNAL(sessions_interface_session_invite_accepted_callback); IEOS_BIND_SIGNAL(sessions_interface_session_invite_received_callback); IEOS_BIND_SIGNAL(sessions_interface_session_invite_rejected_callback); IEOS_BIND_SIGNAL(sessions_interface_start_session_callback); IEOS_BIND_SIGNAL(sessions_interface_unregister_players_callback); IEOS_BIND_SIGNAL(sessions_interface_update_session_callback); IEOS_BIND_SIGNAL(stats_interface_ingest_stat_callback); IEOS_BIND_SIGNAL(stats_interface_query_stats_callback); IEOS_BIND_SIGNAL(titlestorage_interface_delete_cache_callback); IEOS_BIND_SIGNAL(titlestorage_interface_file_transfer_progress_callback); IEOS_BIND_SIGNAL(titlestorage_interface_query_file_callback); IEOS_BIND_SIGNAL(titlestorage_interface_query_file_list_callback); IEOS_BIND_SIGNAL(titlestorage_interface_read_file_callback); IEOS_BIND_SIGNAL(titlestorage_interface_read_file_data_callback); IEOS_BIND_SIGNAL(ui_interface_display_settings_updated_callback); IEOS_BIND_SIGNAL(ui_interface_hide_friends_callback); IEOS_BIND_SIGNAL(ui_interface_memory_monitor_callback); IEOS_BIND_SIGNAL(ui_interface_show_block_player_callback); IEOS_BIND_SIGNAL(ui_interface_show_friends_callback); IEOS_BIND_SIGNAL(ui_interface_show_native_profile_callback); IEOS_BIND_SIGNAL(ui_interface_show_report_player_callback); IEOS_BIND_SIGNAL(user_info_interface_query_user_info_by_display_name_callback); IEOS_BIND_SIGNAL(user_info_interface_query_user_info_by_external_account_callback); IEOS_BIND_SIGNAL(user_info_interface_query_user_info_callback); IEOS_BIND_SIGNAL(anticheatserver_interface_message_to_client_callback); IEOS_BIND_SIGNAL(anticheatserver_interface_client_action_required_callback); IEOS_BIND_SIGNAL(anticheatserver_interface_client_auth_status_changed_callback); IEOS_BIND_SIGNAL(anticheat_client_interface_message_to_server_callback); IEOS_BIND_SIGNAL(anticheat_client_interface_message_to_peer_callback); IEOS_BIND_SIGNAL(anticheat_client_interface_client_integrity_violated_callback); } bool IEOS::is_operation_complete(int p_result_code) { return EOSG_GET_BOOL(EOS_EResult_IsOperationComplete(static_cast(p_result_code))); } IEOS *IEOS::singleton = nullptr; IEOS *IEOS::get_singleton() { return singleton; } IEOS::IEOS() { ERR_FAIL_COND(singleton != nullptr); singleton = this; } IEOS::~IEOS() { ERR_FAIL_COND(singleton != this); if (s_platformInterface != nullptr) { platform_interface_release(); } for (auto const &[key, val] : anticheat_client_map) { memfree(val); } anticheat_client_map.clear(); singleton = nullptr; } void IEOS::tick() { if (singleton != nullptr && IEOS::get_singleton()->s_platformInterface != nullptr && IEOS::get_singleton()->isEOSValid) { EOS_Platform_Tick(IEOS::get_singleton()->s_platformInterface); } } ================================================ FILE: src/ieos.h ================================================ #pragma once #include "eos_achievements.h" #include "eos_anticheatclient.h" #include "eos_anticheatserver.h" #include "eos_auth.h" #include "eos_custominvites.h" #include "eos_ecom.h" #include "eos_friends.h" #include "eos_kws.h" #include "eos_leaderboards.h" #include "eos_lobby.h" #include "eos_logging.h" #include "eos_metrics.h" #include "eos_mods.h" #include "eos_p2p.h" #include "eos_playerdatastorage.h" #include "eos_presence.h" #include "eos_progressionsnapshot.h" #include "eos_reports.h" #include "eos_rtc.h" #include "eos_rtc_audio.h" #include "eos_rtc_data.h" #include "eos_sanctions.h" #include "eos_sdk.h" #include "eos_sessions.h" #include "eos_stats.h" #include "eos_titlestorage.h" #include "eos_ui.h" #include "eos_userinfo.h" #include "eos_version.h" #include "eosg_file_transfer_request.h" #include "godot_cpp/classes/object.hpp" #include "godot_cpp/classes/os.hpp" #include "godot_cpp/classes/project_settings.hpp" #include "godot_cpp/classes/ref_counted.hpp" #include "godot_cpp/core/binder_common.hpp" #include "godot_cpp/core/class_db.hpp" #include "godot_cpp/variant/utility_functions.hpp" #include "unordered_map" #include "utils.h" #ifdef _WIN32 #include "Windows/eos_Windows.h" #endif namespace godot { class IEOS : public Object { GDCLASS(IEOS, Object) static IEOS *singleton; protected: static void _bind_methods(); EOS_HAchievements s_achievementsInterface = nullptr; EOS_HAuth s_authInterface = nullptr; EOS_HConnect s_connectInterface = nullptr; EOS_HCustomInvites s_customInvitesInterface = nullptr; EOS_HEcom s_ecomInterface = nullptr; EOS_HFriends s_friendsInterface = nullptr; EOS_HKWS s_kwsInterface = nullptr; EOS_HLeaderboards s_leaderboardsInterface = nullptr; EOS_HLobby s_lobbyInterface = nullptr; EOS_HMetrics s_metricsInterface = nullptr; EOS_HMods s_modsInterface = nullptr; EOS_HP2P s_p2pInterface = nullptr; EOS_HPlatform s_platformInterface = nullptr; EOS_HPlayerDataStorage s_playerDataStorageInterface = nullptr; EOS_HPresence s_presenceInterface = nullptr; EOS_HProgressionSnapshot s_progressionSnapshotInterface = nullptr; EOS_HReports s_reportsInterface = nullptr; EOS_HRTC s_rtcInterface = nullptr; EOS_HRTCAudio s_rtcAudioInterface = nullptr; EOS_HRTCData s_rtcDataInterface = nullptr; EOS_HSanctions s_sanctionsInterface = nullptr; EOS_HSessions s_sessionsInterface = nullptr; EOS_HStats s_statsInterface = nullptr; EOS_HTitleStorage s_titleStorageInterface = nullptr; EOS_HUI s_uiInterface = nullptr; EOS_HUserInfo s_userInfoInterface = nullptr; EOS_HAntiCheatServer s_antiCheatServerInterface = nullptr; EOS_HAntiCheatClient s_antiCheatClientInterface = nullptr; std::unordered_map anticheat_client_map; // {String: String} key: hash value: string public: bool isEOSValid = false; // is true if EOS Platform is initialized static IEOS *get_singleton(); IEOS(); ~IEOS(); static void tick(); bool is_operation_complete(int result_code); // ----- // Platform Interface // ----- bool platform_interface_create(Ref options); Dictionary platform_interface_get_active_country_code(const String &local_user_id); Dictionary platform_interface_get_active_locale_code(const String &local_user_id); Dictionary platform_interface_get_desktop_crossplay_status_info(); Dictionary platform_interface_get_override_country_code(); Dictionary platform_interface_get_override_locale_code(); int platform_interface_check_for_launcher_and_restart(); int platform_interface_get_application_status(); int platform_interface_get_network_status(); int platform_interface_initialize(Ref options); int platform_interface_set_application_status(int status); int platform_interface_set_network_status(int status); int platform_interface_set_override_country_code(const String &country_code); int platform_interface_set_override_locale_code(const String &locale_code); int platform_interface_shutdown(); void platform_interface_release(); // ----- // Logging Interface // ----- int logging_interface_set_log_level(int category, int level); // ----- // Version Interface // ----- Dictionary version_interface_get_constants(); String version_interface_get_version(); // ----- // Auth interface // ----- Dictionary auth_interface_copy_id_token(Ref options); Dictionary auth_interface_copy_user_auth_token(Ref options, const String &local_user_id); Dictionary auth_interface_get_selected_account_id(const String &local_user_id); int auth_interface_get_logged_in_accounts_count(); int auth_interface_get_login_status(const String &local_user_id); int auth_interface_get_merged_accounts_count(const String &local_user_id); String auth_interface_get_logged_in_account_by_index(int index); String auth_interface_get_merged_account_by_index(const String &local_user_id, int index); void auth_interface_delete_persistent_auth(Ref options); void auth_interface_link_account(Ref options); void auth_interface_login(Ref options); void auth_interface_logout(Ref options); void auth_interface_query_id_token(Ref options); void auth_interface_verify_id_token(Ref options); void auth_interface_verify_user_auth(Ref _options); // ----- // Connect Interface // ----- Dictionary connect_interface_copy_id_token(Ref options); Dictionary connect_interface_copy_product_user_external_account_by_account_id(Ref options); Dictionary connect_interface_copy_product_user_external_account_by_account_type(Ref options); Dictionary connect_interface_copy_product_user_external_account_by_index(Ref options); Dictionary connect_interface_copy_product_user_info(Ref options); Dictionary connect_interface_get_product_user_id_mapping(Ref options); int connect_interface_get_logged_in_users_count(); int connect_interface_get_login_status(const String &local_user_id); int connect_interface_get_product_user_external_account_count(Ref options); String connect_interface_get_external_account_mapping(Ref options); String connect_interface_get_logged_in_user_by_index(int index); void connect_interface_create_device_id(Ref options); void connect_interface_create_user(Ref options); void connect_interface_delete_device_id(Ref options); void connect_interface_link_account(Ref options); void connect_interface_login(Ref options); void connect_interface_logout(Ref options); void connect_interface_query_product_user_id_mappings(Ref options); void connect_interface_query_external_account_mappings(Ref options); void connect_interface_transfer_device_id_account(Ref options); void connect_interface_unlink_account(Ref options); void connect_interface_verify_id_token(Ref options); // ----- // Achievements Interface // ----- Dictionary achievements_interface_copy_achievement_definition_v2_by_achievement_id(Ref options); Dictionary achievements_interface_copy_achievement_definition_v2_by_index(Ref options); Dictionary achievements_interface_copy_player_achievement_by_achievement_id(Ref options); Dictionary achievements_interface_copy_player_achievement_by_index(Ref options); int achievements_interface_get_achievement_definition_count(Ref options); int achievements_interface_get_player_achievement_count(Ref options); void achievements_interface_query_definitions(Ref options); void achievements_interface_query_player_achievements(Ref options); void achievements_interface_unlock_achievements(Ref options); // ----- // Custom Invites Interface // ----- int custom_invites_interface_finalize_invite(Ref options); int custom_invites_interface_set_custom_invite(Ref options); void custom_invites_interface_accept_request_to_join(Ref options); void custom_invites_interface_reject_request_to_join(Ref options); void custom_invites_interface_send_custom_invite(Ref options); void custom_invites_interface_send_request_to_join(Ref options); // ----- // Stats Interface // ----- Dictionary stats_interface_copy_stat_by_index(Ref options); Dictionary stats_interface_copy_stat_by_name(Ref options); int stats_interface_get_stats_count(Ref options); void stats_interface_ingest_stat(Ref options); void stats_interface_query_stats(Ref options); // ----- // Leaderboards Interface // ----- Dictionary leaderboards_interface_copy_leaderboard_definition_by_index(Ref options); Dictionary leaderboards_interface_copy_leaderboard_definition_by_leaderboard_id(Ref options); Dictionary leaderboards_interface_copy_leaderboard_record_by_index(Ref options); Dictionary leaderboards_interface_copy_leaderboard_record_by_user_id(Ref options); Dictionary leaderboards_interface_copy_leaderboard_user_score_by_index(Ref options); Dictionary leaderboards_interface_copy_leaderboard_user_score_by_user_id(Ref options); int leaderboards_interface_get_leaderboard_definition_count(Ref options); int leaderboards_interface_get_leaderboard_record_count(Ref options); int leaderboards_interface_get_leaderboard_user_score_count(Ref options); void leaderboards_interface_query_leaderboard_definitions(Ref options); void leaderboards_interface_query_leaderboard_ranks(Ref options); void leaderboards_interface_query_leaderboard_user_scores(Ref options); // ----- // Friends Interface // ----- int friends_interface_get_blocked_users_count(Ref options); int friends_interface_get_friends_count(Ref options); int friends_interface_get_status(Ref options); String friends_interface_get_blocked_user_at_index(Ref options); String friends_interface_get_friend_at_index(Ref options); void friends_interface_accept_invite(Ref options); void friends_interface_query_friends(Ref options); void friends_interface_reject_invite(Ref options); void friends_interface_send_invite(Ref options); // ----- // Userinfo Interface // ----- Dictionary user_info_interface_copy_best_display_name_with_platform(Ref options); Dictionary user_info_interface_copy_best_display_name(Ref options); Dictionary user_info_interface_copy_external_user_info_by_account_id(Ref options); Dictionary user_info_interface_copy_external_user_info_by_account_type(Ref options); Dictionary user_info_interface_copy_external_user_info_by_index(Ref options); Dictionary user_info_interface_copy_user_info(Ref options); int user_info_interface_get_external_user_info_count(Ref options); int user_info_interface_get_local_platform_type(Ref options); void user_info_interface_query_user_info_by_display_name(Ref options); void user_info_interface_query_user_info_by_external_account(Ref options); void user_info_interface_query_user_info(Ref options); // ----- // Ecom Interface // ----- Dictionary ecom_interface_copy_entitlement_by_id(Ref options); Dictionary ecom_interface_copy_entitlement_by_index(Ref options); Dictionary ecom_interface_copy_entitlement_by_name_and_index(Ref options); Dictionary ecom_interface_copy_item_by_id(Ref options); Dictionary ecom_interface_copy_item_image_info_by_index(Ref options); Dictionary ecom_interface_copy_item_release_by_index(Ref options); Dictionary ecom_interface_copy_last_redeemed_entitlement_by_index(Ref options); Dictionary ecom_interface_copy_offer_by_id(Ref options); Dictionary ecom_interface_copy_offer_by_index(Ref options); Dictionary ecom_interface_copy_offer_image_info_by_index(Ref options); Dictionary ecom_interface_copy_offer_item_by_index(Ref options); Dictionary ecom_interface_copy_transaction_by_id(Ref options); Dictionary ecom_interface_copy_transaction_by_index(Ref options); int ecom_interface_get_entitlements_by_name_count(Ref options); int ecom_interface_get_entitlements_count(Ref options); int ecom_interface_get_item_image_info_count(Ref options); int ecom_interface_get_item_release_count(Ref options); int ecom_interface_get_last_redeemed_entitlements_count(Ref options); int ecom_interface_get_offer_count(Ref options); int ecom_interface_get_offer_image_info_count(Ref options); int ecom_interface_get_offer_item_count(Ref options); int ecom_interface_get_transaction_count(Ref options); void ecom_interface_checkout(Ref options); void ecom_interface_query_entitlements(Ref options); void ecom_interface_query_offers(Ref options); void ecom_interface_query_ownership_token(Ref options); void ecom_interface_query_ownership(Ref options); void ecom_interface_redeem_entitlements(Ref options); void ecom_interface_query_entitlement_token(Ref options); void ecom_interface_query_ownership_by_sandbox_ids(Ref options); // ----- // UI Interface // ----- bool ui_interface_get_friends_exclusive_input(Ref options); bool ui_interface_get_friends_visible(Ref options); bool ui_interface_is_social_overlay_paused(Ref options); bool ui_interface_is_valid_button_combination(int button_combination); bool ui_interface_is_valid_key_combination(int key_combination); int ui_interface_acknowledge_event_id(Ref options); int ui_interface_get_notification_location_preference(); int ui_interface_get_toggle_friends_button(Ref options); int ui_interface_get_toggle_friends_key(Ref options); int ui_interface_pause_social_overlay(Ref options); int ui_interface_pre_present(Ref options); int ui_interface_report_input_state(Ref options); int ui_interface_set_display_preference(Ref options); int ui_interface_set_toggle_friends_button(Ref options); int ui_interface_set_toggle_friends_key(Ref options); void ui_interface_hide_friends(Ref options); void ui_interface_show_block_player(Ref options); void ui_interface_show_friends(Ref options); void ui_interface_show_native_profile(Ref options); void ui_interface_show_report_player(Ref options); // ----- // KWS Interface // ----- Dictionary kws_interface_copy_permission_by_index(Ref options); Dictionary kws_interface_get_permission_by_key(Ref options); int kws_interface_get_permissions_count(Ref options); void kws_interface_create_user(Ref options); void kws_interface_query_age_gate(Ref options); void kws_interface_query_permissions(Ref options); void kws_interface_request_permissions(Ref options); void kws_interface_update_parent_email(Ref options); // ----- // Metrics Interface // ----- int metrics_interface_begin_player_session(Ref options); int metrics_interface_end_player_session(Ref options); // ----- // Mods Interface // ----- Dictionary mods_interface_copy_mod_info(Ref options); void mods_interface_enumerate_mods(Ref options); void mods_interface_install_mod(Ref options); void mods_interface_uninstall_mod(Ref options); void mods_interface_update_mod(Ref options); // ----- // Reports Interface // ----- void reports_interface_send_player_behavior_report(Ref options); // ----- // Progression Snapshot Interface // ----- Dictionary progression_snapshot_interface_begin_snapshot(Ref options); int progression_snapshot_interface_add_progression(Ref options); int progression_snapshot_interface_end_snapshot(Ref options); void progression_snapshot_interface_delete_snapshot(Ref options); void progression_snapshot_interface_submit_snapshot(Ref options); // ----- // Presence Interface // ----- bool presence_interface_has_presence(Ref options); Dictionary presence_interface_copy_presence(Ref options); Dictionary presence_interface_create_presence_modification(Ref options); Dictionary presence_interface_get_join_info(Ref options); void presence_interface_query_presence(Ref options); void presence_interface_set_presence(Ref options); // ----- // Lobby Interface // ----- Dictionary lobby_interface_copy_lobby_details_by_invite_id(Ref options); Dictionary lobby_interface_copy_lobby_details_by_ui_event_id(Ref options); Dictionary lobby_interface_copy_lobby_details(Ref options); Dictionary lobby_interface_create_lobby_search(Ref options); Dictionary lobby_interface_get_connect_string(Ref options); Dictionary lobby_interface_get_invite_id_by_index(Ref options); Dictionary lobby_interface_get_rtc_room_name(Ref options); Dictionary lobby_interface_is_rtc_room_connected(Ref options); Dictionary lobby_interface_parse_connect_string(Ref options); Dictionary lobby_interface_update_lobby_modification(Ref options); int lobby_interface_get_invite_count(Ref options); void lobby_interface_create_lobby(Ref options); void lobby_interface_destroy_lobby(Ref options); void lobby_interface_hard_mute_member(Ref options); void lobby_interface_join_lobby_by_id(Ref options); void lobby_interface_join_lobby(Ref options); void lobby_interface_join_rtc_room(Ref options); void lobby_interface_kick_member(Ref options); void lobby_interface_leave_lobby(Ref options); void lobby_interface_leave_rtc_room(Ref options); void lobby_interface_promote_member(Ref options); void lobby_interface_query_invites(Ref options); void lobby_interface_reject_invite(Ref options); void lobby_interface_send_invite(Ref options); void lobby_interface_update_lobby(Ref options); // ----- // P2P Interface // ----- // Available in Godot functions Dictionary p2p_interface_get_packet_queue_info(); Dictionary p2p_interface_get_port_range(); Dictionary p2p_interface_get_nat_type(); Dictionary p2p_interface_get_relay_control(); void p2p_interface_query_nat_type(); int p2p_interface_set_packet_queue_size(Ref options); int p2p_interface_set_port_range(Ref options); int p2p_interface_set_relay_control(int control); // Not available in Godot. Called by EOSGMultiplayerPeer EOS_EResult _p2p_accept_connection(const EOS_P2P_AcceptConnectionOptions *options); EOS_EResult _p2p_clear_packet_queue(const EOS_P2P_ClearPacketQueueOptions *options); EOS_EResult _p2p_close_all_connections(const EOS_P2P_CloseConnectionsOptions *options); EOS_EResult _p2p_close_connection(const EOS_P2P_CloseConnectionOptions *options); EOS_EResult _p2p_get_next_packet_size(const EOS_P2P_GetNextReceivedPacketSizeOptions *options, uint32_t *out_size); EOS_EResult _p2p_receive_packet(const EOS_P2P_ReceivePacketOptions *options, void *out_packet_data, uint32_t *out_packet_size, uint8_t *out_channel, EOS_ProductUserId *remote_user, EOS_P2P_SocketId *out_socket); EOS_EResult _p2p_send_packet(const EOS_P2P_SendPacketOptions *options); EOS_NotificationId _p2p_add_notify_peer_connection_closed(const EOS_P2P_AddNotifyPeerConnectionClosedOptions *options, EOS_P2P_OnRemoteConnectionClosedCallback callback); EOS_NotificationId _p2p_add_notify_peer_connection_established(const EOS_P2P_AddNotifyPeerConnectionEstablishedOptions *options, EOS_P2P_OnPeerConnectionEstablishedCallback callback); EOS_NotificationId _p2p_add_notify_peer_connection_interrupted(const EOS_P2P_AddNotifyPeerConnectionInterruptedOptions *options, EOS_P2P_OnPeerConnectionInterruptedCallback callback); EOS_NotificationId _p2p_add_notify_peer_connection_request(const EOS_P2P_AddNotifyPeerConnectionRequestOptions *options, EOS_P2P_OnIncomingConnectionRequestCallback callback); void _p2p_remove_notify_peer_connection_closed(EOS_NotificationId callback_id); void _p2p_remove_notify_peer_connection_established(EOS_NotificationId callback_id); void _p2p_remove_notify_peer_connection_interrupted(EOS_NotificationId callback_id); void _p2p_remove_notify_peer_connection_request(EOS_NotificationId callback_id); // ----- // PlayerDataStorage Interface // ----- Dictionary playerdatastorage_interface_copy_file_metadata_at_index(Ref options); Dictionary playerdatastorage_interface_copy_file_metadata_by_filename(Ref options); Dictionary playerdatastorage_interface_get_file_metadata_count(Ref options); int playerdatastorage_interface_delete_cache(Ref options); Variant playerdatastorage_interface_read_file(Ref options); Variant playerdatastorage_interface_write_file(Ref options); void playerdatastorage_interface_delete_file(Ref options); void playerdatastorage_interface_duplicate_file(Ref options); void playerdatastorage_interface_query_file_list(Ref options); void playerdatastorage_interface_query_file(Ref options); // ----- // TitleStorage Interface // ----- Dictionary titlestorage_interface_copy_file_metadata_at_index(Ref options); Dictionary titlestorage_interface_copy_file_metadata_by_filename(Ref options); int titlestorage_interface_delete_cache(Ref options); int titlestorage_interface_get_file_metadata_count(Ref options); Variant titlestorage_interface_read_file(Ref options); void titlestorage_interface_query_file_list(Ref options); void titlestorage_interface_query_file(Ref options); // ----- // Sanctions Interface // ----- Dictionary sanctions_interface_copy_player_sanction_by_index(Ref options); int sanctions_interface_get_player_sanction_count(Ref options); void sanctions_interface_query_active_player_sanctions(Ref options); void sanctions_interface_create_player_sanction_appeal(Ref options); // ----- // Sessions Interface // ----- Dictionary sessions_interface_copy_active_session_details(Ref options); Dictionary sessions_interface_copy_session_details_by_invite_id(Ref options); Dictionary sessions_interface_copy_session_details_by_ui_event_id(Ref options); Dictionary sessions_interface_copy_session_details_for_presence(Ref options); Dictionary sessions_interface_create_session_modification(Ref options); Dictionary sessions_interface_create_session_search(Ref options); Dictionary sessions_interface_get_invite_id_by_index(Ref options); Dictionary sessions_interface_update_session_modification(Ref options); int sessions_interface_dump_session_state(Ref options); int sessions_interface_get_invite_count(Ref options); int sessions_interface_is_user_in_session(Ref options); void sessions_interface_destroy_session(Ref options); void sessions_interface_end_session(Ref options); void sessions_interface_join_session(Ref options); void sessions_interface_query_invites(Ref options); void sessions_interface_register_players(Ref options); void sessions_interface_reject_invite(Ref options); void sessions_interface_send_invite(Ref options); void sessions_interface_start_session(Ref options); void sessions_interface_unregister_players(Ref options); void sessions_interface_update_session(Ref options); // ----- // RTCAudio Interface // ----- Dictionary rtc_audio_interface_copy_input_device_information_by_index(Ref options); Dictionary rtc_audio_interface_copy_output_device_information_by_index(Ref options); int rtc_audio_interface_add_notify_audio_before_render(Ref options); int rtc_audio_interface_add_notify_audio_before_send(Ref options); int rtc_audio_interface_add_notify_audio_input_state(Ref options); int rtc_audio_interface_add_notify_audio_output_state(Ref options); int rtc_audio_interface_add_notify_participant_updated(Ref options); int rtc_audio_interface_get_input_devices_count(Ref options); int rtc_audio_interface_get_output_devices_count(Ref options); int rtc_audio_interface_send_audio(const Ref &options); void rtc_audio_interface_query_input_devices_information(Ref options); void rtc_audio_interface_query_output_devices_information(Ref options); void rtc_audio_interface_register_platform_user(Ref options); void rtc_audio_interface_remove_notify_audio_before_render(int notification_id); void rtc_audio_interface_remove_notify_audio_before_send(int notification_id); void rtc_audio_interface_remove_notify_audio_input_state(int notification_id); void rtc_audio_interface_remove_notify_audio_output_state(int notification_id); void rtc_audio_interface_remove_notify_participant_updated(int notification_id); void rtc_audio_interface_set_input_device_settings(Ref options); void rtc_audio_interface_set_output_device_settings(Ref options); void rtc_audio_interface_unregister_platform_user(Ref options); void rtc_audio_interface_update_participant_volume(Ref options); void rtc_audio_interface_update_receiving(Ref options); void rtc_audio_interface_update_receiving_volume(Ref options); void rtc_audio_interface_update_sending(Ref options); void rtc_audio_interface_update_sending_volume(Ref options); // ----- // RTCData Interface // ----- int rtc_data_interface_add_notify_data_received(Ref options); int rtc_data_interface_add_notify_participant_updated(Ref options); int rtc_data_interface_send_data(Ref options); void rtc_data_interface_remove_notify_data_received(int notification_id); void rtc_data_interface_remove_notify_participant_updated(int notification_id); void rtc_data_interface_update_receiving(Ref options); void rtc_data_interface_update_sending(Ref options); // ----- // RTC Interface // ----- // TODO: impl rtc_interface_get_data_interface int rtc_interface_add_notify_disconnected(Ref options); int rtc_interface_add_notify_participant_status_changed(Ref options); int rtc_interface_add_notify_room_statistics_updated(Ref options); int rtc_interface_set_room_setting(Ref options); int rtc_interface_set_setting(Ref options); void rtc_interface_block_participant(Ref options); void rtc_interface_join_room(Ref options); void rtc_interface_leave_room(Ref options); void rtc_interface_remove_notify_disconnected(int notification_id); void rtc_interface_remove_notify_participant_status_changed(int notification_id); void rtc_interface_remove_notify_room_statistics_updated(int notification_id); // ----- // AntiCheatServer Interface // ----- int anticheat_server_interface_begin_session(Ref options); int anticheat_server_interface_end_session(Ref options); int anticheat_server_interface_register_client(Ref options); int anticheat_server_interface_unregister_client(Ref options); int anticheat_server_interface_receive_message_from_client(Ref options); int anticheat_server_interface_set_client_details(Ref options); int anticheat_server_interface_set_game_session_id(Ref options); int anticheat_server_interface_set_client_network_state(Ref options); Dictionary anticheat_server_interface_get_protect_message_output_length(Ref options); Dictionary anticheat_server_interface_protect_message(Ref options); Dictionary anticheat_server_interface_unprotect_message(Ref options); int anticheat_server_interface_register_event(Ref options); int anticheat_server_interface_log_event(Ref options); int anticheat_server_interface_log_game_round_start(Ref options); int anticheat_server_interface_log_game_round_end(Ref options); int anticheat_server_interface_log_player_spawn(Ref options); int anticheat_server_interface_log_player_despawn(Ref options); int anticheat_server_interface_log_player_revive(Ref options); int anticheat_server_interface_log_player_tick(Ref options); int anticheat_server_interface_log_player_use_weapon(Ref options); int anticheat_server_interface_log_player_use_ability(Ref options); int anticheat_server_interface_log_player_take_damage(Ref options); // ----- // AntiCheatClient Interface // ----- int anticheat_client_interface_begin_session(Ref options); int anticheat_client_interface_end_session(Ref options); int anticheat_client_interface_add_external_integrity_catalog(Ref options); int anticheat_client_interface_receive_message_from_server(Ref options); Dictionary anticheat_client_interface_get_protect_message_output_length(Ref options); Dictionary anticheat_client_interface_protect_message(Ref options); Dictionary anticheat_client_interface_unprotect_message(Ref options); int anticheat_client_interface_register_peer(Ref options); int anticheat_client_interface_unregister_peer(Ref options); int anticheat_client_interface_receive_message_from_peer(Ref options); void *_anticheat_player_id_to_handle(CharString p_player_id) { std::string player_id = p_player_id.get_data(); if (anticheat_client_map.count(player_id) == 0) { anticheat_client_map[player_id] = memalloc(1); } return anticheat_client_map[player_id]; } String _anticheat_player_handle_to_id(void *p_player_handle) { for (auto const &[key, val] : anticheat_client_map) { if (val == p_player_handle) { return String(key.c_str()); } } return String(""); } }; } // namespace godot ================================================ FILE: src/kws_interface.cpp ================================================ #include "ieos.h" using namespace godot; Dictionary IEOS::kws_interface_copy_permission_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_kwsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_KWS_CopyPermissionByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_COPYPERMISSIONBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Index = static_cast(static_cast(p_options->get("index"))); EOS_KWS_PermissionStatus *outPermission = nullptr; EOS_EResult res = EOS_KWS_CopyPermissionByIndex(s_kwsInterface, &options, &outPermission); Dictionary ret; ret["result_code"] = static_cast(res); ret["permission"] = eosg_kws_permission_status_to_dict_and_release(outPermission); return ret; } void IEOS::kws_interface_create_user(Ref p_options) { ERR_FAIL_NULL(s_kwsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString parent_email = VARIANT_TO_CHARSTRING(p_options->get("parent_email")); CharString date_of_birth = VARIANT_TO_CHARSTRING(p_options->get("date_of_birth")); EOS_KWS_CreateUserOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_CREATEUSER_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.ParentEmail = parent_email.get_data(); options.DateOfBirth = date_of_birth.get_data(); p_options->reference(); EOS_KWS_CreateUser(s_kwsInterface, &options, (void *)*p_options, [](const EOS_KWS_CreateUserCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["kws_user_id"] = EOSG_GET_STRING(data->KWSUserId); ret["is_minor"] = EOSG_GET_BOOL(data->bIsMinor); IEOS::get_singleton()->emit_signal("kws_interface_create_user_callback", ret); }); } Dictionary IEOS::kws_interface_get_permission_by_key(Ref p_options) { ERR_FAIL_NULL_V(s_kwsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString key = VARIANT_TO_CHARSTRING(p_options->get("key")); EOS_KWS_GetPermissionByKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_GETPERMISSIONBYKEY_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Key = key.get_data(); EOS_EKWSPermissionStatus *outPermission; EOS_EResult res = EOS_KWS_GetPermissionByKey(s_kwsInterface, &options, outPermission); Dictionary ret; ret["result_code"] = static_cast(res); ret["permission"] = static_cast(*outPermission); return ret; } int IEOS::kws_interface_get_permissions_count(Ref p_options) { ERR_FAIL_NULL_V(s_kwsInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_KWS_GetPermissionsCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_GETPERMISSIONSCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); return static_cast(EOS_KWS_GetPermissionsCount(s_kwsInterface, &options)); } void IEOS::kws_interface_query_age_gate(Ref p_options) { ERR_FAIL_NULL(s_kwsInterface); EOS_KWS_QueryAgeGateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_QUERYAGEGATE_API_LATEST; p_options->reference(); EOS_KWS_QueryAgeGate(s_kwsInterface, &options, (void *)*p_options, [](const EOS_KWS_QueryAgeGateCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["country_code"] = EOSG_GET_STRING(data->CountryCode); ret["age_of_consent"] = data->AgeOfConsent; IEOS::get_singleton()->emit_signal("kws_interface_query_age_gate_callback", ret); }); } void IEOS::kws_interface_query_permissions(Ref p_options) { ERR_FAIL_NULL(s_kwsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_KWS_QueryPermissionsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_QUERYPERMISSIONS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_KWS_QueryPermissions(s_kwsInterface, &options, (void *)*p_options, [](const EOS_KWS_QueryPermissionsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["kws_user_id"] = EOSG_GET_STRING(data->KWSUserId); ret["is_minor"] = EOSG_GET_BOOL(data->bIsMinor); ret["parent_email"] = EOSG_GET_STRING(data->ParentEmail); IEOS::get_singleton()->emit_signal("kws_interface_query_permissions_callback", ret); }); } void IEOS::kws_interface_request_permissions(Ref p_options) { ERR_FAIL_NULL(s_kwsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); Array p_permission_keys = p_options->get("permission_keys"); int p_permission_keys_size = p_permission_keys.size(); const char **permissionsKeys = (const char **)memalloc(sizeof(char *) * p_permission_keys_size); PERSISTENT_CHAR_ARRAY_CREATE(permissions_keys, permissions_keys_charstrings, p_permission_keys_size) for (int i = 0; i < p_permission_keys_size; i++) { PERSISTENT_CHAR_ARRAY_SET(permissions_keys, permissions_keys_charstrings, i, p_permission_keys[i]); permissionsKeys[i] = permissions_keys[i]; } EOS_KWS_RequestPermissionsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_REQUESTPERMISSIONS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.PermissionKeys = permissionsKeys; options.PermissionKeyCount = p_permission_keys_size; p_options->reference(); EOS_KWS_RequestPermissions(s_kwsInterface, &options, (void *)*p_options, [](const EOS_KWS_RequestPermissionsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("kws_interface_request_permissions_callback", ret); }); } void IEOS::kws_interface_update_parent_email(Ref p_options) { ERR_FAIL_NULL(s_kwsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString parent_email = VARIANT_TO_CHARSTRING(p_options->get("parent_email")); EOS_KWS_UpdateParentEmailOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_KWS_UPDATEPARENTEMAIL_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.ParentEmail = parent_email.get_data(); p_options->reference(); EOS_KWS_UpdateParentEmail(s_kwsInterface, &options, (void *)*p_options, [](const EOS_KWS_UpdateParentEmailCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("kws_interface_update_parent_email_callback", ret); }); } ================================================ FILE: src/leaderboards_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::leaderboards_interface_copy_leaderboard_definition_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); EOS_Leaderboards_CopyLeaderboardDefinitionByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDDEFINITIONBYINDEX_API_LATEST; options.LeaderboardIndex = static_cast(static_cast(p_options->get("leaderboard_index"))); EOS_Leaderboards_Definition *outLeaderboardDefinition = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardDefinitionByIndex(s_leaderboardsInterface, &options, &outLeaderboardDefinition); Dictionary ret; ret["result_code"] = static_cast(res); ret["definition"] = eosg_leaderboards_definition_to_dict_and_release(outLeaderboardDefinition); return ret; } Dictionary IEOS::leaderboards_interface_copy_leaderboard_definition_by_leaderboard_id(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); CharString leaderboard_id = VARIANT_TO_CHARSTRING(p_options->get("leaderboard_id")); EOS_Leaderboards_CopyLeaderboardDefinitionByLeaderboardIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDDEFINITIONBYLEADERBOARDID_API_LATEST; options.LeaderboardId = leaderboard_id.get_data(); EOS_Leaderboards_Definition *outLeaderboardDefinition = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardDefinitionByLeaderboardId(s_leaderboardsInterface, &options, &outLeaderboardDefinition); Dictionary ret; ret["result_code"] = static_cast(res); ret["definition"] = eosg_leaderboards_definition_to_dict_and_release(outLeaderboardDefinition); return ret; } Dictionary IEOS::leaderboards_interface_copy_leaderboard_record_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); EOS_Leaderboards_CopyLeaderboardRecordByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDRECORDBYINDEX_API_LATEST; options.LeaderboardRecordIndex = static_cast(static_cast(p_options->get("leaderboard_record_index"))); EOS_Leaderboards_LeaderboardRecord *outLeaderboardRecord = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardRecordByIndex(s_leaderboardsInterface, &options, &outLeaderboardRecord); Dictionary ret; ret["result_code"] = static_cast(res); ret["record"] = eosg_leaderboards_leaderboard_record_to_dict_and_release(outLeaderboardRecord); return ret; } Dictionary IEOS::leaderboards_interface_copy_leaderboard_record_by_user_id(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); CharString user_id = VARIANT_TO_CHARSTRING(p_options->get("user_id")); EOS_Leaderboards_CopyLeaderboardRecordByUserIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDRECORDBYUSERID_API_LATEST; options.UserId = eosg_string_to_product_user_id(user_id.get_data()); EOS_Leaderboards_LeaderboardRecord *outLeaderboardRecord = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardRecordByUserId(s_leaderboardsInterface, &options, &outLeaderboardRecord); Dictionary ret; ret["result_code"] = static_cast(res); ret["record"] = eosg_leaderboards_leaderboard_record_to_dict_and_release(outLeaderboardRecord); return ret; } Dictionary IEOS::leaderboards_interface_copy_leaderboard_user_score_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); CharString stat_name = VARIANT_TO_CHARSTRING(p_options->get("stat_name")); EOS_Leaderboards_CopyLeaderboardUserScoreByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDUSERSCOREBYINDEX_API_LATEST; options.LeaderboardUserScoreIndex = static_cast(static_cast(p_options->get("leaderboard_user_score_index"))); options.StatName = stat_name.get_data(); EOS_Leaderboards_LeaderboardUserScore *outLeaderboardUserScore = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardUserScoreByIndex(s_leaderboardsInterface, &options, &outLeaderboardUserScore); Dictionary ret; ret["result_code"] = static_cast(res); ret["user_score"] = eosg_leaderboards_leaderboard_user_score_to_dict_and_release(outLeaderboardUserScore); return ret; } Dictionary IEOS::leaderboards_interface_copy_leaderboard_user_score_by_user_id(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, {}); CharString user_id = VARIANT_TO_CHARSTRING(p_options->get("user_id")); CharString stat_name = VARIANT_TO_CHARSTRING(p_options->get("stat_name")); EOS_Leaderboards_CopyLeaderboardUserScoreByUserIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_COPYLEADERBOARDUSERSCOREBYUSERID_API_LATEST; options.UserId = eosg_string_to_product_user_id(user_id.get_data()); options.StatName = stat_name.get_data(); EOS_Leaderboards_LeaderboardUserScore *outLeaderboardUserScore = nullptr; EOS_EResult res = EOS_Leaderboards_CopyLeaderboardUserScoreByUserId(s_leaderboardsInterface, &options, &outLeaderboardUserScore); Dictionary ret; ret["result_code"] = static_cast(res); ret["user_score"] = eosg_leaderboards_leaderboard_user_score_to_dict_and_release(outLeaderboardUserScore); return ret; } int IEOS::leaderboards_interface_get_leaderboard_definition_count(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, 0); EOS_Leaderboards_GetLeaderboardDefinitionCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_GETLEADERBOARDDEFINITIONCOUNT_API_LATEST; return static_cast(EOS_Leaderboards_GetLeaderboardDefinitionCount(s_leaderboardsInterface, &options)); } int IEOS::leaderboards_interface_get_leaderboard_record_count(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, 0); EOS_Leaderboards_GetLeaderboardRecordCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_GETLEADERBOARDRECORDCOUNT_API_LATEST; return static_cast(EOS_Leaderboards_GetLeaderboardRecordCount(s_leaderboardsInterface, &options)); } int IEOS::leaderboards_interface_get_leaderboard_user_score_count(Ref p_options) { ERR_FAIL_NULL_V(s_leaderboardsInterface, 0); CharString stat_name = VARIANT_TO_CHARSTRING(p_options->get("stat_name")); EOS_Leaderboards_GetLeaderboardUserScoreCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_GETLEADERBOARDUSERSCORECOUNT_API_LATEST; options.StatName = stat_name.get_data(); return static_cast(EOS_Leaderboards_GetLeaderboardUserScoreCount(s_leaderboardsInterface, &options)); } void IEOS::leaderboards_interface_query_leaderboard_definitions(Ref p_options) { ERR_FAIL_NULL(s_leaderboardsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Leaderboards_QueryLeaderboardDefinitionsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_QUERYLEADERBOARDDEFINITIONS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.StartTime = static_cast(p_options->get("start_time")); options.EndTime = static_cast(p_options->get("end_time")); p_options->reference(); EOS_Leaderboards_QueryLeaderboardDefinitions(s_leaderboardsInterface, &options, (void *)*p_options, [](const EOS_Leaderboards_OnQueryLeaderboardDefinitionsCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("leaderboards_interface_query_leaderboard_definitions_callback", ret); }); } void IEOS::leaderboards_interface_query_leaderboard_ranks(Ref p_options) { ERR_FAIL_NULL(s_leaderboardsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString leaderboard_id = VARIANT_TO_CHARSTRING(p_options->get("leaderboard_id")); EOS_Leaderboards_QueryLeaderboardRanksOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_QUERYLEADERBOARDRANKS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LeaderboardId = leaderboard_id.get_data(); p_options->reference(); EOS_Leaderboards_QueryLeaderboardRanks(s_leaderboardsInterface, &options, (void *)*p_options, [](const EOS_Leaderboards_OnQueryLeaderboardRanksCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["leaderboard_id"] = EOSG_GET_STRING(data->LeaderboardId); IEOS::get_singleton()->emit_signal("leaderboards_interface_query_leaderboard_ranks_callback", ret); }); } void IEOS::leaderboards_interface_query_leaderboard_user_scores(Ref p_options) { ERR_FAIL_NULL(s_leaderboardsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); Array p_user_ids = p_options->get("user_ids"); int p_user_ids_size = p_user_ids.size(); EOS_ProductUserId *user_ids = (EOS_ProductUserId *)memalloc(sizeof(EOS_ProductUserId) * p_user_ids_size); PERSISTENT_CHAR_ARRAY_CREATE(user_ids_cstr, user_ids_cstr_charstrings, p_user_ids_size) for (int i = 0; i < p_user_ids_size; i++) { PERSISTENT_CHAR_ARRAY_SET(user_ids_cstr, user_ids_cstr_charstrings, i, p_user_ids[i]); user_ids[i] = eosg_string_to_product_user_id(user_ids_cstr[i]); } Array p_stat_info = p_options->get("stat_info"); int p_stat_info_size = p_stat_info.size(); EOS_Leaderboards_UserScoresQueryStatInfo *statInfo = (EOS_Leaderboards_UserScoresQueryStatInfo *)memalloc(sizeof(EOS_Leaderboards_UserScoresQueryStatInfo) * p_stat_info_size); PERSISTENT_CHAR_ARRAY_CREATE(stat_name_cstr, stat_name_cstr_charstrings, p_stat_info_size) for (int i = 0; i < p_stat_info_size; i++) { PERSISTENT_CHAR_ARRAY_SET(stat_name_cstr, stat_name_cstr_charstrings, i, p_stat_info[i].get("stat_name")); int aggregation = static_cast(p_stat_info[i].get("aggregation")); statInfo[i].ApiVersion = EOS_LEADERBOARDS_USERSCORESQUERYSTATINFO_API_LATEST; statInfo[i].StatName = stat_name_cstr[i]; statInfo[i].Aggregation = static_cast(aggregation); } EOS_Leaderboards_QueryLeaderboardUserScoresOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LEADERBOARDS_QUERYLEADERBOARDUSERSCORES_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.UserIds = user_ids; options.UserIdsCount = p_user_ids_size; options.StatInfo = statInfo; options.StatInfoCount = p_stat_info_size; options.StartTime = static_cast(p_options->get("start_time")); options.EndTime = static_cast(p_options->get("end_time")); p_options->reference(); EOS_Leaderboards_QueryLeaderboardUserScores(s_leaderboardsInterface, &options, (void *)*p_options, [](const EOS_Leaderboards_OnQueryLeaderboardUserScoresCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("leaderboards_interface_query_leaderboard_user_scores_callback", ret); }); } ================================================ FILE: src/lobby_interface.cpp ================================================ #include "eosg_lobby_details.h" #include "godot_cpp/variant/utility_functions.hpp" #include "ieos.h" using namespace godot; void IEOS::lobby_interface_create_lobby(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString bucket_id = VARIANT_TO_CHARSTRING(p_options->get("bucket_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); int rtc_room_join_action_type = p_options->get("rtc_room_join_action_type"); EOS_Lobby_CreateLobbyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_CREATELOBBY_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.MaxLobbyMembers = static_cast(static_cast(p_options->get("max_lobby_members"))); options.PermissionLevel = static_cast(static_cast(p_options->get("permission_level"))); options.bPresenceEnabled = VARIANT_TO_EOS_BOOL(p_options->get("presence_enabled")); options.bAllowInvites = VARIANT_TO_EOS_BOOL(p_options->get("allow_invites")); options.BucketId = bucket_id.get_data(); options.bDisableHostMigration = VARIANT_TO_EOS_BOOL(p_options->get("disable_host_migration")); options.bEnableRTCRoom = VARIANT_TO_EOS_BOOL(p_options->get("enable_rtc_room")); options.bCrossplayOptOut = VARIANT_TO_EOS_BOOL(p_options->get("crossplay_opt_out")); options.RTCRoomJoinActionType = static_cast(rtc_room_join_action_type); Variant local_rtc_options = p_options->get("local_rtc_options"); EOS_Lobby_LocalRTCOptions localRTCOptions; memset(&localRTCOptions, 0, sizeof(localRTCOptions)); if (options.bEnableRTCRoom && local_rtc_options.get_type() != Variant::NIL) { localRTCOptions = eosg_variant_to_lobby_local_rtc_options(local_rtc_options); options.LocalRTCOptions = &localRTCOptions; } if (lobby_id.length() != 0) { options.LobbyId = lobby_id.get_data(); } options.bEnableJoinById = VARIANT_TO_EOS_BOOL(p_options->get("enable_join_by_id")); options.bRejoinAfterKickRequiresInvite = VARIANT_TO_EOS_BOOL(p_options->get("rejoin_after_kick_requires_invite")); Array allowed_platform_ids = p_options->get("allowed_platform_ids"); uint32_t* allowedPlatformIds = nullptr; if (allowed_platform_ids.size() > 0) { for (int i = 0; i < allowed_platform_ids.size(); i++) { allowedPlatformIds[i] = static_cast(allowed_platform_ids[i]); } options.AllowedPlatformIdsCount = allowed_platform_ids.size(); options.AllowedPlatformIds = allowedPlatformIds; } p_options->reference(); EOS_Lobby_CreateLobby(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_CreateLobbyCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_create_lobby_callback", ret); }); } void IEOS::lobby_interface_join_rtc_room(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_JoinRTCRoomOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_JOINRTCROOM_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); Variant local_rtc_options = p_options->get("local_rtc_options"); EOS_Lobby_LocalRTCOptions localRTCOptions; memset(&localRTCOptions, 0, sizeof(localRTCOptions)); if (local_rtc_options.get_type() != Variant::NIL) { localRTCOptions = eosg_variant_to_lobby_local_rtc_options(local_rtc_options); options.LocalRTCOptions = &localRTCOptions; } p_options->reference(); EOS_Lobby_JoinRTCRoom(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_JoinRTCRoomCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_join_rtc_room_callback", ret); }); } void IEOS::lobby_interface_leave_rtc_room(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_LeaveRTCRoomOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_LEAVERTCROOM_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_LeaveRTCRoom(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_LeaveRTCRoomCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_leave_rtc_room_callback", ret); }); } void IEOS::lobby_interface_destroy_lobby(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_DestroyLobbyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_DESTROYLOBBY_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_DestroyLobby(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_DestroyLobbyCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_destroy_lobby_callback", ret); }); } void IEOS::lobby_interface_join_lobby(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); Ref p_lobby_details = Object::cast_to(p_options->get("lobby_details")); ERR_FAIL_NULL_MSG(p_lobby_details, "Error joining lobby. JoinLobbyOptions.lobby_details is null."); ERR_FAIL_NULL_MSG(p_lobby_details->get_internal(), "Error joining lobby. EOSGLobbyDetails is null."); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int rtc_room_join_action_type = p_options->get("rtc_room_join_action_type"); EOS_Lobby_JoinLobbyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_JOINLOBBY_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.bPresenceEnabled = VARIANT_TO_EOS_BOOL(p_options->get("presence_enabled")); options.RTCRoomJoinActionType = static_cast(rtc_room_join_action_type); Variant local_rtc_options = p_options->get("local_rtc_options"); EOS_Lobby_LocalRTCOptions localRTCOptions; memset(&localRTCOptions, 0, sizeof(localRTCOptions)); if (local_rtc_options.get_type() != Variant::NIL) { localRTCOptions = eosg_variant_to_lobby_local_rtc_options(local_rtc_options); options.LocalRTCOptions = &localRTCOptions; } options.LobbyDetailsHandle = p_lobby_details->get_internal(); p_options->reference(); EOS_Lobby_JoinLobby(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_JoinLobbyCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_join_lobby_callback", ret); }); } void IEOS::lobby_interface_join_lobby_by_id(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); int rtc_room_join_action_type = p_options->get("rtc_room_join_action_type"); EOS_Lobby_JoinLobbyByIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_JOINLOBBYBYID_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); options.bPresenceEnabled = VARIANT_TO_EOS_BOOL(p_options->get("presence_enabled")); options.RTCRoomJoinActionType = static_cast(rtc_room_join_action_type); Variant local_rtc_options = p_options->get("local_rtc_options"); EOS_Lobby_LocalRTCOptions localRTCOptions; memset(&localRTCOptions, 0, sizeof(localRTCOptions)); if (local_rtc_options.get_type() != Variant::NIL) { localRTCOptions = eosg_variant_to_lobby_local_rtc_options(local_rtc_options); options.LocalRTCOptions = &localRTCOptions; } p_options->reference(); EOS_Lobby_JoinLobbyById(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_JoinLobbyByIdCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_join_lobby_by_id_callback", ret); }); } void IEOS::lobby_interface_leave_lobby(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_LeaveLobbyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_LEAVELOBBY_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_LeaveLobby(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_LeaveLobbyCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_leave_lobby_callback", ret); }); } Dictionary IEOS::lobby_interface_update_lobby_modification(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_UpdateLobbyModificationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_UPDATELOBBYMODIFICATION_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); EOS_HLobbyModification outLobbyModification = nullptr; EOS_EResult res = EOS_Lobby_UpdateLobbyModification(s_lobbyInterface, &options, &outLobbyModification); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_modification"] = eosg_lobby_lobby_modification_to_wrapper(outLobbyModification); return ret; } void IEOS::lobby_interface_update_lobby(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); Ref lobby_modification = Object::cast_to(p_options->get("lobby_modification")); ERR_FAIL_NULL_MSG(lobby_modification, "Error updating lobby. UpdateLobbyOptions.lobby_modification is null."); ERR_FAIL_NULL_MSG(lobby_modification->get_internal(), "Error updating lobby. EOSGLobbyModification is null."); EOS_Lobby_UpdateLobbyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_UPDATELOBBY_API_LATEST; options.LobbyModificationHandle = lobby_modification->get_internal(); p_options->reference(); lobby_modification->reference(); EOS_Lobby_UpdateLobby(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_UpdateLobbyCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); Ref got_lobby_modification = Object::cast_to(client_data->get("lobby_modification")); got_lobby_modification->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_update_lobby_callback", ret); }); } void IEOS::lobby_interface_promote_member(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_PromoteMemberOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_PROMOTEMEMBER_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_PromoteMember(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_PromoteMemberCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_promote_member_callback", ret); }); } void IEOS::lobby_interface_kick_member(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_KickMemberOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_KICKMEMBER_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_KickMember(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_KickMemberCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_kick_member_callback", ret); }); } void IEOS::lobby_interface_hard_mute_member(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_HardMuteMemberOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_HARDMUTEMEMBER_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.LobbyId = lobby_id.get_data(); options.bHardMute = VARIANT_TO_EOS_BOOL(p_options->get("hard_mute")); p_options->reference(); EOS_Lobby_HardMuteMember(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_HardMuteMemberCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("lobby_interface_hard_mute_member_callback", ret); }); } void IEOS::lobby_interface_send_invite(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_SendInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_SENDINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.LobbyId = lobby_id.get_data(); p_options->reference(); EOS_Lobby_SendInvite(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_SendInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_send_invite_callback", ret); }); } void IEOS::lobby_interface_reject_invite(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString invite_id = VARIANT_TO_CHARSTRING(p_options->get("invite_id")); EOS_Lobby_RejectInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_REJECTINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.InviteId = invite_id.get_data(); p_options->reference(); EOS_Lobby_RejectInvite(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_RejectInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["invite_id"] = EOSG_GET_STRING(data->InviteId); IEOS::get_singleton()->emit_signal("lobby_interface_reject_invite_callback", ret); }); } void IEOS::lobby_interface_query_invites(Ref p_options) { ERR_FAIL_NULL(s_lobbyInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Lobby_QueryInvitesOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_QUERYINVITES_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_Lobby_QueryInvites(s_lobbyInterface, &options, (void *)*p_options, [](const EOS_Lobby_QueryInvitesCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("lobby_interface_query_invites_callback", ret); }); } int IEOS::lobby_interface_get_invite_count(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Lobby_GetInviteCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_GETINVITECOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); return static_cast(EOS_Lobby_GetInviteCount(s_lobbyInterface, &options)); } Dictionary IEOS::lobby_interface_get_invite_id_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Lobby_GetInviteIdByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_GETINVITEIDBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Index = static_cast(static_cast(p_options->get("index"))); char *outBuffer = (char *)memalloc(EOS_LOBBY_INVITEID_MAX_LENGTH + 1); int outBufferSize = EOS_LOBBY_INVITEID_MAX_LENGTH + 1; EOS_EResult result = EOS_Lobby_GetInviteIdByIndex(s_lobbyInterface, &options, outBuffer, &outBufferSize); Dictionary ret; ret["result_code"] = static_cast(result); ret["invite_id"] = EOSG_GET_STRING(outBuffer); return ret; } Dictionary IEOS::lobby_interface_create_lobby_search(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); EOS_Lobby_CreateLobbySearchOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_CREATELOBBYSEARCH_API_LATEST; options.MaxResults = static_cast(static_cast(p_options->get("max_results"))); EOS_HLobbySearch outLobbySearch = nullptr; EOS_EResult res = EOS_Lobby_CreateLobbySearch(s_lobbyInterface, &options, &outLobbySearch); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_search"] = eosg_lobby_lobby_search_to_wrapper(outLobbySearch); return ret; } Dictionary IEOS::lobby_interface_copy_lobby_details_by_invite_id(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString invite_id = VARIANT_TO_CHARSTRING(p_options->get("invite_id")); EOS_Lobby_CopyLobbyDetailsHandleByInviteIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLEBYINVITEID_API_LATEST; options.InviteId = invite_id.get_data(); EOS_HLobbyDetails outLobbyDetails = nullptr; EOS_EResult res = EOS_Lobby_CopyLobbyDetailsHandleByInviteId(s_lobbyInterface, &options, &outLobbyDetails); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_details"] = eosg_lobby_lobby_details_to_wrapper(outLobbyDetails); return ret; } Dictionary IEOS::lobby_interface_copy_lobby_details_by_ui_event_id(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); EOS_Lobby_CopyLobbyDetailsHandleByUiEventIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLEBYUIEVENTID_API_LATEST; options.UiEventId = static_cast(static_cast(p_options->get("ui_event_id"))); EOS_HLobbyDetails outLobbyDetails = nullptr; EOS_EResult res = EOS_Lobby_CopyLobbyDetailsHandleByUiEventId(s_lobbyInterface, &options, &outLobbyDetails); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_details"] = eosg_lobby_lobby_details_to_wrapper(outLobbyDetails); return ret; } Dictionary IEOS::lobby_interface_copy_lobby_details(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_CopyLobbyDetailsHandleOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_COPYLOBBYDETAILSHANDLE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); EOS_HLobbyDetails outLobbyDetails = nullptr; EOS_EResult res = EOS_Lobby_CopyLobbyDetailsHandle(s_lobbyInterface, &options, &outLobbyDetails); Variant lobby_details = eosg_lobby_lobby_details_to_wrapper(outLobbyDetails); Dictionary ret; ret["result_code"] = static_cast(res); ret["lobby_details"] = lobby_details; return ret; } Dictionary IEOS::lobby_interface_get_rtc_room_name(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_GetRTCRoomNameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_GETRTCROOMNAME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); char *outBuffer = (char *)memalloc(256); // TODO: what is the actual max size for the outBuffer? uint32_t outBufferSize = 256; EOS_EResult res = EOS_Lobby_GetRTCRoomName(s_lobbyInterface, &options, outBuffer, &outBufferSize); Dictionary ret; ret["result_code"] = static_cast(res); ret["rtc_room_name"] = EOSG_GET_STRING(outBuffer); return ret; } Dictionary IEOS::lobby_interface_is_rtc_room_connected(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_IsRTCRoomConnectedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_ISRTCROOMCONNECTED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); EOS_Bool outIsConnected = EOS_FALSE; EOS_EResult res = EOS_Lobby_IsRTCRoomConnected(s_lobbyInterface, &options, &outIsConnected); Dictionary ret; ret["result_code"] = static_cast(res); ret["is_connected"] = EOSG_GET_BOOL(outIsConnected); return ret; } Dictionary IEOS::lobby_interface_get_connect_string(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString lobby_id = VARIANT_TO_CHARSTRING(p_options->get("lobby_id")); EOS_Lobby_GetConnectStringOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_GETCONNECTSTRING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.LobbyId = lobby_id.get_data(); char *OutBuffer = (char *)memalloc(EOS_LOBBY_GETCONNECTSTRING_BUFFER_SIZE + 1); uint32_t OutBufferLength = 0; EOS_EResult res = EOS_Lobby_GetConnectString(s_lobbyInterface, &options, OutBuffer, &OutBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); if (res == EOS_EResult::EOS_Success) { ret["connect_string"] = EOSG_GET_STRING(OutBuffer); } else { ret["connect_string"] = String(""); } return ret; } Dictionary IEOS::lobby_interface_parse_connect_string(Ref p_options) { ERR_FAIL_NULL_V(s_lobbyInterface, {}); CharString connect_string = VARIANT_TO_CHARSTRING(p_options->get("connect_string")); EOS_Lobby_ParseConnectStringOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_PARSECONNECTSTRING_API_LATEST; options.ConnectString = connect_string.get_data(); char *OutBuffer = (char *)memalloc(EOS_LOBBY_PARSECONNECTSTRING_BUFFER_SIZE + 1); uint32_t OutBufferLength = 0; EOS_EResult res = EOS_Lobby_ParseConnectString(s_lobbyInterface, &options, OutBuffer, &OutBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); if (res == EOS_EResult::EOS_Success) { ret["lobby_id"] = EOSG_GET_STRING(OutBuffer); } else { ret["lobby_id"] = String(""); } return ret; } ================================================ FILE: src/logging_interface.cpp ================================================ #include "ieos.h" using namespace godot; int IEOS::logging_interface_set_log_level(int category, int level) { EOS_EResult result = EOS_Logging_SetLogLevel(static_cast(category), static_cast(level)); return static_cast(result); } ================================================ FILE: src/metrics_interface.cpp ================================================ #include "ieos.h" using namespace std; int IEOS::metrics_interface_begin_player_session(Ref p_options) { ERR_FAIL_NULL_V(s_metricsInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString account_id = VARIANT_TO_CHARSTRING(p_options->get("account_id")); CharString display_name = VARIANT_TO_CHARSTRING(p_options->get("display_name")); CharString server_ip = VARIANT_TO_CHARSTRING(p_options->get("server_ip")); CharString game_session_id = VARIANT_TO_CHARSTRING(p_options->get("game_session_id")); EOS_Metrics_BeginPlayerSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_METRICS_BEGINPLAYERSESSION_API_LATEST; options.AccountIdType = static_cast(static_cast(p_options->get("account_id_type"))); options.DisplayName = display_name.get_data(); options.ControllerType = static_cast(static_cast(p_options->get("controller_type"))); if (options.AccountIdType == EOS_EMetricsAccountIdType::EOS_MAIT_Epic) { options.AccountId.Epic = eosg_string_to_epic_account_id(account_id.get_data()); } else if (options.AccountIdType == EOS_EMetricsAccountIdType::EOS_MAIT_External) { options.AccountId.External = account_id.get_data(); } if (server_ip.length() != 0) { options.ServerIp = server_ip.get_data(); } if (game_session_id.length() != 0) { options.GameSessionId = game_session_id.get_data(); } return static_cast(EOS_Metrics_BeginPlayerSession(s_metricsInterface, &options)); } int IEOS::metrics_interface_end_player_session(Ref p_options) { ERR_FAIL_NULL_V(s_metricsInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString account_id = VARIANT_TO_CHARSTRING(p_options->get("account_id")); EOS_Metrics_EndPlayerSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_METRICS_ENDPLAYERSESSION_API_LATEST; options.AccountIdType = static_cast(static_cast(p_options->get("account_id_type"))); if (options.AccountIdType == EOS_EMetricsAccountIdType::EOS_MAIT_Epic) { options.AccountId.Epic = eosg_string_to_epic_account_id(account_id.get_data()); } else if (options.AccountIdType == EOS_EMetricsAccountIdType::EOS_MAIT_External) { options.AccountId.External = account_id.get_data(); } return static_cast(EOS_Metrics_EndPlayerSession(s_metricsInterface, &options)); } ================================================ FILE: src/mods_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::mods_interface_copy_mod_info(Ref p_options) { ERR_FAIL_NULL_V(s_modsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Mods_CopyModInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_MODS_COPYMODINFO_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Type = static_cast(static_cast(p_options->get("type"))); EOS_Mods_ModInfo *outEnumeratedMods = nullptr; EOS_EResult res = EOS_Mods_CopyModInfo(s_modsInterface, &options, &outEnumeratedMods); Dictionary ret; ret["result_code"] = static_cast(res); ret["mods"] = eosg_mods_mod_info_to_dict_and_release(outEnumeratedMods); return ret; } void IEOS::mods_interface_enumerate_mods(Ref p_options) { ERR_FAIL_NULL(s_modsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Mods_EnumerateModsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_MODS_ENUMERATEMODS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Type = static_cast(static_cast(p_options->get("type"))); p_options->reference(); EOS_Mods_EnumerateMods(s_modsInterface, &options, (void *)*p_options, [](const EOS_Mods_EnumerateModsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["type"] = static_cast(data->Type); IEOS::get_singleton()->emit_signal("mods_interface_enumerate_mods_callback", ret); }); } void IEOS::mods_interface_install_mod(Ref p_options) { ERR_FAIL_NULL(s_modsInterface); Dictionary p_mod = p_options->get("mod"); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString mod_namespace_id = VARIANT_TO_CHARSTRING(p_mod["namespace_id"]); CharString mod_item_id = VARIANT_TO_CHARSTRING(p_mod["item_id"]); CharString mod_artifact_id = VARIANT_TO_CHARSTRING(p_mod["artifact_id"]); CharString mod_title = VARIANT_TO_CHARSTRING(p_mod["title"]); CharString mod_version = VARIANT_TO_CHARSTRING(p_mod["version"]); EOS_Mod_Identifier mod; memset(&mod, 0, sizeof(mod)); mod.ApiVersion = EOS_MOD_IDENTIFIER_API_LATEST; mod.NamespaceId = mod_namespace_id.get_data(); mod.ItemId = mod_item_id.get_data(); mod.ArtifactId = mod_artifact_id.get_data(); mod.Title = mod_title.get_data(); mod.Version = mod_version.get_data(); EOS_Mods_InstallModOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_MODS_INSTALLMOD_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Mod = &mod; options.bRemoveAfterExit = VARIANT_TO_EOS_BOOL(p_options->get("remove_after_exit")); p_options->reference(); EOS_Mods_InstallMod(s_modsInterface, &options, (void *)*p_options, [](const EOS_Mods_InstallModCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["mod"] = eosg_mods_mod_identifier_to_dict(data->Mod); IEOS::get_singleton()->emit_signal("mods_interface_install_mod_callback", ret); }); } void IEOS::mods_interface_uninstall_mod(Ref p_options) { ERR_FAIL_NULL(s_modsInterface); Dictionary p_mod = p_options->get("mod"); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString mod_namespace_id = VARIANT_TO_CHARSTRING(p_mod["namespace_id"]); CharString mod_item_id = VARIANT_TO_CHARSTRING(p_mod["item_id"]); CharString mod_artifact_id = VARIANT_TO_CHARSTRING(p_mod["artifact_id"]); CharString mod_title = VARIANT_TO_CHARSTRING(p_mod["title"]); CharString mod_version = VARIANT_TO_CHARSTRING(p_mod["version"]); EOS_Mod_Identifier mod; memset(&mod, 0, sizeof(mod)); mod.ApiVersion = EOS_MOD_IDENTIFIER_API_LATEST; mod.NamespaceId = mod_namespace_id.get_data(); mod.ItemId = mod_item_id.get_data(); mod.ArtifactId = mod_artifact_id.get_data(); mod.Title = mod_title.get_data(); mod.Version = mod_version.get_data(); EOS_Mods_UninstallModOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_MODS_UNINSTALLMOD_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Mod = &mod; p_options->reference(); EOS_Mods_UninstallMod(s_modsInterface, &options, (void *)*p_options, [](const EOS_Mods_UninstallModCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["mod"] = eosg_mods_mod_identifier_to_dict(data->Mod); IEOS::get_singleton()->emit_signal("mods_interface_uninstall_mod_callback", ret); }); } void IEOS::mods_interface_update_mod(Ref p_options) { ERR_FAIL_NULL(s_modsInterface); Dictionary p_mod = p_options->get("mod"); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString mod_namespace_id = VARIANT_TO_CHARSTRING(p_mod["namespace_id"]); CharString mod_item_id = VARIANT_TO_CHARSTRING(p_mod["item_id"]); CharString mod_artifact_id = VARIANT_TO_CHARSTRING(p_mod["artifact_id"]); CharString mod_title = VARIANT_TO_CHARSTRING(p_mod["title"]); CharString mod_version = VARIANT_TO_CHARSTRING(p_mod["version"]); EOS_Mod_Identifier mod; memset(&mod, 0, sizeof(mod)); mod.ApiVersion = EOS_MOD_IDENTIFIER_API_LATEST; mod.NamespaceId = mod_namespace_id.get_data(); mod.ItemId = mod_item_id.get_data(); mod.ArtifactId = mod_artifact_id.get_data(); mod.Title = mod_title.get_data(); mod.Version = mod_version.get_data(); EOS_Mods_UpdateModOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_MODS_UPDATEMOD_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.Mod = &mod; p_options->reference(); EOS_Mods_UpdateMod(s_modsInterface, &options, (void *)*p_options, [](const EOS_Mods_UpdateModCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["mod"] = eosg_mods_mod_identifier_to_dict(data->Mod); IEOS::get_singleton()->emit_signal("mods_interface_update_mod_callback", ret); }); } ================================================ FILE: src/p2p_interface.cpp ================================================ #include "ieos.h" void IEOS::p2p_interface_query_nat_type() { ERR_FAIL_NULL(s_p2pInterface); EOS_P2P_QueryNATTypeOptions options = { 0 }; options.ApiVersion = EOS_P2P_QUERYNATTYPE_API_LATEST; EOS_P2P_QueryNATType(s_p2pInterface, &options, nullptr, [](const EOS_P2P_OnQueryNATTypeCompleteInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); ret["nat_type"] = static_cast(data->NATType); get_singleton()->emit_signal("p2p_interface_query_nat_type_callback", ret); }); } Dictionary IEOS::p2p_interface_get_nat_type() { ERR_FAIL_NULL_V(s_p2pInterface, Dictionary()); EOS_P2P_GetNATTypeOptions options = { 0 }; options.ApiVersion = EOS_P2P_GETNATTYPE_API_LATEST; EOS_ENATType nat_type = EOS_ENATType::EOS_NAT_Unknown; EOS_EResult res = EOS_P2P_GetNATType(s_p2pInterface, &options, &nat_type); Dictionary ret; ret["result_code"] = static_cast(res); if (res == EOS_EResult::EOS_Success) { ret["nat_type"] = static_cast(nat_type); } else { ret["nat_type"] = static_cast(EOS_ENATType::EOS_NAT_Unknown); } return ret; } int IEOS::p2p_interface_set_relay_control(int control) { ERR_FAIL_NULL_V(s_p2pInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_ERelayControl relay_control = static_cast(control); EOS_P2P_SetRelayControlOptions options = { 0 }; options.ApiVersion = EOS_P2P_SETRELAYCONTROL_API_LATEST; options.RelayControl = relay_control; return static_cast(EOS_P2P_SetRelayControl(s_p2pInterface, &options)); } Dictionary IEOS::p2p_interface_get_relay_control() { ERR_FAIL_NULL_V(s_p2pInterface, Dictionary()); EOS_P2P_GetRelayControlOptions options = { 0 }; options.ApiVersion = EOS_P2P_GETRELAYCONTROL_API_LATEST; EOS_ERelayControl relay_control; EOS_EResult res = EOS_P2P_GetRelayControl(s_p2pInterface, &options, &relay_control); Dictionary ret; ret["result_code"] = static_cast(res); if (res == EOS_EResult::EOS_Success) { ret["relay_control"] = static_cast(relay_control); } else { ret["relay_control"] = static_cast(EOS_ERelayControl::EOS_RC_NoRelays); } return ret; } int IEOS::p2p_interface_set_port_range(Ref options) { ERR_FAIL_NULL_V(s_p2pInterface, static_cast(EOS_EResult::EOS_InvalidState)); int port = options->get("port"); int max_additional_ports_to_try = options->get("max_additional_ports_to_try"); EOS_P2P_SetPortRangeOptions set_port_range_options = { 0 }; set_port_range_options.ApiVersion = EOS_P2P_SETPORTRANGE_API_LATEST; set_port_range_options.Port = port; set_port_range_options.MaxAdditionalPortsToTry = max_additional_ports_to_try; return static_cast(EOS_P2P_SetPortRange(s_p2pInterface, &set_port_range_options)); } Dictionary IEOS::p2p_interface_get_port_range() { ERR_FAIL_NULL_V(s_p2pInterface, {}); EOS_P2P_GetPortRangeOptions options = { 0 }; options.ApiVersion = EOS_P2P_GETPORTRANGE_API_LATEST; uint16_t port; uint16_t max_additional_ports_to_try; EOS_EResult res = EOS_P2P_GetPortRange(s_p2pInterface, &options, &port, &max_additional_ports_to_try); Dictionary ret; ret["result_code"] = static_cast(res); if (res != EOS_EResult::EOS_Success) { ret["port"] = port; ret["max_additional_ports_to_try"] = max_additional_ports_to_try; } else { ret["port"] = 0; ret["max_additional_ports_to_try"] = 0; } return ret; } int IEOS::p2p_interface_set_packet_queue_size(Ref options) { ERR_FAIL_NULL_V(s_p2pInterface, static_cast(EOS_EResult::EOS_InvalidState)); uint64_t incoming_packet_queue_max_size_bytes = options->get("incoming_packet_queue_max_size_bytes"); uint64_t outgoing_packet_queue_max_size_bytes = options->get("outgoing_packet_queue_max_size_bytes"); EOS_P2P_SetPacketQueueSizeOptions set_packet_queue_options; set_packet_queue_options.ApiVersion = EOS_P2P_SETPACKETQUEUESIZE_API_LATEST; set_packet_queue_options.IncomingPacketQueueMaxSizeBytes = incoming_packet_queue_max_size_bytes; set_packet_queue_options.OutgoingPacketQueueMaxSizeBytes = outgoing_packet_queue_max_size_bytes; return static_cast(EOS_P2P_SetPacketQueueSize(s_p2pInterface, &set_packet_queue_options)); } Dictionary IEOS::p2p_interface_get_packet_queue_info() { ERR_FAIL_NULL_V(s_p2pInterface, {}); EOS_P2P_GetPacketQueueInfoOptions options = { 0 }; options.ApiVersion = EOS_P2P_GETPACKETQUEUEINFO_API_LATEST; EOS_P2P_PacketQueueInfo info; EOS_EResult res = EOS_P2P_GetPacketQueueInfo(s_p2pInterface, &options, &info); Dictionary ret; ret["result_code"] = static_cast(res); ret["incoming_packet_queue_current_packet_count"] = info.IncomingPacketQueueCurrentPacketCount; ret["incoming_packet_queue_current_size_bytes"] = info.IncomingPacketQueueCurrentSizeBytes; ret["incoming_packet_queue_max_size_bytes"] = info.IncomingPacketQueueMaxSizeBytes; ret["outgoing_packet_queue_current_packet_count"] = info.OutgoingPacketQueueCurrentPacketCount; ret["outgoing_packet_queue_current_size_bytes"] = info.OutgoingPacketQueueCurrentSizeBytes; ret["outgoing_packet_queue_max_size_bytes"] = info.OutgoingPacketQueueMaxSizeBytes; return ret; } EOS_EResult IEOS::_p2p_send_packet(const EOS_P2P_SendPacketOptions *options) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_SendPacket(s_p2pInterface, options); return result; } EOS_EResult IEOS::_p2p_receive_packet(const EOS_P2P_ReceivePacketOptions *options, void *out_packet_data, uint32_t *out_packet_size, uint8_t *out_channel, EOS_ProductUserId *out_remote_user, EOS_P2P_SocketId *out_socket) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_ReceivePacket(s_p2pInterface, options, out_remote_user, out_socket, out_channel, out_packet_data, out_packet_size); return result; } EOS_EResult IEOS::_p2p_accept_connection(const EOS_P2P_AcceptConnectionOptions *options) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_AcceptConnection(s_p2pInterface, options); return result; } EOS_EResult IEOS::_p2p_close_connection(const EOS_P2P_CloseConnectionOptions *options) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_CloseConnection(s_p2pInterface, options); return result; } EOS_EResult IEOS::_p2p_close_all_connections(const EOS_P2P_CloseConnectionsOptions *options) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_CloseConnections(s_p2pInterface, options); return result; } EOS_EResult IEOS::_p2p_get_next_packet_size(const EOS_P2P_GetNextReceivedPacketSizeOptions *options, uint32_t *out_size) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_GetNextReceivedPacketSize(s_p2pInterface, options, out_size); return result; } EOS_EResult IEOS::_p2p_clear_packet_queue(const EOS_P2P_ClearPacketQueueOptions *options) { if (s_p2pInterface == nullptr) return EOS_EResult::EOS_InvalidParameters; EOS_EResult result = EOS_P2P_ClearPacketQueue(s_p2pInterface, options); return result; } EOS_NotificationId IEOS::_p2p_add_notify_peer_connection_established(const EOS_P2P_AddNotifyPeerConnectionEstablishedOptions *options, EOS_P2P_OnPeerConnectionEstablishedCallback callback) { if (s_p2pInterface == nullptr) return EOS_INVALID_NOTIFICATIONID; EOS_NotificationId callback_id = EOS_P2P_AddNotifyPeerConnectionEstablished(s_p2pInterface, options, nullptr, callback); return callback_id; } EOS_NotificationId IEOS::_p2p_add_notify_peer_connection_closed(const EOS_P2P_AddNotifyPeerConnectionClosedOptions *options, EOS_P2P_OnRemoteConnectionClosedCallback callback) { if (s_p2pInterface == nullptr) return EOS_INVALID_NOTIFICATIONID; EOS_NotificationId callback_id = EOS_P2P_AddNotifyPeerConnectionClosed(s_p2pInterface, options, nullptr, callback); return callback_id; } EOS_NotificationId IEOS::_p2p_add_notify_peer_connection_request(const EOS_P2P_AddNotifyPeerConnectionRequestOptions *options, EOS_P2P_OnIncomingConnectionRequestCallback callback) { if (s_p2pInterface == nullptr) return EOS_INVALID_NOTIFICATIONID; EOS_NotificationId callback_id = EOS_P2P_AddNotifyPeerConnectionRequest(s_p2pInterface, options, nullptr, callback); return callback_id; } EOS_NotificationId IEOS::_p2p_add_notify_peer_connection_interrupted(const EOS_P2P_AddNotifyPeerConnectionInterruptedOptions *options, EOS_P2P_OnPeerConnectionInterruptedCallback callback) { if (s_p2pInterface == nullptr) return EOS_INVALID_NOTIFICATIONID; EOS_NotificationId callback_id = EOS_P2P_AddNotifyPeerConnectionInterrupted(s_p2pInterface, options, nullptr, callback); return callback_id; } void IEOS::_p2p_remove_notify_peer_connection_established(EOS_NotificationId callback_id) { if (s_p2pInterface == nullptr) return; EOS_P2P_RemoveNotifyPeerConnectionEstablished(s_p2pInterface, callback_id); } void IEOS::_p2p_remove_notify_peer_connection_interrupted(EOS_NotificationId callback_id) { if (s_p2pInterface == nullptr) return; EOS_P2P_RemoveNotifyPeerConnectionInterrupted(s_p2pInterface, callback_id); } void IEOS::_p2p_remove_notify_peer_connection_closed(EOS_NotificationId callback_id) { if (s_p2pInterface == nullptr) return; EOS_P2P_RemoveNotifyPeerConnectionClosed(s_p2pInterface, callback_id); } void IEOS::_p2p_remove_notify_peer_connection_request(EOS_NotificationId callback_id) { if (s_p2pInterface == nullptr) return; EOS_P2P_RemoveNotifyPeerConnectionRequest(s_p2pInterface, callback_id); } ================================================ FILE: src/platform_interface.cpp ================================================ #include "ieos.h" using namespace godot; bool IEOS::platform_interface_create(Ref p_options) { CharString productId = VARIANT_TO_CHARSTRING(p_options->get("product_id")); CharString sandboxId = VARIANT_TO_CHARSTRING(p_options->get("sandbox_id")); CharString deploymentId = VARIANT_TO_CHARSTRING(p_options->get("deployment_id")); CharString encryptionKey = VARIANT_TO_CHARSTRING(p_options->get("encryption_key")); int flags = p_options->get("flags"); int tickBudgetInMilliseconds = p_options->get("tick_budget_in_milliseconds"); Variant taskNetworkTimeoutSeconds = p_options->get("task_network_timeout_seconds"); CharString clientId = VARIANT_TO_CHARSTRING(p_options->get("client_id")); CharString clientSecret = VARIANT_TO_CHARSTRING(p_options->get("client_secret")); CharString overrideCountryCode = VARIANT_TO_CHARSTRING(p_options->get("override_country_code")); CharString overrideLocaleCode = VARIANT_TO_CHARSTRING(p_options->get("override_locale_code")); CharString cache_directory = VARIANT_TO_CHARSTRING(p_options->get("cache_directory")); EOS_Platform_Options platformOptions; memset(&platformOptions, 0, sizeof(platformOptions)); platformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST; platformOptions.ProductId = productId.get_data(); platformOptions.SandboxId = sandboxId.get_data(); if (clientId.length() != 0) { platformOptions.ClientCredentials.ClientId = clientId.get_data(); } if (clientSecret.length() != 0) { platformOptions.ClientCredentials.ClientSecret = clientSecret.get_data(); } platformOptions.bIsServer = VARIANT_TO_EOS_BOOL(p_options->get("is_server")); if (encryptionKey.length() != 0) { platformOptions.EncryptionKey = encryptionKey.get_data(); } if (overrideCountryCode.length() != 0) { platformOptions.OverrideLocaleCode = overrideCountryCode.get_data(); } if (overrideLocaleCode.length() != 0) { platformOptions.OverrideCountryCode = overrideLocaleCode.get_data(); } if (cache_directory.length() != 0) { platformOptions.CacheDirectory = cache_directory.get_data(); } platformOptions.DeploymentId = deploymentId.get_data(); platformOptions.Flags = flags; platformOptions.TickBudgetInMilliseconds = tickBudgetInMilliseconds; EOS_Platform_RTCOptions rtcOptions; memset(&rtcOptions, 0, sizeof(rtcOptions)); rtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; #ifdef _WIN32 EOS_Windows_RTCOptions windowsRTCOptions; memset(&windowsRTCOptions, 0, sizeof(windowsRTCOptions)); windowsRTCOptions.ApiVersion = EOS_WINDOWS_RTCOPTIONS_API_LATEST; CharString xAudio29DllPath; if (OS::get_singleton()->has_feature("editor")) { xAudio29DllPath = ProjectSettings::get_singleton()->globalize_path("res://addons/epic-online-services-godot/bin/windows/x64/xaudio2_9redist.dll").utf8(); windowsRTCOptions.XAudio29DllPath = xAudio29DllPath.get_data(); } else { xAudio29DllPath = OS::get_singleton()->get_executable_path().get_base_dir().path_join("xaudio2_9redist.dll").utf8(); windowsRTCOptions.XAudio29DllPath = xAudio29DllPath.get_data(); } rtcOptions.PlatformSpecificOptions = &windowsRTCOptions; #else rtcOptions.PlatformSpecificOptions = nullptr; #endif // Handle background mode Ref p_rtc_options = p_options->get("rtc_options"); if (p_rtc_options->get("background_mode").get_type() != Variant::NIL) { int backgroundMode = p_rtc_options->get("background_mode"); rtcOptions.BackgroundMode = static_cast(backgroundMode); } platformOptions.RTCOptions = &rtcOptions; double p_taskNetworkTimeoutSeconds = taskNetworkTimeoutSeconds; if (taskNetworkTimeoutSeconds.get_type() != Variant::NIL) { platformOptions.TaskNetworkTimeoutSeconds = &p_taskNetworkTimeoutSeconds; } if (!platformOptions.IntegratedPlatformOptionsContainerHandle) { EOS_IntegratedPlatform_CreateIntegratedPlatformOptionsContainerOptions createIntegratedPlatformOptionsContainerOptions{}; createIntegratedPlatformOptionsContainerOptions.ApiVersion = EOS_INTEGRATEDPLATFORM_CREATEINTEGRATEDPLATFORMOPTIONSCONTAINER_API_LATEST; EOS_EResult createIntegratedPlatformOptionsContainerResult = EOS_IntegratedPlatform_CreateIntegratedPlatformOptionsContainer(&createIntegratedPlatformOptionsContainerOptions, &platformOptions.IntegratedPlatformOptionsContainerHandle); if (createIntegratedPlatformOptionsContainerResult != EOS_EResult::EOS_Success) { UtilityFunctions::printerr("\n[EOSG] Failed to create integrated platform options container:" + String(EOS_EResult_ToString(createIntegratedPlatformOptionsContainerResult))); } } s_platformInterface = EOS_Platform_Create(&platformOptions); if (platformOptions.IntegratedPlatformOptionsContainerHandle) { EOS_IntegratedPlatformOptionsContainer_Release(platformOptions.IntegratedPlatformOptionsContainerHandle); } if (s_platformInterface == nullptr) { isEOSValid = false; return false; } else { isEOSValid = true; } // ----- // Achievements Interface // ----- s_achievementsInterface = EOS_Platform_GetAchievementsInterface(s_platformInterface); if (s_achievementsInterface != nullptr) { EOS_Achievements_AddNotifyAchievementsUnlockedV2Options notifyAchievementsUnlockedV2Options; memset(¬ifyAchievementsUnlockedV2Options, 0, sizeof(notifyAchievementsUnlockedV2Options)); notifyAchievementsUnlockedV2Options.ApiVersion = EOS_ACHIEVEMENTS_ADDNOTIFYACHIEVEMENTSUNLOCKEDV2_API_LATEST; EOS_Achievements_AddNotifyAchievementsUnlockedV2(s_achievementsInterface, ¬ifyAchievementsUnlockedV2Options, nullptr, [](const EOS_Achievements_OnAchievementsUnlockedCallbackV2Info *data) { Dictionary ret; ret["user_id"] = eosg_product_user_id_to_string(data->UserId); ret["achievement_id"] = EOSG_GET_STRING(data->AchievementId); ret["unlock_time"] = data->UnlockTime; IEOS::get_singleton()->emit_signal("achievements_interface_achievements_unlocked_v2_callback", ret); }); } // ----- // Auth Interface // ----- s_authInterface = EOS_Platform_GetAuthInterface(s_platformInterface); if (s_authInterface != nullptr) { EOS_Auth_AddNotifyLoginStatusChangedOptions notifyAuthLoginStatusChangedOptions; memset(¬ifyAuthLoginStatusChangedOptions, 0, sizeof(notifyAuthLoginStatusChangedOptions)); notifyAuthLoginStatusChangedOptions.ApiVersion = EOS_AUTH_ADDNOTIFYLOGINSTATUSCHANGED_API_LATEST; EOS_Auth_AddNotifyLoginStatusChanged(s_authInterface, ¬ifyAuthLoginStatusChangedOptions, nullptr, [](const EOS_Auth_LoginStatusChangedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["prev_status"] = static_cast(data->PrevStatus); ret["current_status"] = static_cast(data->CurrentStatus); IEOS::get_singleton()->emit_signal("auth_interface_login_status_changed", ret); }); } // ----- // Connect Interface // ----- s_connectInterface = EOS_Platform_GetConnectInterface(s_platformInterface); if (s_connectInterface != nullptr) { EOS_Connect_AddNotifyAuthExpirationOptions notifyAuthExpirationOptions; memset(¬ifyAuthExpirationOptions, 0, sizeof(notifyAuthExpirationOptions)); notifyAuthExpirationOptions.ApiVersion = EOS_CONNECT_ADDNOTIFYAUTHEXPIRATION_API_LATEST; EOS_Connect_AddNotifyAuthExpiration(s_connectInterface, ¬ifyAuthExpirationOptions, nullptr, [](const EOS_Connect_AuthExpirationCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("connect_interface_auth_expiration", ret); }); EOS_Connect_AddNotifyLoginStatusChangedOptions notifyConnectLoginStatusChangedOptions; memset(¬ifyConnectLoginStatusChangedOptions, 0, sizeof(notifyConnectLoginStatusChangedOptions)); notifyConnectLoginStatusChangedOptions.ApiVersion = EOS_CONNECT_ADDNOTIFYLOGINSTATUSCHANGED_API_LATEST; EOS_Connect_AddNotifyLoginStatusChanged(s_connectInterface, ¬ifyConnectLoginStatusChangedOptions, nullptr, [](const EOS_Connect_LoginStatusChangedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["previous_status"] = static_cast(data->PreviousStatus); ret["current_status"] = static_cast(data->CurrentStatus); IEOS::get_singleton()->emit_signal("connect_interface_login_status_changed", ret); }); } // ----- // Custom Invites Interface // ----- s_customInvitesInterface = EOS_Platform_GetCustomInvitesInterface(s_platformInterface); if (s_customInvitesInterface != nullptr) { EOS_CustomInvites_AddNotifyCustomInviteReceivedOptions notifyCustomInviteReceivedOptions; notifyCustomInviteReceivedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYCUSTOMINVITERECEIVED_API_LATEST; EOS_CustomInvites_AddNotifyCustomInviteReceived(s_customInvitesInterface, ¬ifyCustomInviteReceivedOptions, nullptr, [](const EOS_CustomInvites_OnCustomInviteReceivedCallbackInfo *data) { Dictionary ret; ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["custom_invite_id"] = EOSG_GET_STRING(data->CustomInviteId); ret["payload"] = EOSG_GET_STRING(data->Payload); IEOS::get_singleton()->emit_signal("custom_invites_interface_custom_invite_received_callback", ret); }); EOS_CustomInvites_AddNotifyCustomInviteAcceptedOptions notifyCustomInviteAcceptedOptions; notifyCustomInviteAcceptedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYCUSTOMINVITEACCEPTED_API_LATEST; EOS_CustomInvites_AddNotifyCustomInviteAccepted(s_customInvitesInterface, ¬ifyCustomInviteAcceptedOptions, nullptr, [](const EOS_CustomInvites_OnCustomInviteAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["custom_invite_id"] = EOSG_GET_STRING(data->CustomInviteId); ret["payload"] = EOSG_GET_STRING(data->Payload); IEOS::get_singleton()->emit_signal("custom_invites_interface_custom_invite_accepted_callback", ret); }); EOS_CustomInvites_AddNotifyCustomInviteRejectedOptions notifyCustomInviteRejectedOptions; notifyCustomInviteRejectedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYCUSTOMINVITEREJECTED_API_LATEST; EOS_CustomInvites_AddNotifyCustomInviteRejected(s_customInvitesInterface, ¬ifyCustomInviteRejectedOptions, nullptr, [](const EOS_CustomInvites_CustomInviteRejectedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["custom_invite_id"] = EOSG_GET_STRING(data->CustomInviteId); ret["payload"] = EOSG_GET_STRING(data->Payload); IEOS::get_singleton()->emit_signal("custom_invites_interface_custom_invite_rejected_callback", ret); }); EOS_CustomInvites_AddNotifyRequestToJoinResponseReceivedOptions notifyCustomRequestToJoinResponseReceivedOptions; notifyCustomRequestToJoinResponseReceivedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYREQUESTTOJOINRESPONSERECEIVED_API_LATEST; EOS_CustomInvites_AddNotifyRequestToJoinResponseReceived(s_customInvitesInterface, ¬ifyCustomRequestToJoinResponseReceivedOptions, nullptr, [](const EOS_CustomInvites_RequestToJoinResponseReceivedCallbackInfo *data) { Dictionary ret; ret["from_user_id"] = eosg_product_user_id_to_string(data->FromUserId); ret["to_user_id"] = eosg_product_user_id_to_string(data->ToUserId); ret["response"] = static_cast(data->Response); IEOS::get_singleton()->emit_signal("custom_invites_interface_request_to_join_response_received_callback", ret); }); EOS_CustomInvites_AddNotifyRequestToJoinReceivedOptions notifyCustomRequestToJoinReceivedOptions; notifyCustomRequestToJoinReceivedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYREQUESTTOJOINRECEIVED_API_LATEST; EOS_CustomInvites_AddNotifyRequestToJoinReceived(s_customInvitesInterface, ¬ifyCustomRequestToJoinReceivedOptions, nullptr, [](const EOS_CustomInvites_RequestToJoinReceivedCallbackInfo *data) { Dictionary ret; ret["from_user_id"] = eosg_product_user_id_to_string(data->FromUserId); ret["to_user_id"] = eosg_product_user_id_to_string(data->ToUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_request_to_join_received_callback", ret); }); EOS_CustomInvites_AddNotifyRequestToJoinAcceptedOptions notifyCustomRequestToJoinAcceptedOptions; notifyCustomRequestToJoinAcceptedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYREQUESTTOJOINACCEPTED_API_LATEST; EOS_CustomInvites_AddNotifyRequestToJoinAccepted(s_customInvitesInterface, ¬ifyCustomRequestToJoinAcceptedOptions, nullptr, [](const EOS_CustomInvites_OnRequestToJoinAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_request_to_join_accepted_callback", ret); }); EOS_CustomInvites_AddNotifyRequestToJoinRejectedOptions notifyCustomRequestToJoinRejectedOptions; notifyCustomRequestToJoinRejectedOptions.ApiVersion = EOS_CUSTOMINVITES_ADDNOTIFYREQUESTTOJOINREJECTED_API_LATEST; EOS_CustomInvites_AddNotifyRequestToJoinRejected(s_customInvitesInterface, ¬ifyCustomRequestToJoinRejectedOptions, nullptr, [](const EOS_CustomInvites_OnRequestToJoinRejectedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("custom_invites_interface_request_to_join_rejected_callback", ret); }); } // ----- // ECOM Interface // ----- s_ecomInterface = EOS_Platform_GetEcomInterface(s_platformInterface); // ----- // Friends Interface // ----- s_friendsInterface = EOS_Platform_GetFriendsInterface(s_platformInterface); if (s_friendsInterface != nullptr) { EOS_Friends_AddNotifyFriendsUpdateOptions notifyFriendsUpdateOptions; notifyFriendsUpdateOptions.ApiVersion = EOS_FRIENDS_ADDNOTIFYFRIENDSUPDATE_API_LATEST; EOS_Friends_AddNotifyFriendsUpdate(s_friendsInterface, ¬ifyFriendsUpdateOptions, nullptr, [](const EOS_Friends_OnFriendsUpdateInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); ret["previous_status"] = static_cast(data->PreviousStatus); ret["current_status"] = static_cast(data->CurrentStatus); IEOS::get_singleton()->emit_signal("friends_interface_friends_update_callback", ret); }); EOS_Friends_AddNotifyBlockedUsersUpdateOptions notifyBlockedUsersUpdateOptions; notifyBlockedUsersUpdateOptions.ApiVersion = EOS_FRIENDS_ADDNOTIFYBLOCKEDUSERSUPDATE_API_LATEST; EOS_Friends_AddNotifyBlockedUsersUpdate(s_friendsInterface, ¬ifyBlockedUsersUpdateOptions, nullptr, [](const EOS_Friends_OnBlockedUsersUpdateInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); ret["blocked"] = EOSG_GET_BOOL(data->bBlocked); IEOS::get_singleton()->emit_signal("friends_interface_blocked_users_update_callback", ret); }); } // ----- // KWS Interface // ----- s_kwsInterface = EOS_Platform_GetKWSInterface(s_platformInterface); if (s_kwsInterface != nullptr) { EOS_KWS_AddNotifyPermissionsUpdateReceivedOptions notifyPermissionsUpdateReceivedOptions; notifyPermissionsUpdateReceivedOptions.ApiVersion = EOS_KWS_ADDNOTIFYPERMISSIONSUPDATERECEIVED_API_LATEST; EOS_KWS_AddNotifyPermissionsUpdateReceived(s_kwsInterface, ¬ifyPermissionsUpdateReceivedOptions, nullptr, [](const EOS_KWS_PermissionsUpdateReceivedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["kws_user_id"] = EOSG_GET_STRING(data->KWSUserId); ret["date_of_birth"] = EOSG_GET_STRING(data->DateOfBirth); ret["is_minor"] = EOSG_GET_BOOL(data->bIsMinor); ret["parent_email"] = EOSG_GET_STRING(data->ParentEmail); IEOS::get_singleton()->emit_signal("kws_interface_permissions_update_received_callback", ret); }); } // ----- // Leaderboards Interface // ----- s_leaderboardsInterface = EOS_Platform_GetLeaderboardsInterface(s_platformInterface); // ----- // Lobby Interface // ----- s_lobbyInterface = EOS_Platform_GetLobbyInterface(s_platformInterface); if (s_lobbyInterface != nullptr) { EOS_Lobby_AddNotifyLobbyUpdateReceivedOptions notifyLobbyUpdateReceivedOptions; notifyLobbyUpdateReceivedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYUPDATERECEIVED_API_LATEST; EOS_Lobby_AddNotifyLobbyUpdateReceived(s_lobbyInterface, ¬ifyLobbyUpdateReceivedOptions, nullptr, [](const EOS_Lobby_LobbyUpdateReceivedCallbackInfo *data) { Dictionary ret; ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_update_received_callback", ret); }); EOS_Lobby_AddNotifyLobbyMemberUpdateReceivedOptions notifyLobbyMemberUpdateReceivedOptions; notifyLobbyMemberUpdateReceivedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERUPDATERECEIVED_API_LATEST; EOS_Lobby_AddNotifyLobbyMemberUpdateReceived(s_lobbyInterface, ¬ifyLobbyMemberUpdateReceivedOptions, nullptr, [](const EOS_Lobby_LobbyMemberUpdateReceivedCallbackInfo *data) { Dictionary ret; ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_member_update_received_callback", ret); }); EOS_Lobby_AddNotifyLobbyMemberStatusReceivedOptions notifyLobbyMemberStatusReceivedOptions; notifyLobbyMemberStatusReceivedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYMEMBERSTATUSRECEIVED_API_LATEST; EOS_Lobby_AddNotifyLobbyMemberStatusReceived(s_lobbyInterface, ¬ifyLobbyMemberStatusReceivedOptions, nullptr, [](const EOS_Lobby_LobbyMemberStatusReceivedCallbackInfo *data) { Dictionary ret; ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["current_status"] = static_cast(data->CurrentStatus); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_member_status_received_callback", ret); }); EOS_Lobby_AddNotifyLobbyInviteReceivedOptions notifyLobbyInviteReceivedOptions; notifyLobbyInviteReceivedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYINVITERECEIVED_API_LATEST; EOS_Lobby_AddNotifyLobbyInviteReceived(s_lobbyInterface, ¬ifyLobbyInviteReceivedOptions, nullptr, [](const EOS_Lobby_LobbyInviteReceivedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["invite_id"] = EOSG_GET_STRING(data->InviteId); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_invite_received_callback", ret); }); EOS_Lobby_AddNotifyLobbyInviteAcceptedOptions notifyLobbyInviteAcceptedOptions; notifyLobbyInviteAcceptedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYINVITEACCEPTED_API_LATEST; EOS_Lobby_AddNotifyLobbyInviteAccepted(s_lobbyInterface, ¬ifyLobbyInviteAcceptedOptions, nullptr, [](const EOS_Lobby_LobbyInviteAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["invite_id"] = EOSG_GET_STRING(data->InviteId); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_invite_accepted_callback", ret); }); EOS_Lobby_AddNotifyLobbyInviteRejectedOptions notifyLobbyInviteRejectedOptions; notifyLobbyInviteRejectedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLOBBYINVITEREJECTED_API_LATEST; EOS_Lobby_AddNotifyLobbyInviteRejected(s_lobbyInterface, ¬ifyLobbyInviteRejectedOptions, nullptr, [](const EOS_Lobby_LobbyInviteRejectedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); ret["invite_id"] = EOSG_GET_STRING(data->InviteId); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_lobby_invite_rejected_callback", ret); }); EOS_Lobby_AddNotifyJoinLobbyAcceptedOptions notifyJoinLobbyAcceptedOptions; notifyJoinLobbyAcceptedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYJOINLOBBYACCEPTED_API_LATEST; EOS_Lobby_AddNotifyJoinLobbyAccepted(s_lobbyInterface, ¬ifyJoinLobbyAcceptedOptions, nullptr, [](const EOS_Lobby_JoinLobbyAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["ui_event_id"] = data->UiEventId; IEOS::get_singleton()->emit_signal("lobby_interface_join_lobby_accepted_callback", ret); }); EOS_Lobby_AddNotifySendLobbyNativeInviteRequestedOptions notifySendLobbyNativeInviteRequestedOptions; notifySendLobbyNativeInviteRequestedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYSENDLOBBYNATIVEINVITEREQUESTED_API_LATEST; EOS_Lobby_AddNotifySendLobbyNativeInviteRequested(s_lobbyInterface, ¬ifySendLobbyNativeInviteRequestedOptions, nullptr, [](const EOS_Lobby_SendLobbyNativeInviteRequestedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["ui_event_id"] = data->UiEventId; ret["target_native_account_type"] = EOSG_GET_STRING(data->TargetNativeAccountType); ret["target_native_account_id"] = EOSG_GET_STRING(data->TargetUserNativeAccountId); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_send_lobby_native_invite_requested_callback", ret); }); EOS_Lobby_AddNotifyRTCRoomConnectionChangedOptions notifyRTCRoomConnectionChangedOptions; notifyRTCRoomConnectionChangedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYRTCROOMCONNECTIONCHANGED_API_LATEST; EOS_Lobby_AddNotifyRTCRoomConnectionChanged(s_lobbyInterface, ¬ifyRTCRoomConnectionChangedOptions, nullptr, [](const EOS_Lobby_RTCRoomConnectionChangedCallbackInfo *data) { Dictionary ret; ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["is_connected"] = EOSG_GET_BOOL(data->bIsConnected); ret["disconnect_reason"] = static_cast(data->DisconnectReason); IEOS::get_singleton()->emit_signal("lobby_interface_rtc_room_connection_changed_callback", ret); }); EOS_Lobby_AddNotifyLeaveLobbyRequestedOptions notifyLeaveLobbyRequestedOptions; notifyLeaveLobbyRequestedOptions.ApiVersion = EOS_LOBBY_ADDNOTIFYLEAVELOBBYREQUESTED_API_LATEST; EOS_Lobby_AddNotifyLeaveLobbyRequested(s_lobbyInterface, ¬ifyLeaveLobbyRequestedOptions, nullptr, [](const EOS_Lobby_LeaveLobbyRequestedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["lobby_id"] = EOSG_GET_STRING(data->LobbyId); IEOS::get_singleton()->emit_signal("lobby_interface_leave_lobby_requested_callback", ret); }); } // ----- // Metrics Interface // ----- s_metricsInterface = EOS_Platform_GetMetricsInterface(s_platformInterface); // ----- // Mods Interface // ----- s_modsInterface = EOS_Platform_GetModsInterface(s_platformInterface); // ----- // P2P Interface // ----- s_p2pInterface = EOS_Platform_GetP2PInterface(s_platformInterface); if (s_p2pInterface != nullptr) { EOS_P2P_AddNotifyIncomingPacketQueueFullOptions notifyIncomingPacketQueueFullOptions; notifyIncomingPacketQueueFullOptions.ApiVersion = EOS_P2P_ADDNOTIFYINCOMINGPACKETQUEUEFULL_API_LATEST; EOS_P2P_AddNotifyIncomingPacketQueueFull(s_p2pInterface, ¬ifyIncomingPacketQueueFullOptions, nullptr, [](const EOS_P2P_OnIncomingPacketQueueFullInfo *data) { Dictionary ret; ret["overflow_packet_channel"] = data->OverflowPacketChannel; ret["overflow_packet_local_user_id"] = eosg_product_user_id_to_string(data->OverflowPacketLocalUserId); ret["overflow_packet_size_bytes"] = data->OverflowPacketSizeBytes; ret["packet_queue_current_size_bytes"] = data->PacketQueueCurrentSizeBytes; ret["packet_queue_max_size_bytes"] = data->PacketQueueMaxSizeBytes; IEOS::get_singleton()->emit_signal("p2p_interface_incoming_packet_queue_full", ret); }); } // ----- // PlayerDataStorage Interface // ----- s_playerDataStorageInterface = EOS_Platform_GetPlayerDataStorageInterface(s_platformInterface); // ----- // Presence Interface // ----- s_presenceInterface = EOS_Platform_GetPresenceInterface(s_platformInterface); if (s_presenceInterface != nullptr) { EOS_Presence_AddNotifyOnPresenceChangedOptions notifyOnPresenceChangedOptions; notifyOnPresenceChangedOptions.ApiVersion = EOS_PRESENCE_ADDNOTIFYONPRESENCECHANGED_API_LATEST; EOS_Presence_AddNotifyOnPresenceChanged(s_presenceInterface, ¬ifyOnPresenceChangedOptions, nullptr, [](const EOS_Presence_PresenceChangedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["presence_user_id"] = eosg_epic_account_id_to_string(data->PresenceUserId); IEOS::get_singleton()->emit_signal("presence_interface_presence_changed_callback", ret); }); EOS_Presence_AddNotifyJoinGameAcceptedOptions notifyJoinGameAcceptedOptions; notifyJoinGameAcceptedOptions.ApiVersion = EOS_PRESENCE_ADDNOTIFYJOINGAMEACCEPTED_API_LATEST; EOS_Presence_AddNotifyJoinGameAccepted(s_presenceInterface, ¬ifyJoinGameAcceptedOptions, nullptr, [](const EOS_Presence_JoinGameAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); ret["ui_event_id"] = data->UiEventId; ret["join_info"] = EOSG_GET_STRING(data->JoinInfo); IEOS::get_singleton()->emit_signal("presence_interface_join_game_accepted_callback", ret); }); } // ----- // ProgressionSnapshot Interface // ----- s_progressionSnapshotInterface = EOS_Platform_GetProgressionSnapshotInterface(s_platformInterface); // ----- // Reports Interface // ----- s_reportsInterface = EOS_Platform_GetReportsInterface(s_platformInterface); // ----- // Sanctions Interface // ----- s_sanctionsInterface = EOS_Platform_GetSanctionsInterface(s_platformInterface); // ----- // Sessions Interface // ----- s_sessionsInterface = EOS_Platform_GetSessionsInterface(s_platformInterface); if (s_sessionsInterface != nullptr) { EOS_Sessions_AddNotifySessionInviteReceivedOptions notifySessionInviteReceivedOptions; notifySessionInviteReceivedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYSESSIONINVITERECEIVED_API_LATEST; EOS_Sessions_AddNotifySessionInviteReceived(s_sessionsInterface, ¬ifySessionInviteReceivedOptions, nullptr, [](const EOS_Sessions_SessionInviteReceivedCallbackInfo *data) { Dictionary ret; ret["invite_id"] = EOSG_GET_STRING(data->InviteId); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("sessions_interface_session_invite_received_callback", ret); }); EOS_Sessions_AddNotifySessionInviteAcceptedOptions notifySessionInviteAcceptedOptions; notifySessionInviteAcceptedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYSESSIONINVITEACCEPTED_API_LATEST; EOS_Sessions_AddNotifySessionInviteAccepted(s_sessionsInterface, ¬ifySessionInviteAcceptedOptions, nullptr, [](const EOS_Sessions_SessionInviteAcceptedCallbackInfo *data) { Dictionary ret; ret["invite_id"] = EOSG_GET_STRING(data->InviteId); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["session_id"] = EOSG_GET_STRING(data->SessionId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("sessions_interface_session_invite_accepted_callback", ret); }); EOS_Sessions_AddNotifySessionInviteRejectedOptions notifySessionInviteRejectedOptions; notifySessionInviteRejectedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYSESSIONINVITEREJECTED_API_LATEST; EOS_Sessions_AddNotifySessionInviteRejected(s_sessionsInterface, ¬ifySessionInviteRejectedOptions, nullptr, [](const EOS_Sessions_SessionInviteRejectedCallbackInfo *data) { Dictionary ret; ret["invite_id"] = EOSG_GET_STRING(data->InviteId); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["session_id"] = EOSG_GET_STRING(data->SessionId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("sessions_interface_session_invite_rejected_callback", ret); }); EOS_Sessions_AddNotifyJoinSessionAcceptedOptions notifyJoinSessionAcceptedOptions; notifyJoinSessionAcceptedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYJOINSESSIONACCEPTED_API_LATEST; EOS_Sessions_AddNotifyJoinSessionAccepted(s_sessionsInterface, ¬ifyJoinSessionAcceptedOptions, nullptr, [](const EOS_Sessions_JoinSessionAcceptedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["ui_event_id"] = data->UiEventId; IEOS::get_singleton()->emit_signal("sessions_interface_join_session_accepted_callback", ret); }); EOS_Sessions_AddNotifyLeaveSessionRequestedOptions notifyLeaveSessionRequestedOptions; notifyLeaveSessionRequestedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYLEAVESESSIONREQUESTED_API_LATEST; EOS_Sessions_AddNotifyLeaveSessionRequested(s_sessionsInterface, ¬ifyLeaveSessionRequestedOptions, nullptr, [](const EOS_Sessions_LeaveSessionRequestedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["session_name"] = EOSG_GET_STRING(data->SessionName); IEOS::get_singleton()->emit_signal("sessions_interface_leave_session_requested_callback", ret); }); EOS_Sessions_AddNotifySendSessionNativeInviteRequestedOptions notifySendSessionNativeInviteRequestedOptions; notifySendSessionNativeInviteRequestedOptions.ApiVersion = EOS_SESSIONS_ADDNOTIFYSENDSESSIONNATIVEINVITEREQUESTED_API_LATEST; EOS_Sessions_AddNotifySendSessionNativeInviteRequested(s_sessionsInterface, ¬ifySendSessionNativeInviteRequestedOptions, nullptr, [](const EOS_Sessions_SendSessionNativeInviteRequestedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["session_id"] = EOSG_GET_STRING(data->SessionId); ret["target_native_account_type"] = EOSG_GET_STRING(data->TargetNativeAccountType); ret["target_user_native_account_id"] = EOSG_GET_STRING(data->TargetUserNativeAccountId); ret["ui_event_id"] = data->UiEventId; IEOS::get_singleton()->emit_signal("sessions_interface_send_session_native_invite_requested_callback", ret); }); } // ----- // Stats Interface // ----- s_statsInterface = EOS_Platform_GetStatsInterface(s_platformInterface); // ----- // RTC Interface // ----- s_rtcInterface = EOS_Platform_GetRTCInterface(s_platformInterface); // ----- // RTCAudio Interface // ----- s_rtcAudioInterface = EOS_RTC_GetAudioInterface(s_rtcInterface); if (s_rtcAudioInterface != nullptr) { EOS_RTCAudio_AddNotifyAudioDevicesChangedOptions notifyAudioDevicesChangedOptions; memset(¬ifyAudioDevicesChangedOptions, 0, sizeof(notifyAudioDevicesChangedOptions)); notifyAudioDevicesChangedOptions.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYAUDIODEVICESCHANGED_API_LATEST; EOS_RTCAudio_AddNotifyAudioDevicesChanged(s_rtcAudioInterface, ¬ifyAudioDevicesChangedOptions, nullptr, [](const EOS_RTCAudio_AudioDevicesChangedCallbackInfo *data) { Dictionary ret; IEOS::get_singleton()->emit_signal("rtc_audio_audio_devices_changed", ret); }); } // ----- // RTCData Interface // ----- s_rtcDataInterface = EOS_RTC_GetDataInterface(s_rtcInterface); // ----- // TitleStorage Interface // ----- s_titleStorageInterface = EOS_Platform_GetTitleStorageInterface(s_platformInterface); // ----- // UI Interface // ----- s_uiInterface = EOS_Platform_GetUIInterface(s_platformInterface); if (s_uiInterface != nullptr) { EOS_UI_AddNotifyDisplaySettingsUpdatedOptions notifyDisplaySettingsUpdatedOptions; notifyDisplaySettingsUpdatedOptions.ApiVersion = EOS_UI_ADDNOTIFYDISPLAYSETTINGSUPDATED_API_LATEST; EOS_UI_AddNotifyDisplaySettingsUpdated(s_uiInterface, ¬ifyDisplaySettingsUpdatedOptions, nullptr, [](const EOS_UI_OnDisplaySettingsUpdatedCallbackInfo *data) { Dictionary ret; ret["is_visible"] = EOSG_GET_BOOL(data->bIsVisible); ret["is_exclusive_input"] = EOSG_GET_BOOL(data->bIsExclusiveInput); IEOS::get_singleton()->emit_signal("ui_interface_display_settings_updated_callback", ret); }); EOS_UI_AddNotifyMemoryMonitorOptions notifyMemoryMonitorOptions; notifyMemoryMonitorOptions.ApiVersion = EOS_UI_ADDNOTIFYMEMORYMONITOR_API_LATEST; EOS_UI_AddNotifyMemoryMonitor(s_uiInterface, ¬ifyMemoryMonitorOptions, nullptr, [](const EOS_UI_MemoryMonitorCallbackInfo *data) { Dictionary ret; IEOS::get_singleton()->emit_signal("ui_interface_memory_monitor_callback", ret); }); } // ----- // UserInfo Interface // ----- s_userInfoInterface = EOS_Platform_GetUserInfoInterface(s_platformInterface); // ----- // AntiCheatServer Interface // ----- s_antiCheatServerInterface = EOS_Platform_GetAntiCheatServerInterface(s_platformInterface); if (s_antiCheatServerInterface != nullptr) { EOS_AntiCheatServer_AddNotifyMessageToClientOptions notifyMessageToClientOptions; notifyMessageToClientOptions.ApiVersion = EOS_ANTICHEATSERVER_ADDNOTIFYMESSAGETOCLIENT_API_LATEST; EOS_AntiCheatServer_AddNotifyMessageToClient(s_antiCheatServerInterface, ¬ifyMessageToClientOptions, nullptr, [](const EOS_AntiCheatCommon_OnMessageToClientCallbackInfo *data) { PackedByteArray buffer; buffer.resize(data->MessageDataSizeBytes); std::memcpy(buffer.ptrw(), data->MessageData, data->MessageDataSizeBytes); String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); Dictionary ret; ret["client_handle"] = client_handle; ret["message_data"] = buffer; IEOS::get_singleton()->emit_signal("anticheatserver_interface_message_to_client_callback", ret); }); EOS_AntiCheatServer_AddNotifyClientActionRequiredOptions notifyClientActionRequiredOptions; notifyClientActionRequiredOptions.ApiVersion = EOS_ANTICHEATSERVER_ADDNOTIFYCLIENTACTIONREQUIRED_API_LATEST; EOS_AntiCheatServer_AddNotifyClientActionRequired(s_antiCheatServerInterface, ¬ifyClientActionRequiredOptions, nullptr, [](const EOS_AntiCheatCommon_OnClientActionRequiredCallbackInfo *data) { Dictionary ret; String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); ret["client_handle"] = client_handle; ret["client_action"] = static_cast(data->ClientAction); ret["action_reason_code"] = static_cast(data->ActionReasonCode); ret["action_reason_details_string"] = EOSG_GET_STRING(data->ActionReasonDetailsString); IEOS::get_singleton()->emit_signal("anticheatserver_interface_client_action_required_callback", ret); }); EOS_AntiCheatServer_AddNotifyClientAuthStatusChangedOptions notifyClientAuthStatusChangedOptions; notifyClientAuthStatusChangedOptions.ApiVersion = EOS_ANTICHEATSERVER_ADDNOTIFYCLIENTAUTHSTATUSCHANGED_API_LATEST; EOS_AntiCheatServer_AddNotifyClientAuthStatusChanged(s_antiCheatServerInterface, ¬ifyClientAuthStatusChangedOptions, nullptr, [](const EOS_AntiCheatCommon_OnClientAuthStatusChangedCallbackInfo *data) { Dictionary ret; String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); ret["client_handle"] = client_handle; ret["client_auth_status"] = static_cast(data->ClientAuthStatus); IEOS::get_singleton()->emit_signal("anticheatserver_interface_client_auth_status_changed_callback", ret); }); } // ----- // AntiCheatClient Interface // ----- s_antiCheatClientInterface = EOS_Platform_GetAntiCheatClientInterface(s_platformInterface); if (s_antiCheatClientInterface != nullptr) { EOS_AntiCheatClient_AddNotifyMessageToServerOptions notifyMessageToServerOptions; notifyMessageToServerOptions.ApiVersion = EOS_ANTICHEATCLIENT_ADDNOTIFYMESSAGETOSERVER_API_LATEST; EOS_AntiCheatClient_AddNotifyMessageToServer(s_antiCheatClientInterface, ¬ifyMessageToServerOptions, nullptr, [](const EOS_AntiCheatClient_OnMessageToServerCallbackInfo *data) { PackedByteArray buffer; buffer.resize(data->MessageDataSizeBytes); std::memcpy(buffer.ptrw(), data->MessageData, data->MessageDataSizeBytes); Dictionary ret; ret["message_data"] = buffer; IEOS::get_singleton()->emit_signal("anticheat_client_interface_message_to_server_callback", ret); }); EOS_AntiCheatClient_AddNotifyMessageToPeerOptions notifyMessageToPeerOptions; notifyMessageToPeerOptions.ApiVersion = EOS_ANTICHEATCLIENT_ADDNOTIFYMESSAGETOPEER_API_LATEST; EOS_AntiCheatClient_AddNotifyMessageToPeer(s_antiCheatClientInterface, ¬ifyMessageToPeerOptions, nullptr, [](const EOS_AntiCheatCommon_OnMessageToClientCallbackInfo *data) { PackedByteArray buffer; buffer.resize(data->MessageDataSizeBytes); std::memcpy(buffer.ptrw(), data->MessageData, data->MessageDataSizeBytes); String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); Dictionary ret; ret["client_handle"] = client_handle; ret["message_data"] = buffer; IEOS::get_singleton()->emit_signal("anticheat_client_interface_message_to_peer_callback", ret); }); EOS_AntiCheatClient_AddNotifyPeerActionRequiredOptions notifyPeerActionRequiredOptions; notifyPeerActionRequiredOptions.ApiVersion = EOS_ANTICHEATCLIENT_ADDNOTIFYPEERACTIONREQUIRED_API_LATEST; EOS_AntiCheatClient_AddNotifyPeerActionRequired(s_antiCheatClientInterface, ¬ifyPeerActionRequiredOptions, nullptr, [](const EOS_AntiCheatCommon_OnClientActionRequiredCallbackInfo *data) { String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); Dictionary ret; ret["client_handle"] = client_handle; ret["client_action"] = static_cast(data->ClientAction); ret["action_reason_code"] = static_cast(data->ActionReasonCode); ret["action_reason_details_string"] = EOSG_GET_STRING(data->ActionReasonDetailsString); }); EOS_AntiCheatClient_AddNotifyPeerAuthStatusChangedOptions notifyPeerAuthStatusChangedOptions; notifyPeerAuthStatusChangedOptions.ApiVersion = EOS_ANTICHEATCLIENT_ADDNOTIFYPEERAUTHSTATUSCHANGED_API_LATEST; EOS_AntiCheatClient_AddNotifyPeerAuthStatusChanged(s_antiCheatClientInterface, ¬ifyPeerAuthStatusChangedOptions, nullptr, [](const EOS_AntiCheatCommon_OnClientAuthStatusChangedCallbackInfo *data) { String client_handle = IEOS::get_singleton()->_anticheat_player_handle_to_id(data->ClientHandle); Dictionary ret; ret["client_handle"] = client_handle; ret["client_auth_status"] = static_cast(data->ClientAuthStatus); }); EOS_AntiCheatClient_AddNotifyClientIntegrityViolatedOptions notifyClientIntegrityViolatedOptions; notifyClientIntegrityViolatedOptions.ApiVersion = EOS_ANTICHEATCLIENT_ADDNOTIFYCLIENTINTEGRITYVIOLATED_API_LATEST; EOS_AntiCheatClient_AddNotifyClientIntegrityViolated(s_antiCheatClientInterface, ¬ifyClientIntegrityViolatedOptions, nullptr, [](const EOS_AntiCheatClient_OnClientIntegrityViolatedCallbackInfo *data) { Dictionary ret; ret["violation_type"] = static_cast(data->ViolationType); ret["violation_message"] = EOSG_GET_STRING(data->ViolationMessage); IEOS::get_singleton()->emit_signal("anticheat_client_interface_client_integrity_violated_callback", ret); }); } return true; } Dictionary IEOS::platform_interface_get_active_country_code(const String &p_local_user_id) { ERR_FAIL_NULL_V(s_platformInterface, {}); CharString user_id = p_local_user_id.utf8(); EOS_EpicAccountId localUserId = EOS_EpicAccountId_FromString(user_id.get_data()); char *OutBuffer = (char *)memalloc(EOS_COUNTRYCODE_MAX_LENGTH + 1); int OutBufferLength = 0; EOS_EResult res = EOS_Platform_GetActiveCountryCode(s_platformInterface, localUserId, OutBuffer, &OutBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["country_code"] = ""; if (res == EOS_EResult::EOS_Success) { ret["country_code"] = String(OutBuffer); } memfree(OutBuffer); return ret; } Dictionary IEOS::platform_interface_get_active_locale_code(const String &p_user_id) { ERR_FAIL_NULL_V(s_platformInterface, {}); CharString user_id = p_user_id.utf8(); EOS_EpicAccountId LocalUserId = EOS_EpicAccountId_FromString(user_id.get_data()); char *OutBuffer = (char *)memalloc(EOS_LOCALECODE_MAX_LENGTH + 1); int OutBufferLength = 0; EOS_EResult res = EOS_Platform_GetActiveLocaleCode(s_platformInterface, LocalUserId, OutBuffer, &OutBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["locale_code"] = ""; if (res == EOS_EResult::EOS_Success) { ret["locale_code"] = String(OutBuffer); } memfree(OutBuffer); return ret; } Dictionary IEOS::platform_interface_get_override_country_code() { ERR_FAIL_NULL_V(s_platformInterface, {}); char *OutBuffer = (char *)memalloc(EOS_COUNTRYCODE_MAX_LENGTH + 1); int32_t *OutBufferLength = (int32_t *)memalloc(sizeof(int32_t)); EOS_EResult res = EOS_Platform_GetOverrideCountryCode(s_platformInterface, OutBuffer, OutBufferLength); Dictionary dict; dict["result_code"] = static_cast(res); dict["country_code"] = ""; if (res == EOS_EResult::EOS_Success) { dict["country_code"] = String(OutBuffer); } memfree(OutBuffer); memfree(OutBufferLength); return dict; } Dictionary IEOS::platform_interface_get_override_locale_code() { ERR_FAIL_NULL_V(s_platformInterface, {}); char *OutBuffer = (char *)memalloc(EOS_LOCALECODE_MAX_LENGTH + 1); int32_t *OutBufferLength = (int32_t *)memalloc(sizeof(int32_t)); EOS_EResult res = EOS_Platform_GetOverrideLocaleCode(s_platformInterface, OutBuffer, OutBufferLength); Dictionary dict; dict["result_code"] = static_cast(res); dict["locale_code"] = ""; if (res == EOS_EResult::EOS_Success) { dict["locale_code"] = String(OutBuffer); } memfree(OutBuffer); memfree(OutBufferLength); return dict; } int IEOS::platform_interface_set_override_country_code(const String &p_country_code) { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString country_code = p_country_code.utf8(); return static_cast(EOS_Platform_SetOverrideCountryCode(s_platformInterface, country_code.get_data())); } int IEOS::platform_interface_set_override_locale_code(const String &p_locale_code) { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString locale_code = p_locale_code.utf8(); return static_cast(EOS_Platform_SetOverrideLocaleCode(s_platformInterface, locale_code.get_data())); } int IEOS::platform_interface_check_for_launcher_and_restart() { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EResult::EOS_InvalidState)); return static_cast(EOS_Platform_CheckForLauncherAndRestart(s_platformInterface)); } int IEOS::platform_interface_initialize(Ref p_options) { CharString productName = VARIANT_TO_CHARSTRING(p_options->get("product_name")); CharString productVersion = VARIANT_TO_CHARSTRING(p_options->get("product_version")); EOS_InitializeOptions initOptions; memset(&initOptions, 0, sizeof(initOptions)); initOptions.ApiVersion = EOS_INITIALIZE_API_LATEST; initOptions.ProductName = productName.get_data(); initOptions.ProductVersion = productVersion.get_data(); initOptions.AllocateMemoryFunction = nullptr; initOptions.ReallocateMemoryFunction = nullptr; initOptions.ReleaseMemoryFunction = nullptr; initOptions.Reserved = nullptr; // TODO: Implement EOS_Android_InitializeOptions for Android initOptions.SystemInitializeOptions = nullptr; initOptions.OverrideThreadAffinity = nullptr; EOS_EResult res = EOS_Initialize(&initOptions); EOS_EResult setCallbackResult = EOS_Logging_SetCallback([](const EOS_LogMessage *logMessage) { Dictionary logMsg; logMsg["category"] = logMessage->Category; logMsg["message"] = logMessage->Message; logMsg["level"] = (int)logMessage->Level; IEOS::get_singleton()->emit_signal("logging_interface_callback", logMsg); }); if (setCallbackResult != EOS_EResult::EOS_Success) { UtilityFunctions::print("[EOSG] Failed to set logging callback: " + String(EOS_EResult_ToString(setCallbackResult))); } return static_cast(res); } Dictionary IEOS::platform_interface_get_desktop_crossplay_status_info() { ERR_FAIL_NULL_V(s_platformInterface, {}); EOS_Platform_GetDesktopCrossplayStatusOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLATFORM_GETDESKTOPCROSSPLAYSTATUS_API_LATEST; EOS_Platform_DesktopCrossplayStatusInfo outDesktopCrossplayStatusInfo; EOS_EResult result = EOS_Platform_GetDesktopCrossplayStatus(s_platformInterface, &options, &outDesktopCrossplayStatusInfo); Dictionary dict; dict["result_code"] = static_cast(result); Dictionary desktopCrossplayStatus = Dictionary(); if (result == EOS_EResult::EOS_Success) { desktopCrossplayStatus["status"] = static_cast(outDesktopCrossplayStatusInfo.Status); desktopCrossplayStatus["service_init_result"] = static_cast(outDesktopCrossplayStatusInfo.ServiceInitResult); } dict["desktop_crossplay_status"] = desktopCrossplayStatus; return dict; } int IEOS::platform_interface_set_application_status(int p_status) { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_EResult result = EOS_Platform_SetApplicationStatus(s_platformInterface, static_cast(p_status)); return static_cast(result); } int IEOS::platform_interface_get_application_status() { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EApplicationStatus::EOS_AS_Foreground)); EOS_EApplicationStatus status = EOS_Platform_GetApplicationStatus(s_platformInterface); return static_cast(status); } int IEOS::platform_interface_set_network_status(int p_status) { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_EResult result = EOS_Platform_SetNetworkStatus(s_platformInterface, static_cast(p_status)); return static_cast(result); } int IEOS::platform_interface_get_network_status() { ERR_FAIL_NULL_V(s_platformInterface, static_cast(EOS_ENetworkStatus::EOS_NS_Online)); EOS_ENetworkStatus status = EOS_Platform_GetNetworkStatus(s_platformInterface); return static_cast(status); } void IEOS::platform_interface_release() { isEOSValid = false; s_achievementsInterface = nullptr; s_authInterface = nullptr; s_connectInterface = nullptr; s_customInvitesInterface = nullptr; s_ecomInterface = nullptr; s_friendsInterface = nullptr; s_kwsInterface = nullptr; s_leaderboardsInterface = nullptr; s_lobbyInterface = nullptr; s_metricsInterface = nullptr; s_modsInterface = nullptr; s_p2pInterface = nullptr; s_playerDataStorageInterface = nullptr; s_presenceInterface = nullptr; s_progressionSnapshotInterface = nullptr; s_reportsInterface = nullptr; s_rtcAudioInterface = nullptr; s_rtcDataInterface = nullptr; s_rtcInterface = nullptr; s_sanctionsInterface = nullptr; s_sessionsInterface = nullptr; s_statsInterface = nullptr; s_titleStorageInterface = nullptr; s_uiInterface = nullptr; s_userInfoInterface = nullptr; s_antiCheatServerInterface = nullptr; s_antiCheatClientInterface = nullptr; if (s_platformInterface != nullptr) { EOS_Platform_Release(s_platformInterface); s_platformInterface = nullptr; } } int IEOS::platform_interface_shutdown() { isEOSValid = false; EOS_EResult res = EOS_Shutdown(); return static_cast(res); } ================================================ FILE: src/playerdatastorage_interface.cpp ================================================ #include "godot_cpp/variant/packed_byte_array.hpp" #include "ieos.h" using namespace godot; void IEOS::playerdatastorage_interface_query_file(Ref p_options) { ERR_FAIL_NULL(s_playerDataStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); EOS_PlayerDataStorage_QueryFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_QUERYFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); p_options->reference(); EOS_PlayerDataStorage_QueryFile(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_QueryFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_query_file_callback", ret); }); } void IEOS::playerdatastorage_interface_query_file_list(Ref p_options) { ERR_FAIL_NULL(s_playerDataStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_PlayerDataStorage_QueryFileListOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_QUERYFILELIST_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_PlayerDataStorage_QueryFileList(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_QueryFileListCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["file_count"] = static_cast(data->FileCount); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_query_file_list_callback", ret); }); } Dictionary IEOS::playerdatastorage_interface_copy_file_metadata_by_filename(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); EOS_PlayerDataStorage_CopyFileMetadataByFilenameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_COPYFILEMETADATABYFILENAME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); EOS_PlayerDataStorage_FileMetadata *outMetadata = nullptr; EOS_EResult res = EOS_PlayerDataStorage_CopyFileMetadataByFilename(s_playerDataStorageInterface, &options, &outMetadata); Dictionary ret; ret["result_code"] = static_cast(res); ret["metadata"] = eosg_playerdatastorage_file_metadata_to_dict_and_release(outMetadata); return ret; } Dictionary IEOS::playerdatastorage_interface_get_file_metadata_count(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_PlayerDataStorage_GetFileMetadataCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_GETFILEMETADATACOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); int32_t outFileMetadataCount = 0; EOS_EResult res = EOS_PlayerDataStorage_GetFileMetadataCount(s_playerDataStorageInterface, &options, &outFileMetadataCount); Dictionary ret; ret["result_code"] = static_cast(res); ret["file_metadata_count"] = outFileMetadataCount; return ret; } Dictionary IEOS::playerdatastorage_interface_copy_file_metadata_at_index(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int index = p_options->get("index"); EOS_PlayerDataStorage_CopyFileMetadataAtIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_COPYFILEMETADATAATINDEX_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Index = static_cast(index); EOS_PlayerDataStorage_FileMetadata *outMetadata = nullptr; EOS_EResult res = EOS_PlayerDataStorage_CopyFileMetadataAtIndex(s_playerDataStorageInterface, &options, &outMetadata); Dictionary ret; ret["result_code"] = static_cast(res); ret["metadata"] = eosg_playerdatastorage_file_metadata_to_dict_and_release(outMetadata); return ret; } void IEOS::playerdatastorage_interface_duplicate_file(Ref p_options) { ERR_FAIL_NULL(s_playerDataStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString source_filename = VARIANT_TO_CHARSTRING(p_options->get("source_filename")); CharString destination_filename = VARIANT_TO_CHARSTRING(p_options->get("destination_filename")); EOS_PlayerDataStorage_DuplicateFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_DUPLICATEFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.SourceFilename = source_filename.get_data(); options.DestinationFilename = destination_filename.get_data(); p_options->reference(); EOS_PlayerDataStorage_DuplicateFile(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_DuplicateFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_duplicate_file_callback", ret); }); } void IEOS::playerdatastorage_interface_delete_file(Ref p_options) { ERR_FAIL_NULL(s_playerDataStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); EOS_PlayerDataStorage_DeleteFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_DELETEFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); p_options->reference(); EOS_PlayerDataStorage_DeleteFile(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_DeleteFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_delete_file_callback", ret); }); } int IEOS::playerdatastorage_interface_delete_cache(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_PlayerDataStorage_DeleteCacheOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_DELETECACHE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_EResult res = EOS_PlayerDataStorage_DeleteCache(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_DeleteCacheCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_delete_cache_callback", ret); }); return static_cast(res); } Variant IEOS::playerdatastorage_interface_read_file(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, Variant()); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); int read_chunk_length_bytes = p_options->get("read_chunk_length_bytes"); EOS_PlayerDataStorage_ReadFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_READFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); options.ReadChunkLengthBytes = static_cast(read_chunk_length_bytes); options.ReadFileDataCallback = [](const EOS_PlayerDataStorage_ReadFileDataCallbackInfo *data) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["total_file_size_bytes"] = static_cast(data->TotalFileSizeBytes); ret["is_last_chunk"] = EOSG_GET_BOOL(data->bIsLastChunk); ret["data_chunk_length_bytes"] = static_cast(data->DataChunkLengthBytes); // Copy data into a PackedByteArray PackedByteArray data_chunk; data_chunk.resize(data->DataChunkLengthBytes); memcpy(data_chunk.ptrw(), data->DataChunk, data->DataChunkLengthBytes); ret["data_chunk"] = data_chunk; IEOS::get_singleton()->emit_signal("playerdatastorage_interface_read_file_data_callback", ret); // TODO: emit the signal on the EOSGFileTransferRequest object instead of IEOS return EOS_PlayerDataStorage_EReadResult::EOS_RR_ContinueReading; }; options.FileTransferProgressCallback = [](const EOS_PlayerDataStorage_FileTransferProgressCallbackInfo *data) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["bytes_transferred"] = static_cast(data->BytesTransferred); ret["total_file_size_bytes"] = static_cast(data->TotalFileSizeBytes); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_file_transfer_progress_callback", ret); // TODO: emit the signal on the EOSGFileTransferRequest object instead of IEOS }; p_options->reference(); EOS_HPlayerDataStorageFileTransferRequest fileTranferRequest = EOS_PlayerDataStorage_ReadFile(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_ReadFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; IEOS::get_singleton()->emit_signal("playerdatastorage_interface_read_file_callback", ret); }); return eosg_playerdatastorage_file_tranfer_request_to_wrapper(fileTranferRequest); } Variant IEOS::playerdatastorage_interface_write_file(Ref p_options) { ERR_FAIL_NULL_V(s_playerDataStorageInterface, Variant()); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); PackedByteArray data = p_options->get("data"); int chunk_length_bytes = p_options->get("chunk_length_bytes"); EOS_PlayerDataStorage_WriteFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PLAYERDATASTORAGE_WRITEFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); options.ChunkLengthBytes = static_cast(chunk_length_bytes); // has the signature options.WriteFileDataCallback = [](const EOS_PlayerDataStorage_WriteFileDataCallbackInfo *data, void *outDataBuffer, uint32_t *bytesWritten) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["data_buffer_length_bytes"] = static_cast(data->DataBufferLengthBytes); int written_buffer_length_bytes = 0; if (client_data->get("written_buffer_length_bytes")) { written_buffer_length_bytes = client_data->get("written_buffer_length_bytes"); } PackedByteArray data_buffer = client_data->get("data"); int chunk_length_bytes = client_data->get("chunk_length_bytes"); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_write_file_data_callback", ret); uint32_t bytes_to_write = std::min((uint32_t)chunk_length_bytes, (uint32_t)(data_buffer.size() - written_buffer_length_bytes)); if (bytes_to_write > 0) { memcpy(outDataBuffer, data_buffer.ptr() + written_buffer_length_bytes, bytes_to_write); *bytesWritten = static_cast(bytes_to_write); written_buffer_length_bytes += bytes_to_write; client_data->set("written_buffer_length_bytes", written_buffer_length_bytes); if (written_buffer_length_bytes == data_buffer.size()) { return EOS_PlayerDataStorage_EWriteResult::EOS_WR_CompleteRequest; } return EOS_PlayerDataStorage_EWriteResult::EOS_WR_ContinueWriting; } return EOS_PlayerDataStorage_EWriteResult::EOS_WR_CompleteRequest; }; options.FileTransferProgressCallback = [](const EOS_PlayerDataStorage_FileTransferProgressCallbackInfo *data) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["bytes_transferred"] = static_cast(data->BytesTransferred); ret["total_file_size_bytes"] = static_cast(data->TotalFileSizeBytes); IEOS::get_singleton()->emit_signal("playerdatastorage_interface_file_transfer_progress_callback", ret); // TODO: emit the signal on the EOSGFileTransferRequest object instead of IEOS }; p_options->reference(); EOS_HPlayerDataStorageFileTransferRequest fileTranferRequest = EOS_PlayerDataStorage_WriteFile(s_playerDataStorageInterface, &options, (void *)*p_options, [](const EOS_PlayerDataStorage_WriteFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; IEOS::get_singleton()->emit_signal("playerdatastorage_interface_write_file_callback", ret); }); return eosg_playerdatastorage_file_tranfer_request_to_wrapper(fileTranferRequest); } ================================================ FILE: src/presence_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::presence_interface_copy_presence(Ref p_options) { ERR_FAIL_NULL_V(s_presenceInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Presence_CopyPresenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_COPYPRESENCE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); EOS_Presence_Info *outPresenceInfo = nullptr; EOS_EResult res = EOS_Presence_CopyPresence(s_presenceInterface, &options, &outPresenceInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["presence_info"] = eosg_presence_presence_info_to_dict_and_release(outPresenceInfo); return ret; } Dictionary IEOS::presence_interface_create_presence_modification(Ref p_options) { ERR_FAIL_NULL_V(s_presenceInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Presence_CreatePresenceModificationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_CREATEPRESENCEMODIFICATION_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); EOS_HPresenceModification outPresenceModification = nullptr; EOS_EResult res = EOS_Presence_CreatePresenceModification(s_presenceInterface, &options, &outPresenceModification); Dictionary ret; ret["result_code"] = static_cast(res); ret["presence_modification"] = eosg_presence_presence_modification_to_wrapper(outPresenceModification); return ret; } Dictionary IEOS::presence_interface_get_join_info(Ref p_options) { ERR_FAIL_NULL_V(s_presenceInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Presence_GetJoinInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_GETJOININFO_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); char *outBuffer = (char *)memalloc(EOS_PRESENCEMODIFICATION_JOININFO_MAX_LENGTH + 1); int outBufferLength = EOS_PRESENCEMODIFICATION_JOININFO_MAX_LENGTH + 1; EOS_EResult res = EOS_Presence_GetJoinInfo(s_presenceInterface, &options, outBuffer, &outBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["join_info"] = outBuffer; return ret; } bool IEOS::presence_interface_has_presence(Ref p_options) { ERR_FAIL_NULL_V(s_presenceInterface, false); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Presence_HasPresenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_HASPRESENCE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); return EOSG_GET_BOOL( EOS_Presence_HasPresence(s_presenceInterface, &options)); } void IEOS::presence_interface_query_presence(Ref p_options) { ERR_FAIL_NULL(s_presenceInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Presence_QueryPresenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_QUERYPRESENCE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_Presence_QueryPresence(s_presenceInterface, &options, (void *)*p_options, [](const EOS_Presence_QueryPresenceCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("presence_interface_query_presence_callback", ret); }); } void IEOS::presence_interface_set_presence(Ref p_options) { ERR_FAIL_NULL(s_presenceInterface); Ref presence_modification = Object::cast_to(p_options->get("presence_modification")); ERR_FAIL_NULL_MSG(presence_modification, "Error setting presence. SetPresenceOptions.presence_modification is null."); ERR_FAIL_NULL_MSG(presence_modification->get_internal(), "Error setting presence. EOSGPresenceModification is null."); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Presence_SetPresenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PRESENCE_SETPRESENCE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.PresenceModificationHandle = presence_modification->get_internal(); p_options->reference(); EOS_Presence_SetPresence(s_presenceInterface, &options, (void *)*p_options, [](const EOS_Presence_SetPresenceCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["rich_presence_result_code"] = static_cast(data->RichPresenceResultCode); IEOS::get_singleton()->emit_signal("presence_interface_set_presence_callback", ret); }); } ================================================ FILE: src/progression_snapshot_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::progression_snapshot_interface_begin_snapshot(Ref p_options) { ERR_FAIL_NULL_V(s_progressionSnapshotInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_ProgressionSnapshot_BeginSnapshotOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PROGRESSIONSNAPSHOT_BEGINSNAPSHOT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); uint32_t outSnapshotId = 0; EOS_EResult res = EOS_ProgressionSnapshot_BeginSnapshot(s_progressionSnapshotInterface, &options, &outSnapshotId); Dictionary ret; ret["result_code"] = static_cast(res); ret["snapshot_id"] = outSnapshotId; return ret; } int IEOS::progression_snapshot_interface_add_progression(Ref p_options) { ERR_FAIL_NULL_V(s_progressionSnapshotInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString key = VARIANT_TO_CHARSTRING(p_options->get("key")); CharString value = VARIANT_TO_CHARSTRING(p_options->get("value")); EOS_ProgressionSnapshot_AddProgressionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PROGRESSIONSNAPSHOT_ADDPROGRESSION_API_LATEST; options.SnapshotId = static_cast(static_cast(p_options->get("snapshot_id"))); options.Key = key.get_data(); options.Value = value.get_data(); return static_cast(EOS_ProgressionSnapshot_AddProgression(s_progressionSnapshotInterface, &options)); } void IEOS::progression_snapshot_interface_submit_snapshot(Ref p_options) { ERR_FAIL_NULL(s_progressionSnapshotInterface); EOS_ProgressionSnapshot_SubmitSnapshotOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PROGRESSIONSNAPSHOT_SUBMITSNAPSHOT_API_LATEST; options.SnapshotId = static_cast(static_cast(p_options->get("snapshot_id"))); p_options->reference(); EOS_ProgressionSnapshot_SubmitSnapshot(s_progressionSnapshotInterface, &options, (void *)*p_options, [](const EOS_ProgressionSnapshot_SubmitSnapshotCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["snapshot_id"] = static_cast(data->SnapshotId); IEOS::get_singleton()->emit_signal("progression_snapshot_interface_submit_snapshot_callback", ret); }); } void IEOS::progression_snapshot_interface_delete_snapshot(Ref p_options) { ERR_FAIL_NULL(s_progressionSnapshotInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_ProgressionSnapshot_DeleteSnapshotOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PROGRESSIONSNAPSHOT_DELETESNAPSHOT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_ProgressionSnapshot_DeleteSnapshot(s_progressionSnapshotInterface, &options, (void *)*p_options, [](const EOS_ProgressionSnapshot_DeleteSnapshotCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("progression_snapshot_interface_delete_snapshot_callback", ret); }); } int IEOS::progression_snapshot_interface_end_snapshot(Ref p_options) { ERR_FAIL_NULL_V(s_progressionSnapshotInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_ProgressionSnapshot_EndSnapshotOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_PROGRESSIONSNAPSHOT_ENDSNAPSHOT_API_LATEST; options.SnapshotId = static_cast(static_cast(p_options->get("snapshot_id"))); return static_cast(EOS_ProgressionSnapshot_EndSnapshot(s_progressionSnapshotInterface, &options)); } ================================================ FILE: src/register_types.cpp ================================================ #include "register_types.h" #include "eosg_active_session.h" #include "eosg_continuance_token.h" #include "eosg_file_transfer_request.h" #include "eosg_lobby_details.h" #include "eosg_lobby_modification.h" #include "eosg_lobby_search.h" #include "eosg_multiplayer_peer.h" #include "eosg_packet_peer_mediator.h" #include "eosg_presence_modification.h" #include "eosg_session_details.h" #include "eosg_session_modification.h" #include "eosg_session_search.h" #include "eosg_transaction.h" #include "godot_cpp/classes/engine.hpp" #include "godot_cpp/godot.hpp" #include "ieos.h" using namespace godot; static IEOS *_ieos; static EOSGPacketPeerMediator *_mediator; void initialize_eosg_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } ClassDB::register_class(); _ieos = memnew(IEOS); Engine::get_singleton()->register_singleton("IEOS", IEOS::get_singleton()); ClassDB::register_class(); _mediator = memnew(EOSGPacketPeerMediator); Engine::get_singleton()->register_singleton("EOSGPacketPeerMediator", EOSGPacketPeerMediator::get_singleton()); ClassDB::register_abstract_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); } void uninitialize_eosg_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } Engine::get_singleton()->unregister_singleton("EOSGPacketPeerMediator"); Engine::get_singleton()->unregister_singleton("IEOS"); memdelete(_mediator); memdelete(_ieos); _mediator = nullptr; _ieos = nullptr; } extern "C" { // Initialization. GDExtensionBool GDE_EXPORT eosg_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); init_obj.register_initializer(initialize_eosg_module); init_obj.register_terminator(uninitialize_eosg_module); init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); return init_obj.init(); } } ================================================ FILE: src/register_types.h ================================================ #pragma once void initialize_eosg_module(); void uninitialize_eosg_module(); ================================================ FILE: src/reports_interface.cpp ================================================ #include "ieos.h" using namespace std; void IEOS::reports_interface_send_player_behavior_report(Ref p_options) { ERR_FAIL_NULL(s_reportsInterface); CharString reporter_user_id = VARIANT_TO_CHARSTRING(p_options->get("reporter_user_id")); CharString reported_user_id = VARIANT_TO_CHARSTRING(p_options->get("reported_user_id")); CharString message = VARIANT_TO_CHARSTRING(p_options->get("message")); CharString context = VARIANT_TO_CHARSTRING(p_options->get("context")); EOS_Reports_SendPlayerBehaviorReportOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_REPORTS_SENDPLAYERBEHAVIORREPORT_API_LATEST; options.ReporterUserId = eosg_string_to_product_user_id(reporter_user_id.get_data()); options.ReportedUserId = eosg_string_to_product_user_id(reported_user_id.get_data()); options.Category = static_cast(static_cast(p_options->get("category"))); if (message.length() != 0) { options.Message = message.get_data(); } if (context.length() != 0) { options.Context = context.get_data(); } p_options->reference(); EOS_Reports_SendPlayerBehaviorReport(s_reportsInterface, &options, (void *)*p_options, [](const EOS_Reports_SendPlayerBehaviorReportCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("reports_interface_send_player_behavior_report_callback", ret); }); } ================================================ FILE: src/rtc_audio_interface.cpp ================================================ #include "godot_cpp/variant/packed_int32_array.hpp" #include "ieos.h" Dictionary IEOS::rtc_audio_interface_copy_input_device_information_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, {}); int device_index = p_options->get("device_index"); EOS_RTCAudio_CopyInputDeviceInformationByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_COPYINPUTDEVICEINFORMATIONBYINDEX_API_LATEST; options.DeviceIndex = static_cast(device_index); EOS_RTCAudio_InputDeviceInformation *outInputDeviceInfo; EOS_EResult res = EOS_RTCAudio_CopyInputDeviceInformationByIndex(s_rtcAudioInterface, &options, &outInputDeviceInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["input_device_information"] = eosg_rtc_audio_input_device_information_to_dict_and_release(outInputDeviceInfo); return ret; } Dictionary IEOS::rtc_audio_interface_copy_output_device_information_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, {}); int device_index = p_options->get("device_index"); EOS_RTCAudio_CopyOutputDeviceInformationByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_COPYOUTPUTDEVICEINFORMATIONBYINDEX_API_LATEST; options.DeviceIndex = static_cast(device_index); EOS_RTCAudio_OutputDeviceInformation *outOutputDeviceInfo; EOS_EResult res = EOS_RTCAudio_CopyOutputDeviceInformationByIndex(s_rtcAudioInterface, &options, &outOutputDeviceInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["output_device_information"] = eosg_rtc_audio_output_device_information_to_dict_and_release(outOutputDeviceInfo); return ret; } int IEOS::rtc_audio_interface_get_input_devices_count(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, 0); EOS_RTCAudio_GetInputDevicesCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_GETINPUTDEVICESCOUNT_API_LATEST; return static_cast(EOS_RTCAudio_GetInputDevicesCount(s_rtcAudioInterface, &options)); } int IEOS::rtc_audio_interface_get_output_devices_count(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, 0); EOS_RTCAudio_GetOutputDevicesCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_GETOUTPUTDEVICESCOUNT_API_LATEST; return static_cast(EOS_RTCAudio_GetOutputDevicesCount(s_rtcAudioInterface, &options)); } int IEOS::rtc_audio_interface_send_audio(const Ref &p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_EResult::EOS_InvalidParameters)); const CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); const CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); // Extract buffer data from options const int sample_rate = p_options->get("sample_rate"); const int channels = p_options->get("channels"); PackedInt32Array frames_array = p_options->get("frames"); const int frames_count = frames_array.size(); // Allocate and copy frame data int16_t *frames_data = nullptr; if (frames_count > 0) { frames_data = (int16_t *)memalloc(sizeof(int16_t) * frames_count); for (int i = 0; i < frames_count; i++) { frames_data[i] = static_cast(static_cast(frames_array[i])); } } // Setup audio buffer EOS_RTCAudio_AudioBuffer audio_buffer; memset(&audio_buffer, 0, sizeof(audio_buffer)); audio_buffer.ApiVersion = EOS_RTCAUDIO_AUDIOBUFFER_API_LATEST; audio_buffer.Frames = frames_data; audio_buffer.FramesCount = static_cast(frames_count); audio_buffer.SampleRate = static_cast(sample_rate); audio_buffer.Channels = static_cast(channels); // Setup send audio options EOS_RTCAudio_SendAudioOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_SENDAUDIO_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.Buffer = &audio_buffer; EOS_EResult result = EOS_RTCAudio_SendAudio(s_rtcAudioInterface, &options); // Free allocated memory if (frames_data != nullptr) { memfree(frames_data); } return static_cast(result); } void IEOS::rtc_audio_interface_query_input_devices_information(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_QueryInputDevicesInformationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_QUERYINPUTDEVICESINFORMATION_API_LATEST; p_options->reference(); EOS_RTCAudio_QueryInputDevicesInformation(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnQueryInputDevicesInformationCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); IEOS::get_singleton()->emit_signal("rtc_audio_interface_query_input_devices_information_callback", ret); }); } void IEOS::rtc_audio_interface_query_output_devices_information(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_QueryOutputDevicesInformationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_QUERYOUTPUTDEVICESINFORMATION_API_LATEST; p_options->reference(); EOS_RTCAudio_QueryOutputDevicesInformation(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnQueryOutputDevicesInformationCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); IEOS::get_singleton()->emit_signal("rtc_audio_interface_query_output_devices_information_callback", ret); }); } void IEOS::rtc_audio_interface_register_platform_user(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString platform_user_id = VARIANT_TO_CHARSTRING(p_options->get("platform_user_id")); EOS_RTCAudio_RegisterPlatformUserOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_REGISTERPLATFORMUSER_API_LATEST; options.PlatformUserId = platform_user_id.get_data(); p_options->reference(); EOS_RTCAudio_RegisterPlatformUser(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnRegisterPlatformUserCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["platform_user_id"] = EOSG_GET_STRING(data->PlatformUserId); IEOS::get_singleton()->emit_signal("rtc_audio_interface_register_platform_user_callback", ret); }); } void IEOS::rtc_audio_interface_set_input_device_settings(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString real_device_id = VARIANT_TO_CHARSTRING(p_options->get("real_device_id")); bool platform_aec = p_options->get("platform_aec"); EOS_RTCAudio_SetInputDeviceSettingsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_SETINPUTDEVICESETTINGS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RealDeviceId = real_device_id.get_data(); options.bPlatformAEC = platform_aec ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTCAudio_SetInputDeviceSettings(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnSetInputDeviceSettingsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["real_device_id"] = EOSG_GET_STRING(data->RealDeviceId); IEOS::get_singleton()->emit_signal("rtc_audio_interface_set_input_device_settings_callback", ret); }); } void IEOS::rtc_audio_interface_set_output_device_settings(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString real_device_id = VARIANT_TO_CHARSTRING(p_options->get("real_device_id")); EOS_RTCAudio_SetOutputDeviceSettingsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_SETOUTPUTDEVICESETTINGS_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RealDeviceId = real_device_id.get_data(); p_options->reference(); EOS_RTCAudio_SetOutputDeviceSettings(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnSetOutputDeviceSettingsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["real_device_id"] = EOSG_GET_STRING(data->RealDeviceId); IEOS::get_singleton()->emit_signal("rtc_audio_interface_set_output_device_settings_callback", ret); }); } void IEOS::rtc_audio_interface_unregister_platform_user(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString platform_user_id = VARIANT_TO_CHARSTRING(p_options->get("platform_user_id")); EOS_RTCAudio_UnregisterPlatformUserOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UNREGISTERPLATFORMUSER_API_LATEST; options.PlatformUserId = platform_user_id.get_data(); p_options->reference(); EOS_RTCAudio_UnregisterPlatformUser(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_OnUnregisterPlatformUserCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["platform_user_id"] = EOSG_GET_STRING(data->PlatformUserId); IEOS::get_singleton()->emit_signal("rtc_audio_interface_unregister_platform_user_callback", ret); }); } void IEOS::rtc_audio_interface_update_participant_volume(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString participant_id = VARIANT_TO_CHARSTRING(p_options->get("participant_id")); float volume = p_options->get("volume"); EOS_RTCAudio_UpdateParticipantVolumeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UPDATEPARTICIPANTVOLUME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.ParticipantId = eosg_string_to_product_user_id(participant_id.get_data()); options.Volume = volume; p_options->reference(); EOS_RTCAudio_UpdateParticipantVolume(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_UpdateParticipantVolumeCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["volume"] = data->Volume; IEOS::get_singleton()->emit_signal("rtc_audio_interface_update_participant_volume_callback", ret); }); } void IEOS::rtc_audio_interface_update_receiving_volume(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); float volume = p_options->get("volume"); EOS_RTCAudio_UpdateReceivingVolumeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UPDATERECEIVINGVOLUME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.Volume = volume; p_options->reference(); EOS_RTCAudio_UpdateReceivingVolume(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_UpdateReceivingVolumeCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["volume"] = data->Volume; IEOS::get_singleton()->emit_signal("rtc_audio_interface_update_receiving_volume_callback", ret); }); } void IEOS::rtc_audio_interface_update_receiving(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString participant_id = VARIANT_TO_CHARSTRING(p_options->get("participant_id")); bool audio_enabled = p_options->get("audio_enabled"); EOS_RTCAudio_UpdateReceivingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UPDATERECEIVING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.ParticipantId = eosg_string_to_product_user_id(participant_id.get_data()); options.bAudioEnabled = audio_enabled ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTCAudio_UpdateReceiving(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_UpdateReceivingCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["audio_enabled"] = data->bAudioEnabled == EOS_TRUE; IEOS::get_singleton()->emit_signal("rtc_audio_interface_update_receiving_callback", ret); }); } void IEOS::rtc_audio_interface_update_sending_volume(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); float volume = p_options->get("volume"); EOS_RTCAudio_UpdateSendingVolumeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UPDATESENDINGVOLUME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.Volume = volume; p_options->reference(); EOS_RTCAudio_UpdateSendingVolume(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_UpdateSendingVolumeCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["volume"] = data->Volume; IEOS::get_singleton()->emit_signal("rtc_audio_interface_update_sending_volume_callback", ret); }); } void IEOS::rtc_audio_interface_update_sending(Ref p_options) { ERR_FAIL_NULL(s_rtcAudioInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); int audio_status = p_options->get("audio_status"); EOS_RTCAudio_UpdateSendingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_UPDATESENDING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.AudioStatus = static_cast(audio_status); p_options->reference(); EOS_RTCAudio_UpdateSending(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_UpdateSendingCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["audio_status"] = static_cast(data->AudioStatus); IEOS::get_singleton()->emit_signal("rtc_audio_interface_update_sending_callback", ret); }); } int IEOS::rtc_audio_interface_add_notify_audio_before_render(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); bool unmixed_audio = p_options->get("unmixed_audio"); EOS_RTCAudio_AddNotifyAudioBeforeRenderOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYAUDIOBEFORERENDER_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.bUnmixedAudio = unmixed_audio ? EOS_TRUE : EOS_FALSE; EOS_NotificationId notification_id = EOS_RTCAudio_AddNotifyAudioBeforeRender(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_AudioBeforeRenderCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["buffer"] = eosg_rtc_audio_audio_buffer_to_dict(data->Buffer); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); IEOS::get_singleton()->emit_signal("rtc_audio_audio_before_render", ret); }); return static_cast(notification_id); } int IEOS::rtc_audio_interface_add_notify_audio_before_send(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCAudio_AddNotifyAudioBeforeSendOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYAUDIOBEFORESEND_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); EOS_NotificationId notification_id = EOS_RTCAudio_AddNotifyAudioBeforeSend(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_AudioBeforeSendCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["buffer"] = eosg_rtc_audio_audio_buffer_to_dict(data->Buffer); IEOS::get_singleton()->emit_signal("rtc_audio_audio_before_send", ret); }); return static_cast(notification_id); } int IEOS::rtc_audio_interface_add_notify_audio_input_state(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCAudio_AddNotifyAudioInputStateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYAUDIOINPUTSTATE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); EOS_NotificationId notification_id = EOS_RTCAudio_AddNotifyAudioInputState(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_AudioInputStateCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["status"] = static_cast(data->Status); IEOS::get_singleton()->emit_signal("rtc_audio_audio_input_state", ret); }); return static_cast(notification_id); } int IEOS::rtc_audio_interface_add_notify_audio_output_state(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCAudio_AddNotifyAudioOutputStateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYAUDIOOUTPUTSTATE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); EOS_NotificationId notification_id = EOS_RTCAudio_AddNotifyAudioOutputState(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_AudioOutputStateCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["status"] = static_cast(data->Status); IEOS::get_singleton()->emit_signal("rtc_audio_audio_output_state", ret); }); return static_cast(notification_id); } int IEOS::rtc_audio_interface_add_notify_participant_updated(Ref p_options) { ERR_FAIL_NULL_V(s_rtcAudioInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCAudio_AddNotifyParticipantUpdatedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCAUDIO_ADDNOTIFYPARTICIPANTUPDATED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); EOS_NotificationId notification_id = EOS_RTCAudio_AddNotifyParticipantUpdated(s_rtcAudioInterface, &options, (void *)*p_options, [](const EOS_RTCAudio_ParticipantUpdatedCallbackInfo *data) { Dictionary ret; ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["speaking"] = EOSG_GET_BOOL(data->bSpeaking); ret["audio_status"] = static_cast(data->AudioStatus); IEOS::get_singleton()->emit_signal("rtc_audio_participant_updated", ret); }); return static_cast(notification_id); } void IEOS::rtc_audio_interface_remove_notify_audio_before_render(int p_notification_id) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_RemoveNotifyAudioBeforeRender(s_rtcAudioInterface, static_cast(p_notification_id)); } void IEOS::rtc_audio_interface_remove_notify_audio_before_send(int p_notification_id) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_RemoveNotifyAudioBeforeSend(s_rtcAudioInterface, static_cast(p_notification_id)); } void IEOS::rtc_audio_interface_remove_notify_audio_input_state(int p_notification_id) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_RemoveNotifyAudioInputState(s_rtcAudioInterface, static_cast(p_notification_id)); } void IEOS::rtc_audio_interface_remove_notify_audio_output_state(int p_notification_id) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_RemoveNotifyAudioOutputState(s_rtcAudioInterface, static_cast(p_notification_id)); } void IEOS::rtc_audio_interface_remove_notify_participant_updated(int p_notification_id) { ERR_FAIL_NULL(s_rtcAudioInterface); EOS_RTCAudio_RemoveNotifyParticipantUpdated(s_rtcAudioInterface, static_cast(p_notification_id)); } ================================================ FILE: src/rtc_data_interface.cpp ================================================ #include "ieos.h" int IEOS::rtc_data_interface_add_notify_data_received(Ref p_options) { ERR_FAIL_NULL_V(s_rtcDataInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCData_AddNotifyDataReceivedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCDATA_ADDNOTIFYDATARECEIVED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); return static_cast(EOS_RTCData_AddNotifyDataReceived(s_rtcDataInterface, &options, (void *)*p_options, [](const EOS_RTCData_DataReceivedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["data_length_bytes"] = data->DataLengthBytes; PackedByteArray data_arr; data_arr.resize(data->DataLengthBytes); memcpy(data_arr.ptrw(), data->Data, data->DataLengthBytes); ret["data"] = data_arr; IEOS::get_singleton()->emit_signal("rtc_data_data_received", ret); })); } int IEOS::rtc_data_interface_add_notify_participant_updated(Ref p_options) { ERR_FAIL_NULL_V(s_rtcDataInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTCData_AddNotifyParticipantUpdatedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCDATA_ADDNOTIFYPARTICIPANTUPDATED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); return static_cast(EOS_RTCData_AddNotifyParticipantUpdated(s_rtcDataInterface, &options, (void *)*p_options, [](const EOS_RTCData_ParticipantUpdatedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["data_status"] = static_cast(data->DataStatus); IEOS::get_singleton()->emit_signal("rtc_data_participant_updated", ret); })); } int IEOS::rtc_data_interface_send_data(Ref p_options) { ERR_FAIL_NULL_V(s_rtcDataInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); PackedByteArray p_data = p_options->get("data"); EOS_RTCData_SendDataOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCDATA_SENDDATA_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.Data = p_data.ptr(); options.DataLengthBytes = p_data.size(); return static_cast(EOS_RTCData_SendData(s_rtcDataInterface, &options)); } void IEOS::rtc_data_interface_remove_notify_data_received(int p_notification_id) { ERR_FAIL_NULL(s_rtcDataInterface); EOS_RTCData_RemoveNotifyDataReceived(s_rtcDataInterface, static_cast(p_notification_id)); } void IEOS::rtc_data_interface_remove_notify_participant_updated(int p_notification_id) { ERR_FAIL_NULL(s_rtcDataInterface); EOS_RTCData_RemoveNotifyParticipantUpdated(s_rtcDataInterface, static_cast(p_notification_id)); } void IEOS::rtc_data_interface_update_receiving(Ref p_options) { ERR_FAIL_NULL(s_rtcDataInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString participant_id = VARIANT_TO_CHARSTRING(p_options->get("participant_id")); bool data_enabled = p_options->get("data_enabled"); EOS_RTCData_UpdateReceivingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCDATA_UPDATERECEIVING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.ParticipantId = eosg_string_to_product_user_id(participant_id.get_data()); options.bDataEnabled = data_enabled ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTCData_UpdateReceiving(s_rtcDataInterface, &options, (void *)*p_options, [](const EOS_RTCData_UpdateReceivingCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["data_enabled"] = EOSG_GET_BOOL(data->bDataEnabled); IEOS::get_singleton()->emit_signal("rtc_data_interface_update_receiving_callback", ret); }); } void IEOS::rtc_data_interface_update_sending(Ref p_options) { ERR_FAIL_NULL(s_rtcDataInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); bool data_enabled = p_options->get("data_enabled"); EOS_RTCData_UpdateSendingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTCDATA_UPDATESENDING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.bDataEnabled = data_enabled ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTCData_UpdateSending(s_rtcDataInterface, &options, (void *)*p_options, [](const EOS_RTCData_UpdateSendingCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["data_enabled"] = EOSG_GET_BOOL(data->bDataEnabled); IEOS::get_singleton()->emit_signal("rtc_data_interface_update_sending_callback", ret); }); } ================================================ FILE: src/rtc_interface.cpp ================================================ #include "ieos.h" void IEOS::rtc_interface_join_room(Ref p_options) { ERR_FAIL_NULL(s_rtcInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString client_base_url = VARIANT_TO_CHARSTRING(p_options->get("client_base_url")); CharString participant_token = VARIANT_TO_CHARSTRING(p_options->get("participant_token")); CharString participant_id = VARIANT_TO_CHARSTRING(p_options->get("participant_id")); int flags = p_options->get("flags"); bool manual_audio_input_enabled = p_options->get("manual_audio_input_enabled"); bool manual_audio_output_enabled = p_options->get("manual_audio_output_enabled"); EOS_RTC_JoinRoomOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_JOINROOM_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.ClientBaseUrl = client_base_url.get_data(); options.ParticipantToken = participant_token.get_data(); options.ParticipantId = eosg_string_to_product_user_id(participant_id.get_data()); options.Flags = static_cast(flags); options.bManualAudioInputEnabled = manual_audio_input_enabled ? EOS_TRUE : EOS_FALSE; options.bManualAudioOutputEnabled = manual_audio_output_enabled ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTC_JoinRoom(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_JoinRoomCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); Array room_options = Array(); if (data->RoomOptionsCount > 0) { for (int i = 0; i < data->RoomOptionsCount; i++) { Dictionary room_option; room_option["key"] = EOSG_GET_STRING(data->RoomOptions[i].Key); room_option["value"] = EOSG_GET_STRING(data->RoomOptions[i].Value); room_options.append(room_option); } } ret["room_options"] = room_options; IEOS::get_singleton()->emit_signal("rtc_interface_join_room_callback", ret); }); } void IEOS::rtc_interface_leave_room(Ref p_options) { ERR_FAIL_NULL(s_rtcInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTC_LeaveRoomOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_LEAVEROOM_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); p_options->reference(); EOS_RTC_LeaveRoom(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_LeaveRoomCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); IEOS::get_singleton()->emit_signal("rtc_interface_leave_room_callback", ret); }); } void IEOS::rtc_interface_block_participant(Ref p_options) { ERR_FAIL_NULL(s_rtcInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString participant_id = VARIANT_TO_CHARSTRING(p_options->get("participant_id")); bool blocked = p_options->get("blocked"); EOS_RTC_BlockParticipantOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_BLOCKPARTICIPANT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); options.ParticipantId = eosg_string_to_product_user_id(participant_id.get_data()); options.bBlocked = blocked ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_RTC_BlockParticipant(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_BlockParticipantCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["blocked"] = EOSG_GET_BOOL(data->bBlocked); IEOS::get_singleton()->emit_signal("rtc_interface_block_participant_callback", ret); }); } int IEOS::rtc_interface_set_setting(Ref p_options) { ERR_FAIL_NULL_V(s_rtcInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString setting_name = VARIANT_TO_CHARSTRING(p_options->get("setting_name")); CharString setting_value = VARIANT_TO_CHARSTRING(p_options->get("setting_value")); EOS_RTC_SetSettingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_SETSETTING_API_LATEST; options.SettingName = setting_name.get_data(); options.SettingValue = setting_value.get_data(); return static_cast(EOS_RTC_SetSetting(s_rtcInterface, &options)); } int IEOS::rtc_interface_set_room_setting(Ref p_options) { ERR_FAIL_NULL_V(s_rtcInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString lcoal_user_id = VARIANT_TO_CHARSTRING(p_options->get("lcoal_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); CharString setting_name = VARIANT_TO_CHARSTRING(p_options->get("setting_name")); CharString setting_value = VARIANT_TO_CHARSTRING(p_options->get("setting_value")); EOS_RTC_SetRoomSettingOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_SETROOMSETTING_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(lcoal_user_id.get_data()); options.RoomName = room_name.get_data(); options.SettingName = setting_name.get_data(); options.SettingValue = setting_value.get_data(); return static_cast(EOS_RTC_SetRoomSetting(s_rtcInterface, &options)); } int IEOS::rtc_interface_add_notify_disconnected(Ref p_options) { ERR_FAIL_NULL_V(s_rtcInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTC_AddNotifyDisconnectedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_ADDNOTIFYDISCONNECTED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); return static_cast(EOS_RTC_AddNotifyDisconnected(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_DisconnectedCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); IEOS::get_singleton()->emit_signal("rtc_interface_disconnected", ret); })); } int IEOS::rtc_interface_add_notify_participant_status_changed(Ref p_options) { ERR_FAIL_NULL_V(s_rtcInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTC_AddNotifyParticipantStatusChangedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_ADDNOTIFYPARTICIPANTSTATUSCHANGED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); return static_cast(EOS_RTC_AddNotifyParticipantStatusChanged(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_ParticipantStatusChangedCallbackInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["participant_id"] = eosg_product_user_id_to_string(data->ParticipantId); ret["participant_status"] = static_cast(data->ParticipantStatus); ret["participant_in_blocklist"] = EOSG_GET_BOOL(data->bParticipantInBlocklist); Array participant_metadata = Array(); if (data->ParticipantMetadataCount > 0) { for (int i = 0; i < data->ParticipantMetadataCount; i++) { Dictionary metadata; metadata["key"] = EOSG_GET_STRING(data->ParticipantMetadata[i].Key); metadata["value"] = EOSG_GET_STRING(data->ParticipantMetadata[i].Value); participant_metadata.append(metadata); } } ret["participant_metadata"] = participant_metadata; IEOS::get_singleton()->emit_signal("rtc_interface_participant_status_changed", ret); })); } int IEOS::rtc_interface_add_notify_room_statistics_updated(Ref p_options) { ERR_FAIL_NULL_V(s_rtcInterface, static_cast(EOS_INVALID_NOTIFICATIONID)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString room_name = VARIANT_TO_CHARSTRING(p_options->get("room_name")); EOS_RTC_AddNotifyRoomStatisticsUpdatedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_RTC_ADDNOTIFYROOMSTATISTICSUPDATED_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.RoomName = room_name.get_data(); return static_cast(EOS_RTC_AddNotifyRoomStatisticsUpdated(s_rtcInterface, &options, (void *)*p_options, [](const EOS_RTC_RoomStatisticsUpdatedInfo *data) { Dictionary ret; ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["room_name"] = EOSG_GET_STRING(data->RoomName); ret["statistic"] = EOSG_GET_STRING(data->Statistic); IEOS::get_singleton()->emit_signal("rtc_interface_room_statistics_updated", ret); })); } void IEOS::rtc_interface_remove_notify_disconnected(int p_notification_id) { ERR_FAIL_NULL(s_rtcInterface); EOS_RTC_RemoveNotifyDisconnected(s_rtcInterface, static_cast(p_notification_id)); } void IEOS::rtc_interface_remove_notify_participant_status_changed(int p_notification_id) { ERR_FAIL_NULL(s_rtcInterface); EOS_RTC_RemoveNotifyParticipantStatusChanged(s_rtcInterface, static_cast(p_notification_id)); } void IEOS::rtc_interface_remove_notify_room_statistics_updated(int p_notification_id) { ERR_FAIL_NULL(s_rtcInterface); EOS_RTC_RemoveNotifyRoomStatisticsUpdated(s_rtcInterface, static_cast(p_notification_id)); } ================================================ FILE: src/sanctions_interface.cpp ================================================ #include "godot_cpp/variant/packed_byte_array.hpp" #include "ieos.h" using namespace godot; void IEOS::sanctions_interface_query_active_player_sanctions(Ref p_options) { ERR_FAIL_NULL(s_sanctionsInterface); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Sanctions_QueryActivePlayerSanctionsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SANCTIONS_QUERYACTIVEPLAYERSANCTIONS_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); EOS_Sanctions_QueryActivePlayerSanctions(s_sanctionsInterface, &options, (void *)*p_options, [](const EOS_Sanctions_QueryActivePlayerSanctionsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("sanctions_interface_query_active_player_sanctions_callback", ret); }); } int IEOS::sanctions_interface_get_player_sanction_count(Ref p_options) { ERR_FAIL_NULL_V(s_sanctionsInterface, 0); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Sanctions_GetPlayerSanctionCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SANCTIONS_GETPLAYERSANCTIONCOUNT_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); return static_cast(EOS_Sanctions_GetPlayerSanctionCount(s_sanctionsInterface, &options)); } Dictionary IEOS::sanctions_interface_copy_player_sanction_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_sanctionsInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); int sanction_index = p_options->get("sanction_index"); EOS_Sanctions_CopyPlayerSanctionByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SANCTIONS_COPYPLAYERSANCTIONBYINDEX_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.SanctionIndex = sanction_index; EOS_Sanctions_PlayerSanction *outSanction = nullptr; EOS_EResult result = EOS_Sanctions_CopyPlayerSanctionByIndex(s_sanctionsInterface, &options, &outSanction); Dictionary ret; ret["result_code"] = static_cast(result); ret["sanction"] = eosg_sanctions_player_sanction_to_dict_and_release(outSanction); return ret; } void IEOS::sanctions_interface_create_player_sanction_appeal(Ref p_options) { ERR_FAIL_NULL(s_sanctionsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString reference_id = VARIANT_TO_CHARSTRING(p_options->get("reference_id")); int reason = p_options->get("reason"); EOS_Sanctions_CreatePlayerSanctionAppealOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SANCTIONS_CREATEPLAYERSANCTIONAPPEAL_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.ReferenceId = reference_id.get_data(); options.Reason = static_cast(reason); EOS_Sanctions_CreatePlayerSanctionAppeal(s_sanctionsInterface, &options, (void *)*p_options, [](const EOS_Sanctions_CreatePlayerSanctionAppealCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["reference_id"] = EOSG_GET_STRING(data->ReferenceId); IEOS::get_singleton()->emit_signal("sanctions_interface_create_player_sanction_appeal_callback", ret); }); } ================================================ FILE: src/sessions_interface.cpp ================================================ #include "ieos.h" Dictionary IEOS::sessions_interface_copy_active_session_details(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_CopyActiveSessionHandleOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_COPYACTIVESESSIONHANDLE_API_LATEST; options.SessionName = session_name.get_data(); EOS_HActiveSession outActiveSession = nullptr; EOS_EResult res = EOS_Sessions_CopyActiveSessionHandle(s_sessionsInterface, &options, &outActiveSession); Dictionary ret; ret["result_code"] = static_cast(res); ret["active_session"] = eosg_sessions_active_session_to_wrapper(outActiveSession); return ret; } Dictionary IEOS::sessions_interface_copy_session_details_by_invite_id(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); CharString invite_id = VARIANT_TO_CHARSTRING(p_options->get("invite_id")); EOS_Sessions_CopySessionHandleByInviteIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_COPYSESSIONHANDLEBYINVITEID_API_LATEST; options.InviteId = invite_id.get_data(); EOS_HSessionDetails outSessionHandle = nullptr; EOS_EResult res = EOS_Sessions_CopySessionHandleByInviteId(s_sessionsInterface, &options, &outSessionHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_details"] = eosg_sessions_session_details_to_wrapper(outSessionHandle); return ret; } Dictionary IEOS::sessions_interface_copy_session_details_by_ui_event_id(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); int ui_event_id = p_options->get("ui_event_id"); EOS_Sessions_CopySessionHandleByUiEventIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_COPYSESSIONHANDLEBYUIEVENTID_API_LATEST; options.UiEventId = static_cast(ui_event_id); EOS_HSessionDetails outSessionHandle = nullptr; EOS_EResult res = EOS_Sessions_CopySessionHandleByUiEventId(s_sessionsInterface, &options, &outSessionHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_details"] = eosg_sessions_session_details_to_wrapper(outSessionHandle); return ret; } Dictionary IEOS::sessions_interface_copy_session_details_for_presence(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Sessions_CopySessionHandleForPresenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_COPYSESSIONHANDLEFORPRESENCE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); EOS_HSessionDetails outSessionHandle = nullptr; EOS_EResult res = EOS_Sessions_CopySessionHandleForPresence(s_sessionsInterface, &options, &outSessionHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_details"] = eosg_sessions_session_details_to_wrapper(outSessionHandle); return ret; } Dictionary IEOS::sessions_interface_create_session_modification(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); CharString bucket_id = VARIANT_TO_CHARSTRING(p_options->get("bucket_id")); int max_players = p_options->get("max_players"); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); bool presence_enabled = p_options->get("presence_enabled"); CharString session_id = VARIANT_TO_CHARSTRING(p_options->get("session_id")); bool sanctions_enabled = p_options->get("sanctions_enabled"); Array allowed_platform_ids = p_options->get("allowed_platform_ids"); EOS_Sessions_CreateSessionModificationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_CREATESESSIONMODIFICATION_API_LATEST; options.SessionName = session_name.get_data(); options.BucketId = bucket_id.get_data(); options.MaxPlayers = static_cast(max_players); if (local_user_id.length() > 0) { options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); } options.bPresenceEnabled = presence_enabled ? EOS_TRUE : EOS_FALSE; options.SessionId = session_id.get_data(); options.bSanctionsEnabled = sanctions_enabled ? EOS_TRUE : EOS_FALSE; options.AllowedPlatformIdsCount = allowed_platform_ids.size(); if (allowed_platform_ids.size() > 0) { uint32_t *allowed_platform_ids_array = (uint32_t *)malloc(sizeof(uint32_t) * allowed_platform_ids.size()); for (int i = 0; i < allowed_platform_ids.size(); i++) { allowed_platform_ids_array[i] = allowed_platform_ids[i]; } options.AllowedPlatformIds = allowed_platform_ids_array; } EOS_HSessionModification outSessionModificationHandle = nullptr; EOS_EResult res = EOS_Sessions_CreateSessionModification(s_sessionsInterface, &options, &outSessionModificationHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_modification"] = eosg_sessions_session_modification_to_wrapper(outSessionModificationHandle); return ret; } Dictionary IEOS::sessions_interface_create_session_search(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); int max_search_results = p_options->get("max_search_results"); EOS_Sessions_CreateSessionSearchOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_CREATESESSIONSEARCH_API_LATEST; options.MaxSearchResults = static_cast(max_search_results); EOS_HSessionSearch outSessionSearchHandle = nullptr; EOS_EResult res = EOS_Sessions_CreateSessionSearch(s_sessionsInterface, &options, &outSessionSearchHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_search"] = eosg_sessions_session_search_to_wrapper(outSessionSearchHandle); return ret; } int IEOS::sessions_interface_dump_session_state(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_DumpSessionStateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_DUMPSESSIONSTATE_API_LATEST; options.SessionName = session_name.get_data(); return static_cast(EOS_Sessions_DumpSessionState(s_sessionsInterface, &options)); } Dictionary IEOS::sessions_interface_get_invite_id_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); int index = p_options->get("index"); EOS_Sessions_GetInviteIdByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_GETINVITEIDBYINDEX_API_LATEST; options.Index = static_cast(index); char *outBuffer = (char *)memalloc(sizeof(char) * (EOS_SESSIONS_INVITEID_MAX_LENGTH + 1)); int32_t outBufferLength = 0; EOS_EResult res = EOS_Sessions_GetInviteIdByIndex(s_sessionsInterface, &options, outBuffer, &outBufferLength); Dictionary ret; ret["result_code"] = static_cast(res); ret["invite_id"] = String(outBuffer); return ret; } int IEOS::sessions_interface_is_user_in_session(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Sessions_IsUserInSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_ISUSERINSESSION_API_LATEST; options.SessionName = session_name.get_data(); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); EOS_EResult res = EOS_Sessions_IsUserInSession(s_sessionsInterface, &options); return static_cast(res); } Dictionary IEOS::sessions_interface_update_session_modification(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, {}); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_UpdateSessionModificationOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_UPDATESESSIONMODIFICATION_API_LATEST; options.SessionName = session_name.get_data(); EOS_HSessionModification outSessionModificationHandle = nullptr; EOS_EResult res = EOS_Sessions_UpdateSessionModification(s_sessionsInterface, &options, &outSessionModificationHandle); Dictionary ret; ret["result_code"] = static_cast(res); ret["session_modification"] = eosg_sessions_session_modification_to_wrapper(outSessionModificationHandle); return ret; } int IEOS::sessions_interface_get_invite_count(Ref p_options) { ERR_FAIL_NULL_V(s_sessionsInterface, 0); EOS_Sessions_GetInviteCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_GETINVITECOUNT_API_LATEST; return static_cast(EOS_Sessions_GetInviteCount(s_sessionsInterface, &options)); } void IEOS::sessions_interface_destroy_session(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_DestroySessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_DESTROYSESSION_API_LATEST; options.SessionName = session_name.get_data(); p_options->reference(); EOS_Sessions_DestroySession(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_DestroySessionCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_destroy_session_callback", ret); }); } void IEOS::sessions_interface_end_session(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_EndSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_ENDSESSION_API_LATEST; options.SessionName = session_name.get_data(); p_options->reference(); EOS_Sessions_EndSession(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_EndSessionCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_end_session_callback", ret); }); } void IEOS::sessions_interface_join_session(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); Ref session_details = Object::cast_to(p_options->get("session_details")); ERR_FAIL_NULL_MSG(session_details, "Error joining session. JoinSessionOptions.session_details is null."); ERR_FAIL_NULL_MSG(session_details->get_internal(), "Error joining session. EOSGSessionDetails is null."); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); bool presence_enabled = p_options->get("presence_enabled"); EOS_Sessions_JoinSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_JOINSESSION_API_LATEST; options.SessionName = session_name.get_data(); options.SessionHandle = session_details->get_internal(); options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.bPresenceEnabled = presence_enabled ? EOS_TRUE : EOS_FALSE; p_options->reference(); EOS_Sessions_JoinSession(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_JoinSessionCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_join_session_callback", ret); }); } void IEOS::sessions_interface_query_invites(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_Sessions_QueryInvitesOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_QUERYINVITES_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_Sessions_QueryInvites(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_QueryInvitesCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("sessions_interface_query_invites_callback", ret); }); } void IEOS::sessions_interface_register_players(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); TypedArray players_to_register = p_options->get("players_to_register"); EOS_Sessions_RegisterPlayersOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_REGISTERPLAYERS_API_LATEST; options.SessionName = session_name.get_data(); options.PlayersToRegisterCount = players_to_register.size(); EOS_ProductUserId *players_to_register_array = (EOS_ProductUserId *)malloc(sizeof(EOS_ProductUserId) * players_to_register.size()); for (int i = 0; i < players_to_register.size(); i++) { players_to_register_array[i] = eosg_string_to_product_user_id(VARIANT_TO_CHARSTRING(players_to_register[i]).get_data()); } options.PlayersToRegister = players_to_register_array; p_options->reference(); EOS_Sessions_RegisterPlayers(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_RegisterPlayersCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); Array registered_players_array = Array(); for (int i = 0; i < data->RegisteredPlayersCount; i++) { registered_players_array.append(eosg_product_user_id_to_string(data->RegisteredPlayers[i])); } ret["registered_players"] = registered_players_array; Array sanctioned_players_array = Array(); for (int i = 0; i < data->SanctionedPlayersCount; i++) { sanctioned_players_array.append(eosg_product_user_id_to_string(data->SanctionedPlayers[i])); } ret["sanctioned_players"] = sanctioned_players_array; IEOS::get_singleton()->emit_signal("sessions_interface_register_players_callback", ret); }); } void IEOS::sessions_interface_reject_invite(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString invite_id = VARIANT_TO_CHARSTRING(p_options->get("invite_id")); EOS_Sessions_RejectInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_REJECTINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.InviteId = invite_id.get_data(); p_options->reference(); EOS_Sessions_RejectInvite(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_RejectInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_reject_invite_callback", ret); }); } void IEOS::sessions_interface_send_invite(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_SendInviteOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_SENDINVITE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.SessionName = session_name.get_data(); p_options->reference(); EOS_Sessions_SendInvite(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_SendInviteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_send_invite_callback", ret); }); } void IEOS::sessions_interface_start_session(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); EOS_Sessions_StartSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_STARTSESSION_API_LATEST; options.SessionName = session_name.get_data(); p_options->reference(); EOS_Sessions_StartSession(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_StartSessionCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); IEOS::get_singleton()->emit_signal("sessions_interface_start_session_callback", ret); }); } void IEOS::sessions_interface_unregister_players(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); CharString session_name = VARIANT_TO_CHARSTRING(p_options->get("session_name")); TypedArray players_to_unregister = p_options->get("players_to_unregister"); EOS_Sessions_UnregisterPlayersOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_UNREGISTERPLAYERS_API_LATEST; options.SessionName = session_name.get_data(); options.PlayersToUnregisterCount = players_to_unregister.size(); EOS_ProductUserId *players_to_unregister_array = (EOS_ProductUserId *)malloc(sizeof(EOS_ProductUserId) * players_to_unregister.size()); for (int i = 0; i < players_to_unregister.size(); i++) { players_to_unregister_array[i] = eosg_string_to_product_user_id(VARIANT_TO_CHARSTRING(players_to_unregister[i]).get_data()); } options.PlayersToUnregister = players_to_unregister_array; p_options->reference(); EOS_Sessions_UnregisterPlayers(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_UnregisterPlayersCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); Array unregistered_players_array = Array(); for (int i = 0; i < data->UnregisteredPlayersCount; i++) { unregistered_players_array.append(eosg_product_user_id_to_string(data->UnregisteredPlayers[i])); } ret["unregistered_players"] = unregistered_players_array; IEOS::get_singleton()->emit_signal("sessions_interface_unregister_players_callback", ret); }); } void IEOS::sessions_interface_update_session(Ref p_options) { ERR_FAIL_NULL(s_sessionsInterface); Ref session_modification = Object::cast_to(p_options->get("session_modification")); ERR_FAIL_NULL_MSG(session_modification, "Error updating session. UpdateSessionOptions.session_modification is null."); ERR_FAIL_NULL_MSG(session_modification->get_internal(), "Error updating session. EOSGSessionModification is null."); EOS_Sessions_UpdateSessionOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_SESSIONS_UPDATESESSION_API_LATEST; options.SessionModificationHandle = session_modification->get_internal(); p_options->reference(); EOS_Sessions_UpdateSession(s_sessionsInterface, &options, (void *)*p_options, [](const EOS_Sessions_UpdateSessionCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["session_name"] = EOSG_GET_STRING(data->SessionName); ret["session_id"] = EOSG_GET_STRING(data->SessionId); IEOS::get_singleton()->emit_signal("sessions_interface_update_session_callback", ret); }); } ================================================ FILE: src/stats_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::stats_interface_copy_stat_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_statsInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Stats_CopyStatByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_STATS_COPYSTATBYINDEX_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.StatIndex = static_cast(static_cast(p_options->get("stat_index"))); EOS_Stats_Stat *outStat = nullptr; EOS_EResult res = EOS_Stats_CopyStatByIndex(s_statsInterface, &options, &outStat); Dictionary ret; ret["result_code"] = static_cast(res); ret["stat"] = eosg_stats_stat_to_dict_and_release(outStat); return ret; } Dictionary IEOS::stats_interface_copy_stat_by_name(Ref p_options) { ERR_FAIL_NULL_V(s_statsInterface, {}); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString name = VARIANT_TO_CHARSTRING(p_options->get("name")); EOS_Stats_CopyStatByNameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_STATS_COPYSTATBYNAME_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.Name = name.get_data(); EOS_Stats_Stat *outStat = nullptr; EOS_EResult res = EOS_Stats_CopyStatByName(s_statsInterface, &options, &outStat); Dictionary ret; ret["result_code"] = static_cast(res); ret["stat"] = eosg_stats_stat_to_dict_and_release(outStat); return ret; } int IEOS::stats_interface_get_stats_count(Ref p_options) { ERR_FAIL_NULL_V(s_statsInterface, 0); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_Stats_GetStatCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_STATS_GETSTATSCOUNT_API_LATEST; options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); return static_cast(EOS_Stats_GetStatsCount(s_statsInterface, &options)); } void IEOS::stats_interface_ingest_stat(Ref p_options) { ERR_FAIL_NULL(s_statsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); Array p_stats = p_options->get("stats"); int stats_count = p_stats.size(); EOS_Stats_IngestData *stats = (EOS_Stats_IngestData *)memalloc(sizeof(EOS_Stats_IngestData) * stats_count); PERSISTENT_CHAR_ARRAY_CREATE(stat_names, stat_names_charstrings, stats_count) for (int i = 0; i < stats_count; i++) { Dictionary p_stat = p_stats[i]; PERSISTENT_CHAR_ARRAY_SET(stat_names, stat_names_charstrings, i, p_stat["stat_name"]); stats[i].ApiVersion = EOS_STATS_INGESTDATA_API_LATEST; stats[i].StatName = stat_names[i]; stats[i].IngestAmount = static_cast(static_cast(p_stat["ingest_amount"])); } EOS_Stats_IngestStatOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_STATS_INGESTSTAT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); options.StatsCount = stats_count; options.Stats = stats; p_options->reference(); EOS_Stats_IngestStat(s_statsInterface, &options, (void *)*p_options, [](const EOS_Stats_IngestStatCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("stats_interface_ingest_stat_callback", ret); }); } void IEOS::stats_interface_query_stats(Ref p_options) { ERR_FAIL_NULL(s_statsInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); Array p_stat_names = p_options->get("stat_names"); int stat_names_count = p_stat_names.size(); const char **stat_names = (const char **)memalloc(sizeof(const char *) * stat_names_count); PERSISTENT_CHAR_ARRAY_CREATE(stat_names_cstr, stat_names_cstr_charstrings, stat_names_count) for (int i = 0; i < stat_names_count; i++) { PERSISTENT_CHAR_ARRAY_SET(stat_names_cstr, stat_names_cstr_charstrings, i, p_stat_names[i]); stat_names[i] = stat_names_cstr[i]; } EOS_Stats_QueryStatsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_STATS_QUERYSTATS_API_LATEST; if (local_user_id.length() > 0) { options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); } options.TargetUserId = eosg_string_to_product_user_id(target_user_id.get_data()); if (stat_names_count > 0) { options.StatNamesCount = stat_names_count; options.StatNames = stat_names; } options.StartTime = static_cast(p_options->get("start_time")); options.EndTime = static_cast(p_options->get("end_time")); p_options->reference(); EOS_Stats_QueryStats(s_statsInterface, &options, (void *)*p_options, [](const EOS_Stats_OnQueryStatsCompleteCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_product_user_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("stats_interface_query_stats_callback", ret); }); } ================================================ FILE: src/titlestorage_interface.cpp ================================================ #include "godot_cpp/variant/packed_byte_array.hpp" #include "ieos.h" using namespace godot; void IEOS::titlestorage_interface_query_file(Ref p_options) { ERR_FAIL_NULL(s_titleStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); EOS_TitleStorage_QueryFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_QUERYFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); p_options->reference(); EOS_TitleStorage_QueryFile(s_titleStorageInterface, &options, (void *)*p_options, [](const EOS_TitleStorage_QueryFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("titlestorage_interface_query_file_callback", ret); }); } void IEOS::titlestorage_interface_query_file_list(Ref p_options) { ERR_FAIL_NULL(s_titleStorageInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); Array list_of_tags = p_options->get("list_of_tags"); ERR_FAIL_COND_MSG(list_of_tags.size() == 0, "TitleStorage.QueryFileListOptions.list_of_tags must not be empty."); EOS_TitleStorage_QueryFileListOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_QUERYFILELIST_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.ListOfTagsCount = static_cast(list_of_tags.size()); const char **listOfTags = (const char **)memalloc(sizeof(const char *) * options.ListOfTagsCount); for (int i = 0; i < options.ListOfTagsCount; i++) { listOfTags[i] = VARIANT_TO_CHARSTRING(list_of_tags[i]).get_data(); } options.ListOfTags = listOfTags; p_options->reference(); EOS_TitleStorage_QueryFileList(s_titleStorageInterface, &options, (void *)*p_options, [](const EOS_TitleStorage_QueryFileListCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["file_count"] = static_cast(data->FileCount); IEOS::get_singleton()->emit_signal("titlestorage_interface_query_file_list_callback", ret); }); } Dictionary IEOS::titlestorage_interface_copy_file_metadata_by_filename(Ref p_options) { ERR_FAIL_NULL_V(s_titleStorageInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); EOS_TitleStorage_CopyFileMetadataByFilenameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_COPYFILEMETADATABYFILENAME_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); EOS_TitleStorage_FileMetadata *outMetadata = nullptr; EOS_EResult res = EOS_TitleStorage_CopyFileMetadataByFilename(s_titleStorageInterface, &options, &outMetadata); Dictionary ret; ret["result_code"] = static_cast(res); ret["metadata"] = eosg_titlestorage_file_metadata_to_dict_and_release(outMetadata); return ret; } int IEOS::titlestorage_interface_get_file_metadata_count(Ref p_options) { ERR_FAIL_NULL_V(s_titleStorageInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_TitleStorage_GetFileMetadataCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_GETFILEMETADATACOUNT_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); return static_cast(EOS_TitleStorage_GetFileMetadataCount(s_titleStorageInterface, &options)); } Dictionary IEOS::titlestorage_interface_copy_file_metadata_at_index(Ref p_options) { ERR_FAIL_NULL_V(s_titleStorageInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); int index = p_options->get("index"); EOS_TitleStorage_CopyFileMetadataAtIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_COPYFILEMETADATAATINDEX_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Index = static_cast(index); EOS_TitleStorage_FileMetadata *outMetadata = nullptr; EOS_EResult res = EOS_TitleStorage_CopyFileMetadataAtIndex(s_titleStorageInterface, &options, &outMetadata); Dictionary ret; ret["result_code"] = static_cast(res); ret["metadata"] = eosg_titlestorage_file_metadata_to_dict_and_release(outMetadata); return ret; } int IEOS::titlestorage_interface_delete_cache(Ref p_options) { ERR_FAIL_NULL_V(s_titleStorageInterface, static_cast(EOS_EResult::EOS_InvalidState)); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_TitleStorage_DeleteCacheOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_DELETECACHE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); p_options->reference(); EOS_EResult res = EOS_TitleStorage_DeleteCache(s_titleStorageInterface, &options, (void *)*p_options, [](const EOS_TitleStorage_DeleteCacheCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("titlestorage_interface_delete_cache_callback", ret); }); return static_cast(res); } Variant IEOS::titlestorage_interface_read_file(Ref p_options) { ERR_FAIL_NULL_V(s_titleStorageInterface, Variant()); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString filename = VARIANT_TO_CHARSTRING(p_options->get("filename")); int read_chunk_length_bytes = p_options->get("read_chunk_length_bytes"); EOS_TitleStorage_ReadFileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_TITLESTORAGE_READFILE_API_LATEST; options.LocalUserId = eosg_string_to_product_user_id(local_user_id.get_data()); options.Filename = filename.get_data(); options.ReadChunkLengthBytes = static_cast(read_chunk_length_bytes); options.ReadFileDataCallback = [](const EOS_TitleStorage_ReadFileDataCallbackInfo *data) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["total_file_size_bytes"] = static_cast(data->TotalFileSizeBytes); ret["is_last_chunk"] = EOSG_GET_BOOL(data->bIsLastChunk); ret["data_chunk_length_bytes"] = static_cast(data->DataChunkLengthBytes); // Copy data into a PackedByteArray PackedByteArray data_chunk; data_chunk.resize(data->DataChunkLengthBytes); memcpy(data_chunk.ptrw(), data->DataChunk, data->DataChunkLengthBytes); ret["data_chunk"] = data_chunk; IEOS::get_singleton()->emit_signal("titlestorage_interface_read_file_data_callback", ret); // TODO: emit the signal on the EOSGFileTransferRequest object instead of IEOS return EOS_TitleStorage_EReadResult::EOS_TS_RR_ContinueReading; }; options.FileTransferProgressCallback = [](const EOS_TitleStorage_FileTransferProgressCallbackInfo *data) { Dictionary ret; Ref client_data = reinterpret_cast(data->ClientData); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; ret["bytes_transferred"] = static_cast(data->BytesTransferred); ret["total_file_size_bytes"] = static_cast(data->TotalFileSizeBytes); IEOS::get_singleton()->emit_signal("titlestorage_interface_file_transfer_progress_callback", ret); // TODO: emit the signal on the EOSGFileTransferRequest object instead of IEOS }; p_options->reference(); EOS_HTitleStorageFileTransferRequest fileTranferRequest = EOS_TitleStorage_ReadFile(s_titleStorageInterface, &options, (void *)*p_options, [](const EOS_TitleStorage_ReadFileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_product_user_id_to_string(data->LocalUserId); ret["filename"] = data->Filename; IEOS::get_singleton()->emit_signal("titlestorage_interface_read_file_callback", ret); }); return eosg_titlestorage_file_tranfer_request_to_wrapper(fileTranferRequest); } ================================================ FILE: src/ui_interface.cpp ================================================ #include "ieos.h" using namespace godot; int IEOS::ui_interface_acknowledge_event_id(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_UI_AcknowledgeEventIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_ACKNOWLEDGEEVENTID_API_LATEST; options.UiEventId = static_cast(p_options->get("ui_event_id")); options.Result = static_cast(static_cast(p_options->get("result"))); return static_cast(EOS_UI_AcknowledgeEventId(s_uiInterface, &options)); } bool IEOS::ui_interface_get_friends_visible(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, false); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_UI_GetFriendsVisibleOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_GETFRIENDSVISIBLE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return EOSG_GET_BOOL(EOS_UI_GetFriendsVisible(s_uiInterface, &options)); } int IEOS::ui_interface_get_notification_location_preference() { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_UI_ENotificationLocation::EOS_UNL_TopLeft)); return static_cast(EOS_UI_GetNotificationLocationPreference(s_uiInterface)); } int IEOS::ui_interface_get_toggle_friends_key(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_UI_EKeyCombination::EOS_UIK_Shift | EOS_UI_EKeyCombination::EOS_UIK_F3)); EOS_UI_GetToggleFriendsKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_GETTOGGLEFRIENDSKEY_API_LATEST; return static_cast(EOS_UI_GetToggleFriendsKey(s_uiInterface, &options)); } void IEOS::ui_interface_hide_friends(Ref p_options) { ERR_FAIL_NULL(s_uiInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_UI_HideFriendsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_HIDEFRIENDS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); p_options->reference(); EOS_UI_HideFriends(s_uiInterface, &options, (void *)*p_options, [](const EOS_UI_HideFriendsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("ui_interface_hide_friends_callback", ret); }); } bool IEOS::ui_interface_is_valid_key_combination(int key_combination) { ERR_FAIL_NULL_V(s_uiInterface, false); return EOSG_GET_BOOL(EOS_UI_IsValidKeyCombination(s_uiInterface, static_cast(key_combination))); } int IEOS::ui_interface_set_display_preference(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_UI_SetDisplayPreferenceOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SETDISPLAYPREFERENCE_API_LATEST; options.NotificationLocation = static_cast(static_cast(p_options->get("notification_location"))); return static_cast(EOS_UI_SetDisplayPreference(s_uiInterface, &options)); } int IEOS::ui_interface_set_toggle_friends_key(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_UI_SetToggleFriendsKeyOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SETTOGGLEFRIENDSKEY_API_LATEST; options.KeyCombination = static_cast(static_cast(p_options->get("key_combination"))); return static_cast(EOS_UI_SetToggleFriendsKey(s_uiInterface, &options)); } void IEOS::ui_interface_show_friends(Ref p_options) { ERR_FAIL_NULL(s_uiInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_UI_ShowFriendsOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SHOWFRIENDS_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); p_options->reference(); EOS_UI_ShowFriends(s_uiInterface, &options, (void *)*p_options, [](const EOS_UI_ShowFriendsCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); IEOS::get_singleton()->emit_signal("ui_interface_show_friends_callback", ret); }); } bool IEOS::ui_interface_get_friends_exclusive_input(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, false); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); EOS_UI_GetFriendsExclusiveInputOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_GETFRIENDSEXCLUSIVEINPUT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); return EOSG_GET_BOOL(EOS_UI_GetFriendsExclusiveInput(s_uiInterface, &options)); } void IEOS::ui_interface_show_block_player(Ref p_options) { ERR_FAIL_NULL(s_uiInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UI_ShowBlockPlayerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SHOWBLOCKPLAYER_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_UI_ShowBlockPlayer(s_uiInterface, &options, (void *)*p_options, [](const EOS_UI_OnShowBlockPlayerCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("ui_interface_show_block_player_callback", ret); }); } void IEOS::ui_interface_show_report_player(Ref p_options) { ERR_FAIL_NULL(s_uiInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UI_ShowReportPlayerOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SHOWREPORTPLAYER_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_UI_ShowReportPlayer(s_uiInterface, &options, (void *)*p_options, [](const EOS_UI_OnShowReportPlayerCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("ui_interface_show_report_player_callback", ret); }); } int IEOS::ui_interface_pause_social_overlay(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_UI_PauseSocialOverlayOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_PAUSESOCIALOVERLAY_API_LATEST; options.bIsPaused = VARIANT_TO_EOS_BOOL(p_options->get("is_paused")); return static_cast(EOS_UI_PauseSocialOverlay(s_uiInterface, &options)); } bool IEOS::ui_interface_is_social_overlay_paused(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, false); EOS_UI_IsSocialOverlayPausedOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_ISSOCIALOVERLAYPAUSED_API_LATEST; return EOSG_GET_BOOL(EOS_UI_IsSocialOverlayPaused(s_uiInterface, &options)); } int IEOS::ui_interface_set_toggle_friends_button(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); int button_combination = p_options->get("button_combination"); EOS_UI_SetToggleFriendsButtonOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SETTOGGLEFRIENDSBUTTON_API_LATEST; options.ButtonCombination = static_cast(button_combination); return static_cast(EOS_UI_SetToggleFriendsButton(s_uiInterface, &options)); } int IEOS::ui_interface_get_toggle_friends_button(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_UI_EInputStateButtonFlags::EOS_UISBF_None)); EOS_UI_GetToggleFriendsButtonOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_GETTOGGLEFRIENDSBUTTON_API_LATEST; return static_cast(EOS_UI_GetToggleFriendsButton(s_uiInterface, &options)); } bool IEOS::ui_interface_is_valid_button_combination(int p_button_combination) { ERR_FAIL_NULL_V(s_uiInterface, false); return EOSG_GET_BOOL(EOS_UI_IsValidButtonCombination(s_uiInterface, static_cast(p_button_combination))); } int IEOS::ui_interface_report_input_state(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); int button_down_flags = p_options->get("button_down_flags"); int mouse_pos_x = p_options->get("mouse_pos_x"); int mouse_pos_y = p_options->get("mouse_pos_y"); int gamepad_index = p_options->get("gamepad_index"); float left_stick_x = p_options->get("left_stick_x"); float left_stick_y = p_options->get("left_stick_y"); float right_stick_x = p_options->get("right_stick_x"); float right_stick_y = p_options->get("right_stick_y"); float left_trigger = p_options->get("left_trigger"); float right_trigger = p_options->get("right_trigger"); EOS_UI_ReportInputStateOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_REPORTINPUTSTATE_API_LATEST; options.ButtonDownFlags = static_cast(button_down_flags); options.bAcceptIsFaceButtonRight = VARIANT_TO_EOS_BOOL(p_options->get("accept_is_face_button_right")); options.bMouseButtonDown = VARIANT_TO_EOS_BOOL(p_options->get("mouse_button_down")); options.MousePosX = static_cast(mouse_pos_x); options.MousePosY = static_cast(mouse_pos_y); options.GamepadIndex = static_cast(gamepad_index); options.LeftStickX = left_stick_x; options.LeftStickY = left_stick_y; options.RightStickX = right_stick_x; options.RightStickY = right_stick_y; options.LeftTrigger = left_trigger; options.RightTrigger = right_trigger; return static_cast(EOS_UI_ReportInputState(s_uiInterface, &options)); } int IEOS::ui_interface_pre_present(Ref p_options) { ERR_FAIL_NULL_V(s_uiInterface, static_cast(EOS_EResult::EOS_InvalidState)); EOS_UI_PrePresentOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_PREPRESENT_API_LATEST; return static_cast(EOS_UI_PrePresent(s_uiInterface, &options)); } void IEOS::ui_interface_show_native_profile(Ref p_options) { ERR_FAIL_NULL(s_uiInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UI_ShowNativeProfileOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_UI_SHOWNATIVEPROFILE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_UI_ShowNativeProfile(s_uiInterface, &options, (void *)*p_options, [](const EOS_UI_ShowNativeProfileCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("ui_interface_show_native_profile_callback", ret); }); } ================================================ FILE: src/user_info_interface.cpp ================================================ #include "ieos.h" using namespace std; Dictionary IEOS::user_info_interface_copy_external_user_info_by_account_id(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); CharString account_id = VARIANT_TO_CHARSTRING(p_options->get("account_id")); EOS_UserInfo_CopyExternalUserInfoByAccountIdOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYEXTERNALUSERINFOBYACCOUNTID_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); options.AccountId = account_id.get_data(); EOS_UserInfo_ExternalUserInfo *outExternalUserInfo = nullptr; EOS_EResult res = EOS_UserInfo_CopyExternalUserInfoByAccountId(s_userInfoInterface, &options, &outExternalUserInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_user_info"] = eosg_user_info_external_user_info_to_dict_and_release(outExternalUserInfo); return ret; } Dictionary IEOS::user_info_interface_copy_external_user_info_by_account_type(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_CopyExternalUserInfoByAccountTypeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYEXTERNALUSERINFOBYACCOUNTTYPE_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); options.AccountType = static_cast(static_cast(p_options->get("account_type"))); EOS_UserInfo_ExternalUserInfo *outExternalUserInfo = nullptr; EOS_EResult res = EOS_UserInfo_CopyExternalUserInfoByAccountType(s_userInfoInterface, &options, &outExternalUserInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_user_info"] = eosg_user_info_external_user_info_to_dict_and_release(outExternalUserInfo); return ret; } Dictionary IEOS::user_info_interface_copy_external_user_info_by_index(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_CopyExternalUserInfoByIndexOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYEXTERNALUSERINFOBYINDEX_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); options.Index = static_cast(p_options->get("index")); EOS_UserInfo_ExternalUserInfo *outExternalUserInfo = nullptr; EOS_EResult res = EOS_UserInfo_CopyExternalUserInfoByIndex(s_userInfoInterface, &options, &outExternalUserInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["external_user_info"] = eosg_user_info_external_user_info_to_dict_and_release(outExternalUserInfo); return ret; } Dictionary IEOS::user_info_interface_copy_user_info(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_CopyUserInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYUSERINFO_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); EOS_UserInfo *outUserInfo = nullptr; EOS_EResult res = EOS_UserInfo_CopyUserInfo(s_userInfoInterface, &options, &outUserInfo); Dictionary ret; ret["result_code"] = static_cast(res); ret["user_info"] = eosg_user_info_user_info_to_dict_and_release(outUserInfo); return ret; } int IEOS::user_info_interface_get_external_user_info_count(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, 0); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_GetExternalUserInfoCountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_GETEXTERNALUSERINFOCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); return static_cast(EOS_UserInfo_GetExternalUserInfoCount(s_userInfoInterface, &options)); } void IEOS::user_info_interface_query_user_info(Ref p_options) { ERR_FAIL_NULL(s_userInfoInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_QueryUserInfoOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_QUERYUSERINFO_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); p_options->reference(); EOS_UserInfo_QueryUserInfo(s_userInfoInterface, &options, (void *)*p_options, [](const EOS_UserInfo_QueryUserInfoCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); IEOS::get_singleton()->emit_signal("user_info_interface_query_user_info_callback", ret); }); } void IEOS::user_info_interface_query_user_info_by_display_name(Ref p_options) { ERR_FAIL_NULL(s_userInfoInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString display_name = VARIANT_TO_CHARSTRING(p_options->get("display_name")); EOS_UserInfo_QueryUserInfoByDisplayNameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_QUERYUSERINFOBYDISPLAYNAME_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.DisplayName = display_name.get_data(); p_options->reference(); EOS_UserInfo_QueryUserInfoByDisplayName(s_userInfoInterface, &options, (void *)*p_options, [](const EOS_UserInfo_QueryUserInfoByDisplayNameCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); ret["display_name"] = EOSG_GET_STRING(data->DisplayName); IEOS::get_singleton()->emit_signal("user_info_interface_query_user_info_by_display_name_callback", ret); }); } void IEOS::user_info_interface_query_user_info_by_external_account(Ref p_options) { ERR_FAIL_NULL(s_userInfoInterface); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString external_account_id = VARIANT_TO_CHARSTRING(p_options->get("external_account_id")); EOS_UserInfo_QueryUserInfoByExternalAccountOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_QUERYUSERINFOBYEXTERNALACCOUNT_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.ExternalAccountId = external_account_id.get_data(); options.AccountType = static_cast(static_cast(p_options->get("account_type"))); p_options->reference(); EOS_UserInfo_QueryUserInfoByExternalAccount(s_userInfoInterface, &options, (void *)*p_options, [](const EOS_UserInfo_QueryUserInfoByExternalAccountCallbackInfo *data) { Dictionary ret; ret["result_code"] = static_cast(data->ResultCode); Ref client_data = reinterpret_cast(data->ClientData); client_data->unreference(); ret["client_data"] = client_data->get("client_data"); ret["local_user_id"] = eosg_epic_account_id_to_string(data->LocalUserId); ret["target_user_id"] = eosg_epic_account_id_to_string(data->TargetUserId); ret["external_account_id"] = EOSG_GET_STRING(data->ExternalAccountId); ret["account_type"] = static_cast(data->AccountType); IEOS::get_singleton()->emit_signal("user_info_interface_query_user_info_by_external_account_callback", ret); }); } Dictionary IEOS::user_info_interface_copy_best_display_name(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); EOS_UserInfo_CopyBestDisplayNameOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYBESTDISPLAYNAME_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); EOS_UserInfo_BestDisplayName *outBestDisplayName = nullptr; EOS_EResult res = EOS_UserInfo_CopyBestDisplayName(s_userInfoInterface, &options, &outBestDisplayName); Dictionary ret; ret["result_code"] = static_cast(res); ret["best_display_name"] = eosg_user_info_best_display_name_to_dict_and_release(outBestDisplayName); return ret; } Dictionary IEOS::user_info_interface_copy_best_display_name_with_platform(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, {}); CharString local_user_id = VARIANT_TO_CHARSTRING(p_options->get("local_user_id")); CharString target_user_id = VARIANT_TO_CHARSTRING(p_options->get("target_user_id")); int target_platform_type = p_options->get("target_platform_type"); EOS_UserInfo_CopyBestDisplayNameWithPlatformOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_COPYBESTDISPLAYNAMEWITHPLATFORM_API_LATEST; options.LocalUserId = eosg_string_to_epic_account_id(local_user_id.get_data()); options.TargetUserId = eosg_string_to_epic_account_id(target_user_id.get_data()); options.TargetPlatformType = static_cast(target_platform_type); EOS_UserInfo_BestDisplayName *outBestDisplayName = nullptr; EOS_EResult res = EOS_UserInfo_CopyBestDisplayNameWithPlatform(s_userInfoInterface, &options, &outBestDisplayName); Dictionary ret; ret["result_code"] = static_cast(res); ret["best_display_name"] = eosg_user_info_best_display_name_to_dict_and_release(outBestDisplayName); return ret; } int IEOS::user_info_interface_get_local_platform_type(Ref p_options) { ERR_FAIL_NULL_V(s_userInfoInterface, static_cast(EOS_OPT_Unknown)); EOS_UserInfo_GetLocalPlatformTypeOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_USERINFO_GETLOCALPLATFORMTYPE_API_LATEST; return static_cast(EOS_UserInfo_GetLocalPlatformType(s_userInfoInterface, &options)); } ================================================ FILE: src/utils.cpp ================================================ #include "utils.h" String eosg_epic_account_id_to_string(EOS_EpicAccountId accountId) { if (accountId == nullptr) { return String(""); } char *tempBuffer = (char *)memalloc(EOS_EPICACCOUNTID_MAX_LENGTH + 1); int32_t tempBufferSize = EOS_EPICACCOUNTID_MAX_LENGTH + 1; EOS_EResult Result = EOS_EpicAccountId_ToString(accountId, tempBuffer, &tempBufferSize); if (Result == EOS_EResult::EOS_Success) { return String(tempBuffer); } if (IEOS::get_singleton()->isEOSValid) { UtilityFunctions::printerr("\n[EOSG]: eosg_epic_account_id_to_string: Got EOS Result: ", EOS_EResult_ToString(Result), "\n\tat: ", __func__, " (", __FILE__, ":", __LINE__, ") ", "\n"); } return ""; } String eosg_product_user_id_to_string(EOS_ProductUserId localUserId) { if (localUserId == nullptr) { return String(""); } char *tempBuffer = (char *)memalloc(EOS_PRODUCTUSERID_MAX_LENGTH + 1); int32_t tempBufferSize = EOS_PRODUCTUSERID_MAX_LENGTH + 1; EOS_EResult Result = EOS_ProductUserId_ToString(localUserId, tempBuffer, &tempBufferSize); if (Result == EOS_EResult::EOS_Success) { return String(tempBuffer); } if (IEOS::get_singleton()->isEOSValid) { UtilityFunctions::printerr("\n[EOSG]: eosg_product_user_id_to_string: Got EOS Result: ", EOS_EResult_ToString(Result), "\n\tat: ", __func__, " (", __FILE__, ":", __LINE__, ") ", "\n"); } return String(""); } ================================================ FILE: src/utils.h ================================================ #pragma once #include "eosg_active_session.h" #include "eosg_continuance_token.h" #include "eosg_lobby_details.h" #include "eosg_lobby_modification.h" #include "eosg_lobby_search.h" #include "eosg_playerdatastorage_file_transfer_request.h" #include "eosg_presence_modification.h" #include "eosg_session_details.h" #include "eosg_session_modification.h" #include "eosg_session_search.h" #include "eosg_titlestorage_file_transfer_request.h" #include "eosg_transaction.h" #include "godot_cpp/core/memory.hpp" #include "godot_cpp/templates/vector.hpp" #include "godot_cpp/variant/char_string.hpp" #include "godot_cpp/variant/utility_functions.hpp" #include "ieos.h" using namespace godot; #define VARIANT_TO_CHARSTRING(str) ((String)str).utf8() #define VARIANT_TO_EOS_BOOL(var) \ ((var.get_type() == Variant::BOOL) ? ((var.operator bool()) ? EOS_TRUE : EOS_FALSE) : EOS_FALSE) #define EOSG_GET_STRING(str) ((str == nullptr) ? String("") : String::utf8(str)) #define EOSG_GET_BOOL(eosBool) ((eosBool == EOS_TRUE) ? true : false) #ifdef _MSC_VER // Check if using Microsoft Visual Studio #define STRNCPY_S(dest, destsz, src, count) strncpy_s(dest, destsz, src, count) #else #define STRNCPY_S(dest, destsz, src, count) strncpy(dest, src, count) #endif #define EOSG_EOS_HANDLE_TO_WRAPPER(m_eos_handle, m_wrapper) \ if (m_eos_handle == nullptr) { \ return Variant(); \ } \ Ref wrapper = memnew(m_wrapper()); \ wrapper->set_internal(m_eos_handle); \ return wrapper; #define PERSISTENT_CHAR_ARRAY_CREATE(array_name, storage_vector, count) \ Vector storage_vector; \ storage_vector.resize(count); \ const char **array_name = (const char **)memalloc(sizeof(char *) * (count)); #define PERSISTENT_CHAR_ARRAY_SET(array_name, storage_vector, index, variant_value) \ storage_vector.write[index] = VARIANT_TO_CHARSTRING(variant_value); \ array_name[index] = storage_vector[index].get_data(); String eosg_epic_account_id_to_string(EOS_EpicAccountId accountId); static EOS_EpicAccountId eosg_string_to_epic_account_id(const char *p_account_id) { EOS_EpicAccountId accountId = EOS_EpicAccountId_FromString(p_account_id); return accountId; } String eosg_product_user_id_to_string(EOS_ProductUserId localUserId); static EOS_ProductUserId eosg_string_to_product_user_id(const char *p_account_id) { EOS_ProductUserId productUserId = EOS_ProductUserId_FromString(p_account_id); return productUserId; } static Variant eosg_auth_pin_grant_info_to_dict(const EOS_Auth_PinGrantInfo *pinGrantInfo) { if (pinGrantInfo == nullptr) { return Variant(); } Dictionary ret; ret["user_code"] = EOSG_GET_STRING(pinGrantInfo->UserCode); ret["verification_uri"] = EOSG_GET_STRING(pinGrantInfo->VerificationURI); ret["expires_in"] = pinGrantInfo->ExpiresIn; ret["verification_uri_complete"] = EOSG_GET_STRING(pinGrantInfo->VerificationURIComplete); return ret; } static Variant eosg_continuance_token_to_wrapper(EOS_ContinuanceToken p_continuance_token) { EOSG_EOS_HANDLE_TO_WRAPPER(p_continuance_token, EOSGContinuanceToken); } static Variant eosg_auth_id_token_to_dict_and_release(EOS_Auth_IdToken *authIdToken) { if (authIdToken == nullptr) { return Variant(); } Dictionary ret; ret["account_id"] = eosg_epic_account_id_to_string(authIdToken->AccountId); ret["json_web_token"] = EOSG_GET_STRING(authIdToken->JsonWebToken); EOS_Auth_IdToken_Release(authIdToken); return ret; } static Variant eosg_connect_id_token_to_dict_and_release(EOS_Connect_IdToken *connectIdToken) { if (connectIdToken == nullptr) { return Variant(); } Dictionary ret; ret["product_user_id"] = eosg_product_user_id_to_string(connectIdToken->ProductUserId); ret["json_web_token"] = EOSG_GET_STRING(connectIdToken->JsonWebToken); EOS_Connect_IdToken_Release(connectIdToken); return ret; } static Variant eosg_auth_token_to_dict_and_release(EOS_Auth_Token *authToken) { if (authToken == nullptr) { return Variant(); } Dictionary ret; ret["app"] = EOSG_GET_STRING(authToken->App); ret["client_id"] = EOSG_GET_STRING(authToken->ClientId); ret["account_id"] = eosg_epic_account_id_to_string(authToken->AccountId); ret["access_token"] = EOSG_GET_STRING(authToken->AccessToken); ret["expires_in"] = authToken->ExpiresIn; ret["expires_at"] = EOSG_GET_STRING(authToken->ExpiresAt); ret["auth_type"] = static_cast(authToken->AuthType); ret["refresh_token"] = EOSG_GET_STRING(authToken->RefreshToken); ret["refresh_expires_in"] = authToken->RefreshExpiresIn; ret["refresh_expires_at"] = EOSG_GET_STRING(authToken->RefreshExpiresAt); EOS_Auth_Token_Release(authToken); return ret; } static Variant eosg_connect_external_account_info_to_dict_and_release(EOS_Connect_ExternalAccountInfo *externalAccountInfo) { if (externalAccountInfo == nullptr) { return Variant(); } Dictionary ret; ret["product_user_id"] = eosg_product_user_id_to_string(externalAccountInfo->ProductUserId); ret["display_name"] = EOSG_GET_STRING(externalAccountInfo->DisplayName); ret["account_id"] = EOSG_GET_STRING(externalAccountInfo->AccountId); ret["account_id_type"] = static_cast(externalAccountInfo->AccountIdType); ret["last_login_time"] = externalAccountInfo->LastLoginTime; EOS_Connect_ExternalAccountInfo_Release(externalAccountInfo); return ret; } static Variant eosg_ecom_entitlement_to_dict_and_release(EOS_Ecom_Entitlement *entitlement) { if (entitlement == nullptr) { return Variant(); } Dictionary ret; ret["entitlement_name"] = EOSG_GET_STRING(entitlement->EntitlementName); ret["entitlement_id"] = EOSG_GET_STRING(entitlement->EntitlementId); ret["catalog_item_id"] = EOSG_GET_STRING(entitlement->CatalogItemId); ret["server_id"] = entitlement->ServerIndex; ret["redeemed"] = EOSG_GET_BOOL(entitlement->bRedeemed); ret["end_timestamp"] = entitlement->EndTimestamp; EOS_Ecom_Entitlement_Release(entitlement); return ret; } static Variant eosg_ecom_catalog_item_to_dict_and_release(EOS_Ecom_CatalogItem *item) { if (item == nullptr) { return Variant(); } Dictionary ret; ret["catalog_namespace"] = EOSG_GET_STRING(item->CatalogNamespace); ret["id"] = EOSG_GET_STRING(item->Id); ret["entitlement_name"] = EOSG_GET_STRING(item->EntitlementName); ret["title_text"] = EOSG_GET_STRING(item->TitleText); ret["description_text"] = EOSG_GET_STRING(item->DescriptionText); ret["long_description_text"] = EOSG_GET_STRING(item->LongDescriptionText); ret["technical_details_text"] = EOSG_GET_STRING(item->TechnicalDetailsText); ret["developer_text"] = EOSG_GET_STRING(item->DeveloperText); ret["item_type"] = static_cast(item->ItemType); ret["entitlement_end_timestamp"] = item->EntitlementEndTimestamp; EOS_Ecom_CatalogItem_Release(item); return ret; } static Variant eosg_ecom_key_image_info_to_dict_and_release(EOS_Ecom_KeyImageInfo *keyInfo) { if (keyInfo == nullptr) { return Variant(); } Dictionary ret; ret["type"] = EOSG_GET_STRING(keyInfo->Type); ret["url"] = EOSG_GET_STRING(keyInfo->Url); ret["width"] = keyInfo->Width; ret["height"] = keyInfo->Height; EOS_Ecom_KeyImageInfo_Release(keyInfo); return ret; } static Variant eosg_ecom_catalog_release_to_dict_and_release(EOS_Ecom_CatalogRelease *release) { if (release == nullptr) { return Variant(); } Dictionary ret; Array compatible_app_ids = Array(); for (int i = 0; i < release->CompatibleAppIdCount; i++) { compatible_app_ids.append(EOSG_GET_STRING(release->CompatibleAppIds[i])); } ret["compatible_app_ids"] = compatible_app_ids; Array compatible_platforms = Array(); for (int i = 0; i < release->CompatiblePlatformCount; i++) { compatible_platforms.append(EOSG_GET_STRING(release->CompatiblePlatforms[i])); } ret["compatible_platforms"] = compatible_platforms; ret["release_note"] = EOSG_GET_STRING(release->ReleaseNote); EOS_Ecom_CatalogRelease_Release(release); return ret; } static Variant eosg_ecom_catalog_offer_to_dict_and_release(EOS_Ecom_CatalogOffer *offer) { if (offer == nullptr) { return Variant(); } Dictionary ret; ret["server_index"] = offer->ServerIndex; ret["catalog_namespace"] = EOSG_GET_STRING(offer->CatalogNamespace); ret["id"] = EOSG_GET_STRING(offer->Id); ret["title_text"] = EOSG_GET_STRING(offer->TitleText); ret["description_text"] = EOSG_GET_STRING(offer->DescriptionText); ret["long_description_text"] = EOSG_GET_STRING(offer->LongDescriptionText); ret["currency_code"] = EOSG_GET_STRING(offer->CurrencyCode); ret["discount_percentage"] = offer->DiscountPercentage; ret["expiration_timestamp"] = offer->ExpirationTimestamp; ret["purchase_limit"] = offer->PurchaseLimit; ret["available_for_purchase"] = EOSG_GET_BOOL(offer->bAvailableForPurchase); ret["price_result"] = static_cast(offer->PriceResult); ret["original_price"] = offer->OriginalPrice64; ret["current_price"] = offer->CurrentPrice64; ret["decimal_point"] = offer->DecimalPoint; ret["release_date_timstamp"] = offer->ReleaseDateTimestamp; ret["effective_date_timestamp"] = offer->EffectiveDateTimestamp; EOS_Ecom_CatalogOffer_Release(offer); return ret; } static Variant eosg_ecom_transaction_to_wrapper(EOS_Ecom_HTransaction p_transaction) { EOSG_EOS_HANDLE_TO_WRAPPER(p_transaction, EOSGTransaction); } static Variant eosg_user_info_external_user_info_to_dict_and_release(EOS_UserInfo_ExternalUserInfo *externalUserInfo) { if (externalUserInfo == nullptr) { return Variant(); } Dictionary ret; ret["account_type"] = static_cast(externalUserInfo->AccountType); ret["account_id"] = EOSG_GET_STRING(externalUserInfo->AccountId); ret["display_name"] = EOSG_GET_STRING(externalUserInfo->DisplayName); ret["display_name_sanitized"] = EOSG_GET_STRING(externalUserInfo->DisplayNameSanitized); EOS_UserInfo_ExternalUserInfo_Release(externalUserInfo); return ret; } static Variant eosg_user_info_user_info_to_dict_and_release(EOS_UserInfo *userInfo) { if (userInfo == nullptr) { return Variant(); } Dictionary ret; ret["user_id"] = eosg_epic_account_id_to_string(userInfo->UserId); ret["country"] = EOSG_GET_STRING(userInfo->Country); ret["display_name"] = EOSG_GET_STRING(userInfo->DisplayName); ret["display_name_sanitized"] = EOSG_GET_STRING(userInfo->DisplayNameSanitized); ret["preferred_language"] = EOSG_GET_STRING(userInfo->PreferredLanguage); ret["nickname"] = EOSG_GET_STRING(userInfo->Nickname); EOS_UserInfo_Release(userInfo); return ret; } static Variant eosg_user_info_best_display_name_to_dict_and_release(EOS_UserInfo_BestDisplayName *bestDisplayName) { if (bestDisplayName == nullptr) { return Variant(); } Dictionary ret; ret["user_id"] = eosg_epic_account_id_to_string(bestDisplayName->UserId); ret["display_name"] = EOSG_GET_STRING(bestDisplayName->DisplayName); ret["display_name_sanitized"] = EOSG_GET_STRING(bestDisplayName->DisplayNameSanitized); ret["nickname"] = EOSG_GET_STRING(bestDisplayName->Nickname); ret["platform_type"] = static_cast(bestDisplayName->PlatformType); EOS_UserInfo_BestDisplayName_Release(bestDisplayName); return ret; } static Variant eosg_mods_mod_info_to_dict_and_release(EOS_Mods_ModInfo *modsInfo) { if (modsInfo == nullptr) { return Variant(); } Dictionary ret; ret["mods_count"] = static_cast(modsInfo->ModsCount); ret["type"] = static_cast(modsInfo->Type); Array mods = Array(); if (modsInfo->ModsCount > 0 && modsInfo->Mods != nullptr) { for (int i = 0; i < modsInfo->ModsCount; i++) { EOS_Mod_Identifier mod = modsInfo->Mods[i]; Dictionary mod_dict; mod_dict["namespace_id"] = EOSG_GET_STRING(mod.NamespaceId); mod_dict["item_id"] = EOSG_GET_STRING(mod.ItemId); mod_dict["artifact_id"] = EOSG_GET_STRING(mod.ArtifactId); mod_dict["title"] = EOSG_GET_STRING(mod.Title); mod_dict["version"] = EOSG_GET_STRING(mod.Version); mods.append(mod_dict); } } ret["mods"] = mods; EOS_Mods_ModInfo_Release(modsInfo); return ret; } static Variant eosg_mods_mod_identifier_to_dict(const EOS_Mod_Identifier *mod) { if (mod == nullptr) { return Variant(); } Dictionary ret; ret["namespace_id"] = EOSG_GET_STRING(mod->NamespaceId); ret["item_id"] = EOSG_GET_STRING(mod->ItemId); ret["artifact_id"] = EOSG_GET_STRING(mod->ArtifactId); ret["title"] = EOSG_GET_STRING(mod->Title); ret["version"] = EOSG_GET_STRING(mod->Version); return ret; } static Variant eosg_presence_presence_info_to_dict_and_release(EOS_Presence_Info *presence) { if (presence == nullptr) { return Variant(); } Dictionary ret; ret["status"] = static_cast(presence->Status); ret["user_id"] = eosg_epic_account_id_to_string(presence->UserId); ret["product_id"] = EOSG_GET_STRING(presence->ProductId); ret["product_version"] = EOSG_GET_STRING(presence->ProductVersion); ret["platform"] = EOSG_GET_STRING(presence->Platform); ret["rich_text"] = EOSG_GET_STRING(presence->RichText); ret["product_name"] = EOSG_GET_STRING(presence->ProductName); ret["integrated_platform"] = EOSG_GET_STRING(presence->IntegratedPlatform); Array records = Array(); if (presence->RecordsCount > 0 && presence->Records != nullptr) { for (int i = 0; i < presence->RecordsCount; i++) { EOS_Presence_DataRecord record = presence->Records[i]; Dictionary record_dict; record_dict["key"] = EOSG_GET_STRING(record.Key); record_dict["value"] = EOSG_GET_STRING(record.Value); records.append(record_dict); } } ret["records"] = records; EOS_Presence_Info_Release(presence); return ret; } static Variant eosg_presence_presence_modification_to_wrapper(EOS_HPresenceModification p_presence_modification) { EOSG_EOS_HANDLE_TO_WRAPPER(p_presence_modification, EOSGPresenceModification); } static Variant eosg_achievements_definition_to_dict_and_release(EOS_Achievements_DefinitionV2 *definition) { if (definition == nullptr) { return Variant(); } Dictionary ret; ret["achievement_id"] = EOSG_GET_STRING(definition->AchievementId); ret["unlocked_display_name"] = EOSG_GET_STRING(definition->UnlockedDisplayName); ret["unlocked_description"] = EOSG_GET_STRING(definition->UnlockedDescription); ret["locked_display_name"] = EOSG_GET_STRING(definition->LockedDisplayName); ret["locked_description"] = EOSG_GET_STRING(definition->LockedDescription); ret["flavor_text"] = EOSG_GET_STRING(definition->FlavorText); ret["unlocked_icon_url"] = EOSG_GET_STRING(definition->UnlockedIconURL); ret["locked_icon_url"] = EOSG_GET_STRING(definition->LockedIconURL); ret["is_hidden"] = EOSG_GET_BOOL(definition->bIsHidden); Array stat_thresholds = Array(); for (int i = 0; i < definition->StatThresholdsCount; i++) { Dictionary stat_threshold_dict; stat_threshold_dict["name"] = EOSG_GET_STRING(definition->StatThresholds[i].Name); stat_threshold_dict["threshold"] = definition->StatThresholds[i].Threshold; stat_thresholds.append(stat_threshold_dict); } ret["stat_thresholds"] = stat_thresholds; EOS_Achievements_DefinitionV2_Release(definition); return ret; } static Variant eosg_achievements_player_achievement_to_dict_and_release(EOS_Achievements_PlayerAchievement *achievement) { if (achievement == nullptr) { return Variant(); } Dictionary ret; ret["achievement_id"] = EOSG_GET_STRING(achievement->AchievementId); ret["progress"] = achievement->Progress; ret["unlock_time"] = achievement->UnlockTime; ret["display_name"] = EOSG_GET_STRING(achievement->DisplayName); ret["description"] = EOSG_GET_STRING(achievement->Description); ret["icon_url"] = EOSG_GET_STRING(achievement->IconURL); ret["flavor_text"] = EOSG_GET_STRING(achievement->FlavorText); Array stat_infos = Array(); for (int i = 0; i < achievement->StatInfoCount; i++) { Dictionary stat_info_dict; stat_info_dict["name"] = EOSG_GET_STRING(achievement->StatInfo[i].Name); stat_info_dict["current_value"] = achievement->StatInfo[i].CurrentValue; stat_info_dict["threshold_value"] = achievement->StatInfo[i].ThresholdValue; stat_infos.append(stat_info_dict); } ret["stat_infos"] = stat_infos; EOS_Achievements_PlayerAchievement_Release(achievement); return ret; } static Variant eosg_stats_stat_to_dict_and_release(EOS_Stats_Stat *stat) { if (stat == nullptr) { return Variant(); } Dictionary ret; ret["name"] = EOSG_GET_STRING(stat->Name); ret["start_time"] = stat->StartTime; ret["end_time"] = stat->EndTime; ret["value"] = stat->Value; EOS_Stats_Stat_Release(stat); return ret; } static Variant eosg_leaderboards_definition_to_dict_and_release(EOS_Leaderboards_Definition *definition) { if (definition == nullptr) { return Variant(); } Dictionary ret; ret["leaderboard_id"] = EOSG_GET_STRING(definition->LeaderboardId); ret["stat_name"] = EOSG_GET_STRING(definition->StatName); ret["aggregation"] = static_cast(definition->Aggregation); ret["start_time"] = definition->StartTime; ret["end_time"] = definition->EndTime; EOS_Leaderboards_Definition_Release(definition); return ret; } static Variant eosg_leaderboards_leaderboard_record_to_dict_and_release(EOS_Leaderboards_LeaderboardRecord *record) { if (record == nullptr) { return Variant(); } Dictionary ret; ret["user_id"] = eosg_product_user_id_to_string(record->UserId); ret["rank"] = record->Rank; ret["score"] = record->Score; ret["user_display_name"] = EOSG_GET_STRING(record->UserDisplayName); EOS_Leaderboards_LeaderboardRecord_Release(record); return ret; } static Variant eosg_leaderboards_leaderboard_user_score_to_dict_and_release(EOS_Leaderboards_LeaderboardUserScore *userScore) { if (userScore == nullptr) { return Variant(); } Dictionary ret; ret["user_id"] = eosg_product_user_id_to_string(userScore->UserId); ret["score"] = userScore->Score; EOS_Leaderboards_LeaderboardUserScore_Release(userScore); return ret; } static Variant eosg_kws_permission_status_to_dict_and_release(EOS_KWS_PermissionStatus *permission) { if (permission == nullptr) { return Variant(); } Dictionary ret; ret["name"] = EOSG_GET_STRING(permission->Name); ret["status"] = static_cast(permission->Status); EOS_KWS_PermissionStatus_Release(permission); return ret; } static Variant eosg_lobby_details_info_to_dict_and_release(EOS_LobbyDetails_Info *info) { if (info == nullptr) { return Variant(); } Dictionary ret; ret["lobby_id"] = EOSG_GET_STRING(info->LobbyId); ret["lobby_owner_user_id"] = eosg_product_user_id_to_string(info->LobbyOwnerUserId); ret["permission_level"] = static_cast(info->PermissionLevel); ret["available_slots"] = info->AvailableSlots; ret["max_members"] = info->MaxMembers; ret["allow_invites"] = EOSG_GET_BOOL(info->bAllowInvites); ret["bucket_id"] = EOSG_GET_STRING(info->BucketId); ret["allow_host_migration"] = EOSG_GET_BOOL(info->bAllowHostMigration); ret["rtc_room_enabled"] = EOSG_GET_BOOL(info->bRTCRoomEnabled); ret["allow_join_by_id"] = EOSG_GET_BOOL(info->bAllowJoinById); ret["rejoin_after_kick_requires_invite"] = EOSG_GET_BOOL(info->bRejoinAfterKickRequiresInvite); ret["presence_enabled"] = EOSG_GET_BOOL(info->bPresenceEnabled); Array allowed_platform_ids = Array(); for (int i = 0; i < info->AllowedPlatformIdsCount; i++) { allowed_platform_ids.append(static_cast(info->AllowedPlatformIds[i])); } ret["allowed_platform_ids"] = allowed_platform_ids; EOS_LobbyDetails_Info_Release(info); return ret; } static Variant eosg_lobby_details_member_info_to_dict_and_release(EOS_LobbyDetails_MemberInfo *memberInfo) { if (memberInfo == nullptr) { return Variant(); } Dictionary ret; ret["user_id"] = eosg_product_user_id_to_string(memberInfo->UserId); ret["platform"] = static_cast(memberInfo->Platform); ret["allows_crossplay"] = EOSG_GET_BOOL(memberInfo->bAllowsCrossplay); EOS_LobbyDetails_MemberInfo_Release(memberInfo); return ret; } static Variant eosg_lobby_attribute_data_to_dict(EOS_Lobby_AttributeData *attributeData) { if (attributeData == nullptr) { return Variant(); } Dictionary ret; ret["key"] = EOSG_GET_STRING(attributeData->Key); ret["value_type"] = static_cast(attributeData->ValueType); switch (attributeData->ValueType) { case EOS_EAttributeType::EOS_AT_BOOLEAN: ret["value"] = attributeData->Value.AsBool; break; case EOS_EAttributeType::EOS_AT_INT64: ret["value"] = attributeData->Value.AsInt64; break; case EOS_EAttributeType::EOS_AT_DOUBLE: ret["value"] = attributeData->Value.AsDouble; break; case EOS_EAttributeType::EOS_AT_STRING: ret["value"] = EOSG_GET_STRING(attributeData->Value.AsUtf8); break; default: UtilityFunctions::printerr("\nError: EOSG Utils: eosg_lobby_attribute_data_to_dict: Unknown value type: ", static_cast(attributeData->ValueType), "\n\tat: ", __func__, " (", __FILE__, ":", __LINE__, ") ", "\n"); break; } return ret; } static Variant eosg_lobby_attribute_to_dict_and_release(EOS_Lobby_Attribute *attribute) { if (attribute == nullptr) { return Variant(); } Dictionary ret; ret["visibility"] = static_cast(attribute->Visibility); ret["data"] = eosg_lobby_attribute_data_to_dict(attribute->Data); EOS_Lobby_Attribute_Release(attribute); return ret; } static Variant eosg_lobby_lobby_modification_to_wrapper(EOS_HLobbyModification lobbyModification) { EOSG_EOS_HANDLE_TO_WRAPPER(lobbyModification, EOSGLobbyModification); } static Variant eosg_lobby_lobby_search_to_wrapper(EOS_HLobbySearch lobbySearch) { EOSG_EOS_HANDLE_TO_WRAPPER(lobbySearch, EOSGLobbySearch); } static Variant eosg_lobby_lobby_details_to_wrapper(EOS_HLobbyDetails lobbyDetails) { EOSG_EOS_HANDLE_TO_WRAPPER(lobbyDetails, EOSGLobbyDetails); } static EOS_Lobby_LocalRTCOptions eosg_variant_to_lobby_local_rtc_options(Variant p_local_rtc_options) { Dictionary local_rtc_options = p_local_rtc_options; int flags = 0; if (local_rtc_options.has("flags")) { flags = local_rtc_options["flags"]; } bool use_manual_audio_input = false; if (local_rtc_options.has("use_manual_audio_input")) { use_manual_audio_input = local_rtc_options["use_manual_audio_input"]; } bool use_manual_audio_output = false; if (local_rtc_options.has("use_manual_audio_output")) { use_manual_audio_output = local_rtc_options["use_manual_audio_output"]; } bool local_audio_device_input_starts_muted = false; if (local_rtc_options.has("local_audio_device_input_starts_muted")) { local_audio_device_input_starts_muted = local_rtc_options["local_audio_device_input_starts_muted"]; } EOS_Lobby_LocalRTCOptions options; memset(&options, 0, sizeof(options)); options.ApiVersion = EOS_LOBBY_LOCALRTCOPTIONS_API_LATEST; options.Flags = flags; options.bUseManualAudioInput = use_manual_audio_input ? EOS_TRUE : EOS_FALSE; options.bUseManualAudioOutput = use_manual_audio_output ? EOS_TRUE : EOS_FALSE; options.bLocalAudioDeviceInputStartsMuted = local_audio_device_input_starts_muted ? EOS_TRUE : EOS_FALSE; return options; } static Variant eosg_playerdatastorage_file_metadata_to_dict_and_release(EOS_PlayerDataStorage_FileMetadata *metadata) { if (metadata == nullptr) { return Variant(); } Dictionary ret; ret["file_size_bytes"] = metadata->FileSizeBytes; ret["md5_hash"] = EOSG_GET_STRING(metadata->MD5Hash); ret["filename"] = EOSG_GET_STRING(metadata->Filename); ret["last_modified_time"] = metadata->LastModifiedTime; ret["unencrypted_data_size_bytes"] = metadata->UnencryptedDataSizeBytes; EOS_PlayerDataStorage_FileMetadata_Release(metadata); return ret; } static Variant eosg_playerdatastorage_file_tranfer_request_to_wrapper(EOS_HPlayerDataStorageFileTransferRequest request) { EOSG_EOS_HANDLE_TO_WRAPPER(request, EOSGPlayerDataStorageFileTransferRequest); } static Variant eosg_titlestorage_file_metadata_to_dict_and_release(EOS_TitleStorage_FileMetadata *metadata) { if (metadata == nullptr) { return Variant(); } Dictionary ret; ret["file_size_bytes"] = metadata->FileSizeBytes; ret["md5_hash"] = EOSG_GET_STRING(metadata->MD5Hash); ret["filename"] = EOSG_GET_STRING(metadata->Filename); ret["unencrypted_data_size_bytes"] = metadata->UnencryptedDataSizeBytes; EOS_TitleStorage_FileMetadata_Release(metadata); return ret; } static Variant eosg_titlestorage_file_tranfer_request_to_wrapper(EOS_HTitleStorageFileTransferRequest request) { EOSG_EOS_HANDLE_TO_WRAPPER(request, EOSGTitleStorageFileTransferRequest); } static Variant eosg_sanctions_player_sanction_to_dict_and_release(EOS_Sanctions_PlayerSanction *playerSanction) { if (playerSanction == nullptr) { return Variant(); } Dictionary ret; ret["time_placed"] = playerSanction->TimePlaced; ret["action"] = EOSG_GET_STRING(playerSanction->Action); ret["time_expires"] = playerSanction->TimeExpires; ret["reference_id"] = EOSG_GET_STRING(playerSanction->ReferenceId); EOS_Sanctions_PlayerSanction_Release(playerSanction); return ret; } static Variant eosg_sessions_session_details_settings_to_dict(const EOS_SessionDetails_Settings *settings) { if (settings == nullptr) { return Variant(); } Dictionary ret; ret["bucket_id"] = EOSG_GET_STRING(settings->BucketId); ret["num_public_connections"] = static_cast(settings->NumPublicConnections); ret["allow_join_in_progress"] = EOSG_GET_BOOL(settings->bAllowJoinInProgress); ret["permission_level"] = static_cast(settings->PermissionLevel); ret["invites_allowed"] = EOSG_GET_BOOL(settings->bInvitesAllowed); ret["sanctions_enabled"] = EOSG_GET_BOOL(settings->bSanctionsEnabled); Array allowed_platform_ids_array = Array(); for (int i = 0; i < settings->AllowedPlatformIdsCount; i++) { allowed_platform_ids_array.append(static_cast(settings->AllowedPlatformIds[i])); } ret["allowed_platform_ids"] = allowed_platform_ids_array; return ret; } static Variant eosg_sessions_session_details_info_to_dict(const EOS_SessionDetails_Info *sessionDetails) { if (sessionDetails == nullptr) { return Variant(); } Dictionary ret; ret["session_id"] = EOSG_GET_STRING(sessionDetails->SessionId); ret["host_address"] = EOSG_GET_STRING(sessionDetails->HostAddress); ret["num_open_public_connections"] = static_cast(sessionDetails->NumOpenPublicConnections); ret["owner_user_id"] = eosg_product_user_id_to_string(sessionDetails->OwnerUserId); ret["owner_server_client_id"] = EOSG_GET_STRING(sessionDetails->OwnerServerClientId); ret["settings"] = eosg_sessions_session_details_settings_to_dict(sessionDetails->Settings); return ret; } static Variant eosg_sessions_session_details_info_to_dict_and_release(EOS_SessionDetails_Info *sessionDetails) { Variant ret = eosg_sessions_session_details_info_to_dict(sessionDetails); EOS_SessionDetails_Info_Release(sessionDetails); return ret; } static Variant eosg_sessions_active_session_info_to_dict_and_release(EOS_ActiveSession_Info *info) { if (info == nullptr) { return Variant(); } Dictionary ret; ret["session_name"] = EOSG_GET_STRING(info->SessionName); ret["local_user_id"] = eosg_product_user_id_to_string(info->LocalUserId); ret["state"] = static_cast(info->State); ret["session_details"] = eosg_sessions_session_details_info_to_dict(info->SessionDetails); EOS_ActiveSession_Info_Release(info); return ret; } static Variant eosg_sessions_attribute_data_to_dict(EOS_Sessions_AttributeData *attributeData) { if (attributeData == nullptr) { return Variant(); } Dictionary ret; ret["key"] = EOSG_GET_STRING(attributeData->Key); ret["value_type"] = static_cast(attributeData->ValueType); switch (attributeData->ValueType) { case EOS_EAttributeType::EOS_AT_INT64: ret["value"] = attributeData->Value.AsInt64; break; case EOS_EAttributeType::EOS_AT_DOUBLE: ret["value"] = attributeData->Value.AsDouble; break; case EOS_EAttributeType::EOS_AT_STRING: ret["value"] = EOSG_GET_STRING(attributeData->Value.AsUtf8); break; default: UtilityFunctions::printerr("\nError: EOSG Utils: eosg_sessions_attribute_data_to_dict: Unknown value type: ", static_cast(attributeData->ValueType), "\n\tat: ", __func__, " (", __FILE__, ":", __LINE__, ") ", "\n"); break; } return ret; } static Variant eosg_sessions_session_details_attribute_to_dict_and_release(EOS_SessionDetails_Attribute *attribute) { if (attribute == nullptr) { return Variant(); } Dictionary ret; ret["data"] = eosg_sessions_attribute_data_to_dict(attribute->Data); ret["advertisement_type"] = static_cast(attribute->AdvertisementType); EOS_SessionDetails_Attribute_Release(attribute); return ret; } static Variant eosg_sessions_active_session_to_wrapper(EOS_HActiveSession p_active_session) { EOSG_EOS_HANDLE_TO_WRAPPER(p_active_session, EOSGActiveSession); } static Variant eosg_sessions_session_details_to_wrapper(EOS_HSessionDetails p_session_details) { EOSG_EOS_HANDLE_TO_WRAPPER(p_session_details, EOSGSessionDetails); } static Variant eosg_sessions_session_modification_to_wrapper(EOS_HSessionModification p_session_modification) { EOSG_EOS_HANDLE_TO_WRAPPER(p_session_modification, EOSGSessionModification); } static Variant eosg_sessions_session_search_to_wrapper(EOS_HSessionSearch p_session_search) { EOSG_EOS_HANDLE_TO_WRAPPER(p_session_search, EOSGSessionSearch); } static Variant eosg_rtc_audio_input_device_information_to_dict_and_release(EOS_RTCAudio_InputDeviceInformation *info) { if (info == nullptr) { return Variant(); } Dictionary ret; ret["default_device"] = EOSG_GET_BOOL(info->bDefaultDevice); ret["device_id"] = EOSG_GET_STRING(info->DeviceId); ret["device_name"] = EOSG_GET_STRING(info->DeviceName); EOS_RTCAudio_InputDeviceInformation_Release(info); return ret; } static Variant eosg_rtc_audio_output_device_information_to_dict_and_release(EOS_RTCAudio_OutputDeviceInformation *info) { if (info == nullptr) { return Variant(); } Dictionary ret; ret["default_device"] = EOSG_GET_BOOL(info->bDefaultDevice); ret["device_id"] = EOSG_GET_STRING(info->DeviceId); ret["device_name"] = EOSG_GET_STRING(info->DeviceName); EOS_RTCAudio_OutputDeviceInformation_Release(info); return ret; } static Variant eosg_rtc_audio_audio_buffer_to_dict(EOS_RTCAudio_AudioBuffer *audio_buffer) { if (audio_buffer == nullptr) { return Variant(); } Dictionary ret; ret["frames_count"] = static_cast(audio_buffer->FramesCount); Array frames = Array(); for (int i = 0; i < audio_buffer->FramesCount; i++) { frames.append(audio_buffer->Frames[i]); } ret["frames"] = frames; ret["sample_rate"] = audio_buffer->SampleRate; ret["channels"] = audio_buffer->Channels; return ret; } ================================================ FILE: src/version_interface.cpp ================================================ #include "ieos.h" using namespace godot; String IEOS::version_interface_get_version() { return EOSG_GET_STRING(EOS_GetVersion()); } Dictionary IEOS::version_interface_get_constants() { Dictionary constants = Dictionary(); constants["company_name"] = EOS_COMPANY_NAME; constants["copyright_string"] = EOS_COPYRIGHT_STRING; constants["product_name"] = EOS_PRODUCT_NAME; constants["product_identifier"] = EOS_PRODUCT_IDENTIFIER; constants["major_version"] = EOS_MAJOR_VERSION; constants["minor_version"] = EOS_MINOR_VERSION; constants["patch_version"] = EOS_PATCH_VERSION; return constants; } ================================================ FILE: thirdparty/Paste the EOS C SDK here as eos-sdk ================================================