Compare commits

...
477 Commits
Author SHA1 Message Date
amacocian 86e3579383 Adjust CD pipeline triggers for the component projects (Closes #1038) (#1053) 2025-07-26 22:09:54 +02:00
amacocian ea39346678 Remove screenshots that are no longer available (#1052) 2025-07-26 22:09:54 +02:00
amacocian e4cdc4f939 Fix chart drawing (#1051)
* Reorder dependencies

* Fix visual bug with graphs
2025-07-26 22:09:54 +02:00
amacocian 5acac1ae7a Update dependencies (#1050)
* Update dependencies (Closes #940)

* Update dependencies

* Chart adjustment
2025-07-26 22:09:54 +02:00
amacocian 8c0e734c71 Fix aggregate exception due to concurrency issue on db initialization at startup (Closes #1029) (#1047) 2025-07-26 22:09:54 +02:00
amacocian a56eb20aa0 Fix updater HttpRequestException when connection cannot be established (Closes #1042) (#1046) 2025-07-26 22:09:54 +02:00
amacocian 482854801c Fix format exception popping up during build parse (Closes #1041) (#1045) 2025-07-26 22:09:54 +02:00
amacocian 1a37b8b08f Release 0.9.9.78 (#1044) 2025-07-26 22:09:54 +02:00
amacocian 40d8280ba6 Sort party members by profession when applying party loadout (Closes #1030) (#1036) 2025-06-30 11:09:16 +02:00
amacocian 9533ef0607 Fix builds not showing in FocusView due to locked skills (Closes #1031) (#1035) 2025-06-30 11:09:16 +02:00
amacocian ac963f60b5 Fix build codes not applying due to unescaped url characters (Closes #1032) (#1034) 2025-06-30 11:09:16 +02:00
amacocian 5b6ea99f79 Release 0.9.9.77 (#1033) 2025-06-30 11:09:16 +02:00
amacocian 83ddafea62 Aggregate custom metrics when enabled (Closes #1024) (#1027) 2025-06-29 17:32:52 +02:00
amacocian ab1b604aec Code cleanup (#1026)
* Resolve warnings and move shared props to Directory.Build.props and Directory.Packages.props

* Cleanup more messages

* Fix more messages

* Fix build issues
2025-06-29 17:32:52 +02:00
amacocian d445d0c5f2 Release 0.9.9.76 (#1025) 2025-06-29 17:32:52 +02:00
amacocian f5216020b6 Bugfix for LaunchButton (Closes #1020) (#1022) 2025-06-27 13:59:45 +02:00
amacocian 1b7654f70f Allow users to load and save builds from Focus View (Closes #1018) (#1021) 2025-06-27 13:59:45 +02:00
amacocian 06288ec2a2 Release 0.9.9.75 (#1019) 2025-06-27 13:59:45 +02:00
amacocian 670fba36df Setup APM connection (#1016) 2025-06-25 20:26:30 +02:00
amacocian 0d74a9c15e Fix time parsing exception on price charts (Closes #989) (#1015) 2025-06-25 20:26:30 +02:00
amacocian e0729d00ff Setup opt-in telemetry service (under Diagnostics/Telemetry) (#1014)
* Setup opt-in telemetry service (under Diagnostics/Telemetry)

* Set application environment
2025-06-25 20:26:30 +02:00
amacocian 0adce88894 Release 0.9.9.74 (#1013) 2025-06-25 20:26:30 +02:00
amacocian 77bf5e5359 Allow mods to be reinjected if they detect they need to (#1010) (Closes #1005 #724 #639) 2025-06-24 18:44:42 +02:00
amacocian 5a92b916dd Improve Focus View performance and hooking availability (#1009) (Closes #1007) 2025-06-24 18:44:42 +02:00
amacocian accfed2440 Improve game discovery and attach performance (#1008) (Closes #1004) 2025-06-24 18:44:42 +02:00
amacocian bdc45eb4b3 Release 0.9.9.73 (#1006) 2025-06-24 18:44:42 +02:00
amacocian 6916138351 Speed up build process (#1003) 2025-06-23 18:51:14 +02:00
amacocian 741fb9059d Update builds (#1002) 2025-06-23 18:51:14 +02:00
amacocian 6c9e47e58e Fix CI (#1001) 2025-06-23 18:51:14 +02:00
amacocian 0c3873b737 Update extractor and api (#999) 2025-06-23 18:51:14 +02:00
amacocian 411f213d8b Fix Launch button to allow launching multiple configs from the same exe (#998) 2025-06-23 18:51:14 +02:00
amacocian a55c2b78e9 Update dependencies (#997) 2025-06-23 18:51:14 +02:00
amacocian da3d878d90 Enable re-attachment for multi-exe configs (#996) Closes #993 2025-06-23 18:51:14 +02:00
amacocian f08104a851 Implement Daybreak.API. Reorganize FocusView components (#995) 2025-06-23 18:51:14 +02:00
amacocian 052bcaa29d Greatly improve installer performance (#994) 2025-06-23 18:51:14 +02:00
amacocian 51edeae93a Further decouple shared project from main application (#991) 2025-06-23 18:51:14 +02:00
amacocian 6d12b2beaf Release 0.9.9.72 (#990) 2025-06-23 18:51:14 +02:00
amacocian 7347de58af Build editor improvements (Closes #776) (#986) 2025-05-16 13:41:10 +02:00
amacocian f1ee932f23 EventsView scroll to current/upcoming event (Closes #777) (#985) 2025-05-16 13:41:10 +02:00
amacocian cdbc81e310 Performance improvements to Build Synchronization View (Closes #980) (#984) 2025-05-16 13:41:10 +02:00
amacocian 629c6ed14f Support for ReShade latest version (Closes #926) (#983) 2025-05-16 13:41:10 +02:00
amacocian 6577db54bc Update README (#982) 2025-05-16 13:41:10 +02:00
amacocian 926e9b7b48 Release 0.9.9.71 (#981) 2025-05-16 13:41:10 +02:00
amacocian c8433210e3 Hotfix for missing DXVK (#978) 2025-05-15 16:43:07 +02:00
amacocian 3274405c51 Release 0.9.9.70 (Closes #976) (#977) 2025-05-15 16:43:07 +02:00
amacocian 01bde80c39 Use latest DXVK binaries (#974) 2025-05-15 14:18:23 +02:00
amacocian 3f810b8a94 Launch Configurations can select any available executable (Closes #970) (#973) 2025-05-15 14:18:23 +02:00
amacocian 5d7ba2598f DXVK support (Closes #231) (#972) 2025-05-15 14:18:23 +02:00
amacocian 670d6bcbd5 Bump version (#971) 2025-05-15 14:18:23 +02:00
amacocian 9690d649fa Fix plugin example (#968) 2025-05-14 16:15:27 +02:00
amacocian 821ce69547 Show vials of dye in price quotes view (Closes #966) (#967) 2025-05-14 16:15:27 +02:00
amacocian 4f524ada67 Change executable checkmark to foreground color (Closes #774) (#965) 2025-05-14 16:15:27 +02:00
amacocian 0c2234f31b Disable executables during update (Closes #779) (#964) 2025-05-14 16:15:27 +02:00
amacocian 70085efd39 Make AccountsView and ExecutablesView scrollable (Closes #956) (#963) 2025-05-14 16:15:27 +02:00
amacocian bfa2128868 Move Accounts, Executables and LaunchConfigurations on the first positions under Settings (Closes #957) (#962) 2025-05-14 16:15:27 +02:00
amacocian 9b0da9b13c Improve startup speed and responsiveness (Closes #959) (#961) 2025-05-14 16:15:27 +02:00
amacocian 721ee8e494 Huge application refactoring to better support plugins (#960)
* Move shared code to Shared project
* Move from Realm to Squealify
* Rename Daybreak.Shared namespaces
* Setup API project to expose a test API
2025-05-14 16:15:27 +02:00
amacocian 5458db891f Release 0.9.9.68 (#958) 2025-05-14 16:15:27 +02:00
amacocian 9d9c15d0f6 Update dependencies (#954) 2025-05-07 12:01:57 +02:00
amacocian 4f49fc1cdf Expose API for creating menu buttons (#953) 2025-05-07 12:01:57 +02:00
amacocian 7efc7ba698 Release 0.9.9.67 (#952) 2025-05-07 12:01:57 +02:00
amacocian a79a6eae98 Bugfix greyed-out launch button (#949) 2025-04-30 19:20:26 +02:00
amacocian 0801256f4c Release 0.9.9.66 (#948) 2025-04-30 19:20:26 +02:00
amacocian e0799dd100 Remove redundant operations on launch UI. Improve UI performance (#946) 2025-04-30 18:48:03 +02:00
amacocian 24f4607899 Release 0.9.9.65 (#945) 2025-04-30 18:48:03 +02:00
amacocian 7aee0dfc16 Bugfix for multi-launch UI freezes (Closes #941) (#943) 2025-04-29 15:05:45 +02:00
amacocian 5fcb2314bc Release 0.9.9.64 (#942) 2025-04-29 15:05:45 +02:00
amacocian f218b89f6c Match Guildwars folder name structures (#938) 2025-04-27 16:01:22 +02:00
amacocian cc607b37c9 Release 0.9.9.63 (#937) 2025-04-27 16:01:22 +02:00
amacocian f80ea8cf0c Inject toolbox before GW initialization (#935) (#927) 2025-04-26 15:48:54 +02:00
amacocian 39f6707d23 Release 0.9.9.62 (#934) 2025-04-26 15:48:54 +02:00
amacocian eb11029ece Update pipeline runtimes (#933) 2025-04-25 02:42:36 +02:00
amacocian 4583b5b2d3 Fix toolbox injection (Closes #927) (#931) 2025-04-25 02:42:36 +02:00
amacocian 8c01377667 Update .NET9 runtime (#930) 2025-04-25 02:42:36 +02:00
amacocian 9a130d71dd Release 0.9.9.61 (#929) 2025-04-25 02:42:36 +02:00
amacocian 018ed6c5b5 Update dependencies (#928) 2025-04-25 02:42:36 +02:00
amacocianandGitHub cb48248c82 Change VirusTotal publish folder (#913) 2024-12-11 17:18:54 +01:00
amacocianandGitHub e1284375e2 Add debugging info to CD pipeline (#912) 2024-12-11 12:34:00 +00:00
amacocianandGitHub d38d620a84 Add VirusTotal scanning to CD pipeline (#911) 2024-12-11 11:36:39 +00:00
amacocian ce810dafab Support for toolbox builds (#908)
Closes #866
2024-12-10 14:50:59 +01:00
amacocian aabc98e4fc Build template metadata with support for hero positions. Closes #906 (#907) 2024-12-10 14:50:59 +01:00
amacocian 7eb0b7cc4f Release 0.9.9.60 (#905) 2024-12-10 14:50:59 +01:00
amacocian f1eb829ac4 Fix non-persistent notifications showing up after expiring. Closes #901 (#903) 2024-12-07 15:37:53 +01:00
amacocian 4b03a5a16f Increase memory scanner responsiveness (#902) 2024-12-07 15:37:53 +01:00
amacocian 71c90100be Fix memory scanner deadlock. Closes #899 (#900) 2024-12-07 15:37:53 +01:00
amacocian ee512d130d Fix launch button text when Focus View is disabled. Closes #897 (#898) 2024-12-07 15:37:53 +01:00
amacocian 03d0759e57 Release 0.9.9.59 (#896) 2024-12-07 15:37:53 +01:00
amacocian 0db7628d66 Cleanup GWCA dependencies (#895) 2024-12-06 21:19:01 +01:00
amacocian d1d1ba6c08 Fix build pipelines (#894) 2024-12-06 21:19:01 +01:00
amacocian d16a88a9bc Remove submodules file (#893) 2024-12-06 21:19:01 +01:00
amacocian a9d93f4162 Update dependencies (#891) 2024-12-06 21:19:01 +01:00
amacocian 632b5a55ac Remove GWCA (#890) Closes #889 #887 2024-12-06 21:19:01 +01:00
amacocian 6424505631 Release 0.9.9.58 (#888) 2024-12-06 21:19:01 +01:00
amacocian dfc1d3ba0a View for Guild Wars Party Search (#880)
Closes #877
2024-11-22 20:19:04 +01:00
amacocian 5bee88016d Upgrade vulnerable testing package (#879) 2024-11-22 20:19:04 +01:00
amacocian 5a37250680 Release 0.9.9.57 (#878) 2024-11-22 20:19:04 +01:00
amacocian 1c4ccb9fa3 Update dependencies (#874) 2024-11-22 15:09:42 +01:00
amacocian 8403dadca7 Patch GWCA module to not rely on the GW Window to start the http server. To be reverted once GWCA is patched (#871) (#873) 2024-11-22 15:09:42 +01:00
amacocian 4ac4cb0886 Check and update toolbox version (#872) 2024-11-22 15:09:42 +01:00
amacocian 74f13d2447 Update GWCA (#869) 2024-11-22 15:09:42 +01:00
amacocian bf53793729 Release 0.9.9.56 (#868) 2024-11-22 15:09:42 +01:00
amacocian e69dbf6791 Fix error test (#864) 2024-11-17 16:25:36 +01:00
amacocian a0eafa8a13 Setup button for bug reporting (#862)
Overhaul bug exception window
Closes #784
2024-11-17 16:25:36 +01:00
amacocian b0a236beb4 Use safe DateTime conversion (#861) 2024-11-17 16:25:36 +01:00
amacocian c10b81f3a1 Release 0.9.9.55 (#860) 2024-11-17 16:25:36 +01:00
amacocian 6cd51d6584 Fix theme sync (#858)
Closes #857
2024-11-16 00:16:40 +01:00
amacocian a283117cc8 Remove Minimap and InventoryComponent (#856)
Closes #855
2024-11-16 00:16:40 +01:00
amacocian 329e64232c Stop spamming when datetime conversion fails. This is a temporary fix for the spam (#854)
Closes #852
2024-11-16 00:16:40 +01:00
amacocian feb3c9042e Release 0.9.9.54 (#853) 2024-11-16 00:16:40 +01:00
amacocian 71f15f56a4 Update dependencies (#850) 2024-11-13 15:10:02 +01:00
amacocian d37951b832 Update GWCA (#849) 2024-11-13 15:10:02 +01:00
amacocian 908a149df4 Release 0.9.9.53 (#848) 2024-11-13 15:10:02 +01:00
amacocian 38cf140dfa Update GWCA (#838) 2024-10-23 22:57:08 +02:00
amacocian d13e1a2d08 Migrate db engine to Realm (#837) 2024-10-23 22:57:08 +02:00
amacocian a0c7842b84 Update dependencies (#836) 2024-10-23 22:57:08 +02:00
amacocian de13b68633 Release 0.9.9.52 (#835) 2024-10-23 22:57:08 +02:00
amacocian f289602068 Update dependencies - fixes critical System.Text.Json vulnerability (#831) 2024-10-10 12:01:01 +02:00
amacocian 15f4ee8979 Release 0.9.9.51 (#830) 2024-10-10 12:01:01 +02:00
amacocian d826dea457 Use sell quotes. Add sell prices and buy prices to trade quote view (#825) 2024-10-07 11:44:47 +02:00
amacocian b89a6666a3 Update dependencies and replace deprecated packages (#824) 2024-10-07 11:44:47 +02:00
amacocian 1dc69fd0c2 Release 0.9.9.50 (#823) 2024-10-07 11:44:47 +02:00
amacocian 5005b07f09 Update GWCA module (#819) 2024-09-27 17:37:40 +02:00
amacocian 0b692165e0 Update dependencies (#818) 2024-09-27 17:37:40 +02:00
amacocian ffee0fdf4c Release 0.9.9.49 (#817) 2024-09-27 17:37:40 +02:00
amacocian 72b4cd5470 Update GWCA (#808) 2024-08-20 17:34:59 +02:00
dependabot[bot]amacociandependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
dc9be53ace Bump MSTest.TestAdapter from 3.5.0 to 3.5.2 (#795)
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.5.0...v3.5.2)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-08-20 17:34:59 +02:00
dependabot[bot]amacociandependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
3a8a6b0f7f Bump MSTest.TestFramework from 3.5.0 to 3.5.2 (#797)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.5.0...v3.5.2)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-20 17:34:59 +02:00
amacocian 7cdd0534f6 Set version checker as first mod in the list (#807)
Closes #778
2024-08-20 17:34:59 +02:00
amacocian 4b14ab9312 Add database cleanup (#806)
Closes #791
2024-08-20 17:34:59 +02:00
amacocian 57e1ddb8da Release 0.9.9.48 (#805) 2024-08-20 17:34:59 +02:00
amacocian c7609c0f7e Update dependencies (#803) 2024-08-20 16:34:04 +02:00
amacocian 1eca8021d2 Fixes for stuttering on image and notification load (#802) 2024-08-20 16:34:04 +02:00
amacocian 0077f35c4d Fix a bug in credentials migrator that would delete newly created credentials (#801) 2024-08-20 16:34:04 +02:00
amacocian 28b0a0a138 Release 0.9.9.47 (#800) 2024-08-20 16:34:04 +02:00
amacocian cc7ce92013 Update dependencies (#789) 2024-07-18 12:15:25 +03:00
amacocian 5bd02e56ee Ignore operation canceled exceptions (#788) 2024-07-18 12:15:25 +03:00
amacocian 8c7cd0a562 Bugfix for executable paths exception (#787) 2024-07-18 12:15:25 +03:00
amacocian 3ab3f72366 Release 0.9.9.46 (#785) 2024-07-18 12:15:25 +03:00
amacocian 733883a6c2 Check executables for fixes and updates (Closes #763) (#772) 2024-07-07 16:55:34 +03:00
amacocian d1ed8aeaf6 Fix GWCA module (#771) 2024-07-07 16:55:34 +03:00
amacocian 66ad44b8ce Update GWCA (#770) 2024-07-07 16:55:34 +03:00
amacocian 18e51ae408 Fix Nahpui Quarter missing id (Closes #761) (#769) 2024-07-07 16:55:34 +03:00
amacocian 06940dbf38 Update dependencies (#768) 2024-07-07 16:55:34 +03:00
amacocian 875b29f8ba Daybreak maintains and updates executables (#767) 2024-07-07 16:55:34 +03:00
amacocian a953c3a0d3 Release 0.9.9.45 (#766) 2024-07-07 16:55:34 +03:00
amacocian ce198fcfc9 Update GWCA (#759) 2024-06-11 15:03:37 +02:00
amacocian 3d84648af2 Update test dependencies (#757) 2024-06-11 15:03:37 +02:00
amacocian 38bb43bba3 Daybreak paths now fall back to root Daybreak folder (Closes #716) (#756)
* Daybreak paths now fall back to root Daybreak folder (Closes #716)

* Fix some more paths
2024-06-11 15:03:37 +02:00
amacocian cc00709016 Show skill icons in skill list menu (Closes #747) (#755)
* Show skill icons in skill list menu (Closes #747)

* Remove unused code
2024-06-11 15:03:37 +02:00
amacocian ee02b3b49a Download Gw.exe directly (Closes #753) (#754)
* Download Gw.exe directly (Closes #753)

* Downloader improvements
2024-06-11 15:03:37 +02:00
amacocian d1d6b7d757 Update GWCA module (#752) 2024-06-11 15:03:37 +02:00
amacocian 4e1c0c4f50 Save dump on crash (Closes #749) (#751) 2024-06-11 15:03:37 +02:00
amacocian 2a7e2c90fc Update dependencies (#750) 2024-06-11 15:03:37 +02:00
amacocian 0dc957a99d Release 0.9.9.44 2024-06-11 15:03:37 +02:00
amacocian f3f0aeb97e Update GWCA (#736) 2024-05-22 13:09:53 +02:00
amacocian d6dbe6f2cd Freeze layouts calculations on resize (Closes #734) (#735) 2024-05-22 13:09:53 +02:00
amacocian 79da1801bc GWCA Debug Module (#733) 2024-05-22 13:09:53 +02:00
amacocian 2b692b2d88 Update GWCA (#732) 2024-05-22 13:09:53 +02:00
amacocian a7a98f7288 Release 0.9.9.43 (#731) 2024-05-22 13:09:53 +02:00
amacocian 5d27d9c380 Update GWCA plugin (#729) 2024-05-10 23:09:23 +02:00
amacocian fb4185b541 Update httplib (#728) 2024-05-10 23:09:23 +02:00
amacocian d2d6e87e12 Add timeouts to GWCA modules (Closes #725) (#727) 2024-05-10 23:09:23 +02:00
amacocian 354cdda109 Release 0.9.9.42 (#726) 2024-05-10 23:09:23 +02:00
amacocian 83429e264a Update GWCA (#723) 2024-05-09 10:33:05 +02:00
amacocian ffdf374cdd Allow mods to cancel Guild Wars startup (Closes #717) (#721) 2024-05-09 10:33:05 +02:00
amacocian aed34608ee Improve GWCA plugin logging (Closes #718) (#720) 2024-05-09 10:33:05 +02:00
amacocian 18981e131d Release 0.9.9.41 (#719) 2024-05-09 10:33:05 +02:00
amacocian fe0a77d89d Fix automatic screen placer (Closes #708) (#714) 2024-05-06 00:27:48 +03:00
amacocian dbfd555f0d Improve detection of Guild Wars launching successfully (Closes #712) (#713) 2024-05-06 00:27:48 +03:00
amacocian 85d4ac3a29 Remove price checker (now incl. in Toolbox) (Closes #707) (#711) 2024-05-06 00:27:48 +03:00
amacocian dcb983d3de Update GWCA (#710) 2024-05-06 00:27:48 +03:00
amacocian ba1e4348ad Release 0.9.9.40 (#709) 2024-05-06 00:27:48 +03:00
amacocian 04bc7cffc6 Update GWCA (Closes #696) (#705) 2024-05-01 12:35:14 +03:00
amacocian 01b74a936b Rewrite GWCA module for better maintainability (#702)
* Use game thread enqueue method

* Remove manual memory handling

* Use game thread enqueue method

* Remove manual memory handling

* Setup module classes
2024-05-01 12:35:14 +03:00
dependabot[bot]amacociandependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
654b6bc10f Bump HtmlAgilityPack from 1.11.60 to 1.11.61 (#704)
Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.60 to 1.11.61.
- [Release notes](https://github.com/zzzprojects/html-agility-pack/releases)
- [Commits](https://github.com/zzzprojects/html-agility-pack/compare/v1.11.60...v1.11.61)

---
updated-dependencies:
- dependency-name: HtmlAgilityPack
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 12:35:14 +03:00
amacocian c9e6b9f626 Shortcut auto-place no longer removes arguments (Closes #695) (#701) 2024-05-01 12:35:14 +03:00
amacocian 554f1aea9b Fix extracted minimap not updating (Closes #698) (#700) 2024-05-01 12:35:14 +03:00
dependabot[bot]amacociandependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6fb817855c Bump SharpCompress from 0.37.0 to 0.37.2 (#697)
Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.37.0 to 0.37.2.
- [Release notes](https://github.com/adamhathcock/sharpcompress/releases)
- [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.37.0...0.37.2)

---
updated-dependencies:
- dependency-name: SharpCompress
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 12:35:14 +03:00
amacocian b3f5fadfe3 Release 0.9.9.39 (#699) 2024-05-01 12:35:14 +03:00
DubbleClick 9275b7a7f8 simpler PreGameModule, test this 2024-04-28 23:14:58 +02:00
DubbleClick 2d06ff85b8 ignore .idea folder 2024-04-28 23:00:38 +02:00
amacocian 07d06313d9 Support for GuildWars launch arguments (Closes #691) (#693) 2024-04-27 13:46:53 +02:00
amacocian 546409130c Auto launch configuration (Closes #689) (#692) 2024-04-27 13:46:53 +02:00
amacocian 82436872f8 Launch argument support (Closes #689) (#690) 2024-04-27 13:46:53 +02:00
amacocian ad6f76af0c Event notification expiration set to 15s (Closes #686) (#688) 2024-04-27 13:46:53 +02:00
amacocian 8f0ad91a22 Bundle quote alerts (Closes #676) (#687) 2024-04-27 13:46:53 +02:00
amacocian 6a70c5b7cc Fix 'yearss' typo in Trade chat (Closes #677) (#685) 2024-04-27 13:46:53 +02:00
amacocian 5f5d67fdf2 Release 0.9.9.38 (#684) 2024-04-27 13:46:53 +02:00
dependabot[bot]andamacocian 150b41647a Bump SharpCompress from 0.36.0 to 0.37.0 (#675)
Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.36.0 to 0.37.0.
- [Release notes](https://github.com/adamhathcock/sharpcompress/releases)
- [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.36.0...0.37.0)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-26 20:48:21 +02:00
amacocian 66e52e7324 Remove redundant GWCA calls on non-visible components (#681) 2024-04-26 20:48:21 +02:00
amacocian 23328240a9 Fix launch button spamming GWCA (#680) 2024-04-26 20:48:21 +02:00
amacocian 3f06e9486d Setup release 0.9.9.37 (#678) 2024-04-26 20:48:21 +02:00
dependabot[bot]amacociandependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
52a8e912e9 Bump Microsoft.Web.WebView2 from 1.0.2420.47 to 1.0.2478.35 (#669)
Bumps Microsoft.Web.WebView2 from 1.0.2420.47 to 1.0.2478.35.

---
updated-dependencies:
- dependency-name: Microsoft.Web.WebView2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 14:59:34 +02:00
amacocian 641aa8250e Minimap player arrow (#673) 2024-04-23 14:59:34 +02:00
amacocian faa47f32c7 Minimap angle (#672) 2024-04-23 14:59:34 +02:00
amacocian e3dac780df Update GWCA (#671) Closes (#668) 2024-04-23 14:59:34 +02:00
amacocian dfc0f12150 Release 0.9.9.36 (#670) 2024-04-23 14:59:34 +02:00
amacocian 638fb38fa0 Fix exception when extracting maximized minimap (Closes #662) (#666) 2024-04-22 14:33:10 +02:00
amacocian 4d6a06ccdf Floodplains of Mahnkelon id (Closes #653) (#665) 2024-04-22 14:33:10 +02:00
amacocian 3228604876 Item modifiers improvements (#664)
* Improve modifiers check for items

* Update minor vigor
2024-04-22 14:33:10 +02:00
amacocian 44879924d6 Release 0.9.9.35 2024-04-22 14:33:10 +02:00
amacocian 6b7478f6ae Save minimap pinned state (Closes #655) (#659) 2024-04-18 19:24:19 +02:00
amacocian f54d95c1e7 Fix word highlighting edge cases (Closes #656) (#658) 2024-04-18 19:24:19 +02:00
amacocian 5315a45a98 Fix browser navigation (Closes #654) (#657) 2024-04-18 19:24:19 +02:00
amacocian 8575d970a3 Setup release 0.9.9.34 2024-04-18 19:24:19 +02:00
amacocian 5c611eca4f GWCA Module Bugfix (#651) 2024-04-16 18:41:44 +02:00
amacocian 71aa37b965 Added 558 id for sparkfly swamp (#650) 2024-04-16 18:41:44 +02:00
amacocian 0f1b1cc83d Release 0.9.9.33 2024-04-16 18:41:44 +02:00
amacocian 198c1e6eb3 Update Daybreak.GWCA to 0.9.9.32 2024-04-16 11:28:18 +02:00
amacocian e7c9111a1f Allow credentials to be backed up (Closes #525) (#648) 2024-04-16 11:28:18 +02:00
amacocian 1191e996db Update GWCA (#647) 2024-04-16 11:28:18 +02:00
amacocian 19de3c99f9 Release 0.9.9.23 2024-04-16 11:28:18 +02:00
amacocian d857b6dc26 Bugfixes in GWCA module (#645) 2024-04-15 21:56:37 +02:00
amacocian 68b5b79035 Release 0.9.9.31 2024-04-15 21:56:37 +02:00
amacocian 318032d998 Inventory component loading widget (#643) 2024-04-15 12:13:52 +02:00
amacocian 483860532f Update GWCA (#642) 2024-04-15 12:13:52 +02:00
amacocian 694dbe82a9 Race condition crashfix - Downgrade LiteDB dependency - Closes #640 (#641) 2024-04-15 12:13:52 +02:00
amacocian a936f24e32 Setup release branch 2024-04-15 12:13:52 +02:00
amacocian a2ec87ced2 Update GWCA module (#638) 2024-04-12 02:46:53 +02:00
amacocian 48955a7dd7 Setup GWCA whisper functionality (#636) 2024-04-12 02:46:53 +02:00
amacocian 9d7c6c29b0 Setup periodical price checker for inventory (#635) 2024-04-12 02:46:53 +02:00
amacocian 902d210dfd Fix pointer in GWCA plugin (#633) 2024-04-12 02:46:53 +02:00
amacocian 57867851a9 Release 0.9.9.29 2024-04-12 02:46:53 +02:00
amacocianandGitHub ca82cc92bf Release 0.9.9.28 (#632)
Update GWCA
2024-04-11 17:58:39 +00:00
amacocianandGitHub 569ce1e656 Release-0.9.9.27
Release 0.9.9.27
Changelog viewer
2024-04-11 19:28:36 +02:00
amacocian a96c6c3f66 Release 0.9.9.27
Implement changelog view (#629)
2024-04-11 19:19:15 +02:00
amacocian 28c3b2f8c1 Fix CD pipeline 2024-04-11 18:29:04 +02:00
amacocianandGitHub deeccd81b4 Release 0.9.9.26 (#628)
* Event Calendar (#625)

* Event calendar view

* Update GWCA (#626)

* Publish changelog (#627)

* Update dependencies
2024-04-11 16:26:22 +00:00
amacocianandGitHub 6a27245ae8 Release 0.9.9.25 (#618) 2024-04-02 16:59:21 +00:00
amacocianandGitHub 6f4063abb7 Release v0.9.9.24 (#617)
Fix price history functionality
Implement all runes
Update dependencies
2024-03-27 21:33:30 +00:00
amacocianandGitHub f307bcd665 Release 0.9.9.23 (#615)
* Create 0.9.9.23 release

* Update dependencies
2024-03-18 17:52:51 +00:00
amacocianandGitHub 02ca41ba6d Release 0.9.9.22 (#612)
* Fix Daybreak restart commands

* Option to disable dynamic backgrounds

* Improve game running detection
2024-03-04 15:33:00 +00:00
amacocianandGitHub a0ebc87b7c Release 0.9.9.21 (#609)
Fix updater packaging logic
2024-02-27 14:08:02 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d21c55deb0 Bump Xotl/cool-github-releases from 1.1.8 to 1.1.10 (#608)
Bumps [Xotl/cool-github-releases](https://github.com/xotl/cool-github-releases) from 1.1.8 to 1.1.10.
- [Release notes](https://github.com/xotl/cool-github-releases/releases)
- [Commits](https://github.com/xotl/cool-github-releases/compare/v1.1.8...v1.1.10)

---
updated-dependencies:
- dependency-name: Xotl/cool-github-releases
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-27 12:53:07 +00:00
amacocianandGitHub 50a8131ec2 Update dependencies (#607)
Improve updater
2024-02-27 12:42:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e70cfa3d99 Bump coverlet.collector from 6.0.0 to 6.0.1 (#596)
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/compare/v6.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-24 20:46:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4c0c45f1c7 Bump MSTest.TestFramework from 3.2.1 to 3.2.2 (#600)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-24 20:32:37 +00:00
amacocianandGitHub a9e819a83a Release 0.9.9.19 (#602)
Disable GWCA component when Focus View is disabled
2024-02-24 20:20:11 +00:00
amacocianandGitHub 65830f9125 Release 0.9.9.18 (#595)
* Setup release 0.9.9.18

* Minimap transparency
Closes #594

* Toggle to display exploration percentage
Closes #593
2024-02-18 22:55:07 +01:00
amacocianandGitHub 3d9106addf Release 0.9.9.17 (#592)
* Setup release 0.9.9.17

* Save extracted minimap window position (#591)

Closes #590

* Add ability to pin minimap to screen
Closes #585

* Fix ObjectDisposedException
Closes #589
2024-02-18 16:04:52 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
2863032d9a Bump MSTest.TestAdapter from 3.2.0 to 3.2.1 (#587)
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v.3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-02-14 10:48:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
aada189b1b Bump MSTest.TestFramework from 3.2.0 to 3.2.1 (#588)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v.3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-14 11:40:09 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
22ea4de66e Bump NSubstitute.Analyzers.CSharp from 1.0.16 to 1.0.17 (#586)
Bumps [NSubstitute.Analyzers.CSharp](https://github.com/nsubstitute/NSubstitute.Analyzers) from 1.0.16 to 1.0.17.
- [Changelog](https://github.com/nsubstitute/NSubstitute.Analyzers/blob/1.0.17/ReleaseNotes.md)
- [Commits](https://github.com/nsubstitute/NSubstitute.Analyzers/compare/1.0.16...1.0.17)

---
updated-dependencies:
- dependency-name: NSubstitute.Analyzers.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 05:46:15 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9859617a74 Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 (#578)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-07 15:41:59 +01:00
amacocianandGitHub 0629c1929d Release 0.9.9.16 (#582)
* Setup release 0.9.9.16

* Copy buttons for build templates (#580)

* Extract minimap functionality (#581)

* Improve performance of RowAutoMargin functionality
2024-02-07 15:24:47 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6ec45396c3 Bump microsoft/setup-msbuild from 1.3.2 to 2.0.0 (#575)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.3.2 to 2.0.0.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.3.2...v2.0.0)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 10:23:57 +01:00
amacocianandGitHub b76a9b80fb Release 0.9.9.15 (#574)
* Setup release 0.9.9.15

* Export team build from focus view (#573)

Closes #573

* Fix load skill template
2024-02-04 19:30:22 +01:00
amacocianandGitHub f4bc58bbf7 Release 0.9.9.14 (#570)
* Team build support (#565)

* Setup support for team builds
Fix parser to load team builds
Fix bug in attribute loading
Adjust encoder to order and skip attributes for consistency

* Small buildtemplate binding fixes
Setup TeamBuildTemplateView

* Setup button to create team build

* Fix launch button (#566)

Closes #563

* Team build improvements (#568)

Closes #564

* Closes #562 (#569)

Closes #561

* Increment version
2024-02-04 14:11:01 +00:00
amacocianandGitHub 09fdbb6f95 Release 0.9.9.13 (#560) 2024-02-01 14:52:15 +00:00
amacocianandGitHub 8ee7ed23e4 Release 0.9.9.12 (#558)
* Release 0.9.9.12

* Move injection to injector executable (#554)

* Fix logview missing history (#556)

Closes #526

* Detect unresponsive Guild Wars instance (#557)

Closes #534
Closes #422
2024-01-31 22:02:14 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
8d8a78cf88 Bump microsoft/setup-msbuild from 1.3.1 to 1.3.2 (#552)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-01-29 17:06:36 +01:00
amacocianandGitHub f0652231cb Fix minimap mouse interaction (#553)
* Rework minimap renderer

* Fix dragging

* Fix mouse over detection
2024-01-29 15:50:32 +00:00
amacocianandGitHub d9aa760256 Release 0.9.9.10 (#550)
* Release 0.9.9.10
Option to rotate minimap with the camera
Fix launch button not disabling properly when launching
Remove minimap id overlay
Closes #549
Closes #533
Closes #532

* Increment version

* Increment Daybreak.GWCA version
2024-01-28 14:56:51 +00:00
amacocianandGitHub a65f375255 Release 0.9.9.9 (#548)
Closes #547
Closes #546
Closes #536
2024-01-27 19:34:47 +00:00
amacocianandGitHub 3ee58e2711 Release 0.9.9.8 (#545)
* Synchronize browser history

* Intercept mouse button navigation
Closes #535
Closes #544
2024-01-26 14:24:32 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
9b3b083aec Bump MSTest.TestAdapter from 3.1.1 to 3.2.0 (#542)
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-01-26 13:09:38 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ecb9b8f172 Bump MSTest.TestFramework from 3.1.1 to 3.2.0 (#543)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-26 13:08:22 +01:00
amacocianandGitHub 5765c27240 Release 0.9.9.7 (#541)
* Introduce support for browser extensions

* Setup ublock origin

* Release 0.9.9.7 branch

* Automatically backup settings
Closes #539

* Compare settings with remote
2024-01-23 18:25:31 +00:00
amacocianandGitHub c53456112a Update GWCA (#538)
Fix inventory bug
2024-01-20 10:19:46 +00:00
amacocianandGitHub ccea21886e Release 0.9.9.5 (#531)
* Remove character name from credentials
Update vulnerable packages

* Update SharpCompress
2024-01-17 20:00:12 +00:00
amacocianandGitHub 37be8de5ac Update httplib (#529)
Fix missing data when player has no titles
Fix http requests stalling
2024-01-15 19:20:55 +00:00
amacocianandGitHub 107e43a15f Update GWCA (#528) 2024-01-15 13:34:41 +00:00
amacocianandGitHub 17b3ed34db Disable wintersday mode (#527) 2024-01-09 14:06:05 +02:00
amacocianandGitHub cce9cd34af Update README.md (#524)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2023-12-22 07:58:23 +02:00
amacocianandGitHub 6a80f8ee5d Escape parameters passed to Guild Wars on launch (#522) 2023-12-20 14:54:34 +00:00
amacocianandGitHub edb7f0d0bd Beta release 0.9 (#521)
Closes #453
2023-12-20 00:27:29 +00:00
amacocianandGitHub 8e69375338 Fix application updater start message (#520)
Closes #519
2023-12-19 23:59:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
d24ae98fb0 Bump github/codeql-action from 2 to 3 (#516)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2023-12-19 23:22:27 +00:00
amacocianandGitHub 111b3ea632 Setup setting synchronization functionality (#518)
* Setup setting synchronization functionality
Update dependencies
Closes #515

* Disable mainwindow periodic check
2023-12-19 23:05:53 +00:00
amacocian cc1e759972 Improve updater responsiveness
Closes #514
2023-12-15 16:18:03 +01:00
amacocianandGitHub ad0527ed73 Notifications improvements (#513)
Add checkbox for unopened notifications
Added sort options for notifications
Closes #511
Closes #510
2023-12-15 15:13:48 +00:00
amacocianandGitHub 8ffbb918ea Add frequency checking option for trader quote alerts (#512)
Hide sell trade quotes since they don't populate
Closes #509
Closes #508
2023-12-15 13:08:51 +00:00
amacocianandGitHub c27a0dba65 Setup alerts for quotes (#507) 2023-12-12 15:22:40 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ad8305c66d Bump actions/setup-dotnet from 3 to 4 (#505)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3 to 4.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-12 12:41:35 +01:00
amacocianandGitHub 29fe12f094 Revert injector extraction (#504)
* Revert "Extract injector to its own process (#503)"

This reverts commit 9dbe4c5eaf.

* Revert injector extraction
2023-12-04 14:54:34 +01:00
amacocianandGitHub 9dbe4c5eaf Extract injector to its own process (#503) 2023-12-04 14:12:18 +01:00
amacocianandGitHub 1e9aca3807 Onboard SharpNav for pathfinding (#502) 2023-11-29 23:05:41 +00:00
amacocianandGitHub 895da23f7d Re-enable beta update by default (#500)
Extra pictures
2023-11-23 18:39:44 +00:00
amacocianandGitHub f788a10879 Implement file version checking for updater (#499)
* Implement file version checking for updater

* Increment version
2023-11-23 17:17:58 +00:00
amacocianandGitHub 9fe4be0467 Remove privilege mode for updater (#497) 2023-11-22 20:51:54 +00:00
amacocianandGitHub b1a7d3a111 Improve update procedure (#496)
* Improve update procedure

* Change release procedure

* Fix gh release

* Fix release tag

* Change release structure to support folders

* Revert zip file changes to make them backwards compatible

* Change path

* Make launcher not contain the entire dependencies

* Add downtime after API rate limit

* Add rate limit checking before create release

* Enumerate through rates

* Adjust rate limit call

* Change to create release as draft

* Add update logic based on azure blobs

* Fix publish

* Create zip

* Disable CD pipeline for prs

* Increment version

* Fix tests
2023-11-22 21:13:01 +01:00
amacocianandGitHub 8b9fdaaa30 Release v152 (#493)
Improve LaunchView responsiveness
Show current uMod version
Make services and models internal
Closes #491
Closes #490
Closes #381
2023-11-21 16:25:32 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
60d5947535 Bump SharpCompress from 0.34.1 to 0.34.2 (#489)
Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.34.1 to 0.34.2.
- [Release notes](https://github.com/adamhathcock/sharpcompress/releases)
- [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.34.1...0.34.2)

---
updated-dependencies:
- dependency-name: SharpCompress
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-21 15:25:31 +01:00
amacocianandGitHub 0ba0e64a2a Small fixes (#488)
Fix copy folder picker
Fix metrics
Closes #487
Closes #474
2023-11-19 00:17:26 +00:00
amacocianandGitHub e1dfd1f0d0 Update to .net8.0 (#486)
Fix uMod installation steps
ReShade monitor config changes
2023-11-18 23:40:09 +00:00
amacocianandGitHub cd2a529381 Catch exceptions on startup (#485)
Extend process injection timeout (mainly due to uMod)
2023-11-17 15:50:08 +00:00
amacocianandGitHub 6ba8dcac49 Upgrade GWCA (#480) 2023-11-14 13:09:32 +00:00
amacocianandGitHub f2e76b5459 Show winter related background for Wintersday (#479)
* Show winter related background for Wintersday
Closes #449
Contributes to #453

* Increment version
2023-11-13 22:40:41 +00:00
amacocianandGitHub cfbf6ed021 Update toolbox and uMod links to gwdevhub (#477) 2023-11-12 15:30:06 +00:00
amacocianandGitHub a372eb5125 Replace uMod with gMod (#476) 2023-11-10 17:09:54 +00:00
amacocianandGitHub 76b89ca0e8 Make Daybreak create a modlist.txt for uMod (#475) 2023-11-10 11:06:30 +00:00
amacocianandGitHub 2c1721d52c Bugfixes (#473)
Fix NRE in minimap
Add a fallback routine to figure out if an elevated guildwars executable is running
2023-11-10 00:11:24 +00:00
amacocianandGitHub 324a77a625 Update UMod startup action (#472)
Minor mod fixes
2023-11-09 22:09:47 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
88e59b8ec9 Bump Microsoft.NET.Test.Sdk from 17.8.0-release-23468-02 to 17.8.0 (#471)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0-release-23468-02 to 17.8.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.8.0-release-23468-02...v17.8.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-09 23:00:54 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
df71a17d9f Bump Microsoft.Web.WebView2 from 1.0.2088.41 to 1.0.2151.40 (#469)
* Bump Microsoft.Web.WebView2 from 1.0.2088.41 to 1.0.2151.40

Bumps Microsoft.Web.WebView2 from 1.0.2088.41 to 1.0.2151.40.

---
updated-dependencies:
- dependency-name: Microsoft.Web.WebView2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex Macocian <amacocian@yahoo.com>
2023-11-08 19:08:20 +00:00
amacocianandGitHub 8451f53e19 UMod bugfixes (#470) 2023-11-08 18:55:07 +00:00
amacocianandGitHub e795af5c11 Fix 7Zip Extractor (#467)
* Fix 7Zip Extractor
Closes #466

* Increment version
2023-11-05 21:27:37 +00:00
amacocianandGitHub efa3c5175c Add DirectSong support (#465)
Various small fixes
Closes #464
2023-11-05 20:31:30 +00:00
amacocianandGitHub 32efe42a61 Minor fixes (#463)
Fix build loading from GWCA
Introduce persistence toggle for logging to reduce disk usage
Improve performance of memory usage and processor usage counters
Add disk usage counters
Closes #462
Closes #461
2023-11-04 19:30:27 +00:00
amacocianandGitHub 97d2d09cdc Fix toolbox and gwca integration (#460)
Closes #458
2023-11-03 20:05:52 +00:00
amacocianandGitHub ed90d567a4 Wintersday preparation (#457)
Snowfall effect
Setup OnlinePictureClient to support more online sources
Contributes to #453
2023-11-02 17:04:18 +00:00
amacocianandGitHub 51d6fb0f42 Fix vanquishing API (#448)
Pass Health/Energy from GWCA
Fix for enemy primary professions
Fix for LiveCharts2 memory leak
Update GWCA

Closes #447
Closes #446
Closes #445
Closes #444
Closes #442
2023-10-29 16:59:05 +00:00
amacocianandGitHub c10525708c Expose APIs for entity and item names (#443)
Rework logic to reuse functions in Utils.h
Rely on GameState for entity positions and state, greatly improves performance
2023-10-29 00:35:26 +00:00
amacocianandGitHub 0a69d795aa Button to load plugin (#438)
Detect broken GuildWars
Closes #437
Closes #407
2023-10-28 17:36:37 +00:00
amacocianandGitHub 3b1d6831a5 Fix quest metadata position (#436)
* Fix quest metadata position
Closes #435

* Increment version
2023-10-28 14:47:07 +02:00
amacocianandGitHub b4c1dbcddb Deprecate memory reader (#434)
* Deprecate memory reader
Switch completely to GWCA plugin
Fix InventoryModule item ids
Fix InventoryModule modifiers
FIx BloogumClient image retrieval

* Increment version
2023-10-28 12:28:26 +00:00
amacocianandGitHub db5d62d7c0 Name module improvements (#431)
Add waiting logic for names
Remove debouncing logic
Add ability to search wiki by npc name
Update GWCA
2023-10-28 09:51:43 +00:00
amacocianandGitHub ed5ccf38c6 GWCA follow-up (#430)
* Improvements to GWCA integration
Optimize GWCA calls
Add API to get entity names
Fix memory violations in GWCA
Closes #429
Closes #428

* Increment version
2023-10-27 21:33:44 +00:00
amacocianandGitHub 7671e0c04c GWCA Integration (#426)
* GWCA integration
GWCAMemoryReader partial implementation

* Complete Daybreak.GWCA implementation
Fix metrics view
Upgrade dependencies

* Update version

* Add solution dir property

* Another try

* Fix version check

* Fix codeql pipeline

* Codeql fixes

* Fetch submodules

* Final codeql change
2023-10-26 23:18:08 +02:00
amacocianandGitHub f3b8221b29 Improve Guild Wars launcher process (#423)
Added timeouts for mod startup
Added detection logic for uMod breaking after GuildWars update

Closes #415
2023-10-13 11:44:47 +00:00
amacocianandGitHub 8fdc9a67c0 Setup launch configurations (#420)
Adjust Daybreak logic to support loading multiple configurations
Closes #419
Closes #410
2023-10-11 13:29:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bb2e739876 Bump Microsoft.NET.Test.Sdk (#416)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0-preview-23424-02 to 17.8.0-release-23468-02.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.8.0-preview-23424-02...v17.8.0-release-23468-02)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 16:47:47 +02:00
amacocianandGitHub 852cbff5f3 Integrate with ReShade (#417)
Change build to x86 to support new process injection
Add an intermediary step for mods when launching Guild Wars
2023-10-05 12:26:54 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
6e1c111876 Bump HtmlAgilityPack from 1.11.53 to 1.11.54 (#414)
* Bump HtmlAgilityPack from 1.11.53 to 1.11.54

Bumps [HtmlAgilityPack](https://github.com/zzzprojects/html-agility-pack) from 1.11.53 to 1.11.54.
- [Release notes](https://github.com/zzzprojects/html-agility-pack/releases)
- [Commits](https://github.com/zzzprojects/html-agility-pack/compare/v1.11.53...v1.11.54)

---
updated-dependencies:
- dependency-name: HtmlAgilityPack
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Increment version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandru Macocian <amacocian@yahoo.com>
2023-10-04 10:51:24 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9f60839e40 Bump LiveChartsCore.SkiaSharpView.WPF from 2.0.0-rc1 to 2.0.0-rc1.2 (#411)
Bumps [LiveChartsCore.SkiaSharpView.WPF](https://github.com/beto-rodriguez/LiveCharts2) from 2.0.0-rc1 to 2.0.0-rc1.2.
- [Release notes](https://github.com/beto-rodriguez/LiveCharts2/releases)
- [Commits](https://github.com/beto-rodriguez/LiveCharts2/commits)

---
updated-dependencies:
- dependency-name: LiveChartsCore.SkiaSharpView.WPF
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-26 18:26:03 +02:00
amacocianandGitHub dd37db18b5 Theming fixes and improvements (#413)
Fixes issue with LightMode initialization
Syncs Daybreak theme with Windows theme
Closes #412
Closes #406
2023-09-26 16:09:38 +00:00
amacocianandGitHub 793d60f7b5 Add tooltips for buttons (#409)
Closes #379
2023-09-25 14:22:52 +00:00
amacocianandGitHub 08674cd7a6 UMod improvements (#405)
Allow reordering of uMod load order
Allow uMod to load zip files
Fix auto-enable description

Closes #388
Closes #386
Closes #385
2023-09-24 19:57:12 +00:00
amacocianandGitHub dca3e39a6d Force background image (#404)
* Force background image
Closes #403

* Increment version
2023-09-24 20:56:46 +02:00
amacocianandGitHub 8a1033d4b6 Fix for missing options on fresh install (#402)
Closes #401
2023-09-24 20:34:41 +02:00
amacocianandGitHub e4ebe558d7 Use Reloaded.Injector to avoid cross WoW64 boundary issues (#400)
Closes #399
2023-09-23 01:40:40 +00:00
amacocianandGitHub 70728cc5ed Delay toolbox startup (#398)
Closes #397
2023-09-23 01:54:20 +02:00
amacocianandGitHub b49a0a5ff4 Update dependencies (#396) 2023-09-22 23:43:07 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b48d959ffb Bump FluentAssertions from 6.11.0 to 6.12.0 (#394)
Bumps [FluentAssertions](https://github.com/fluentassertions/fluentassertions) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/fluentassertions/fluentassertions/releases)
- [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1)
- [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.11.0...6.12.0)

---
updated-dependencies:
- dependency-name: FluentAssertions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-23 01:23:05 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b8e1ad89af Bump Microsoft.NET.Test.Sdk (#391)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0-preview-23371-04 to 17.8.0-preview-23424-02.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.8.0-preview-23371-04...v17.8.0-preview-23424-02)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-23 01:22:46 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b2bfbfcd86 Bump actions/checkout from 3 to 4 (#390)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-23 01:22:17 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
062e28a6d2 Bump NSubstitute from 5.0.0 to 5.1.0 (#395)
Bumps NSubstitute from 5.0.0 to 5.1.0.

---
updated-dependencies:
- dependency-name: NSubstitute
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-23 01:21:42 +02:00
amacocian dc0f833291 Re-create dependabot.yaml 2023-09-23 01:20:22 +02:00
amacocian e598237916 Remove dependabot.yaml 2023-09-23 01:19:56 +02:00
amacocianandGitHub 6b96d3d9e9 Inject toolbox dll into GuildWars (#389)
Closes #384
2023-09-22 23:11:36 +00:00
amacocianandGitHub 1dbb610846 Code cleanup (#387)
Closes #382
2023-09-22 16:10:07 +00:00
amacocianandGitHub 5c649b0c15 Integrate with uMod dll (#383) 2023-09-22 15:35:05 +00:00
amacocianandGitHub 527ab041d9 Fixes for the Background provider (#377)
Closes #376
2023-09-19 16:21:23 +00:00
amacocianandGitHub ff94e90259 Toggle background effects (#375) 2023-09-19 08:20:52 +00:00
amacocianandGitHub 51efbcce1e Update README.md (#374)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2023-09-19 09:28:37 +02:00
amacocianandGitHub 87b2345105 Daybreak Splash Screen (#373)
Closes #369
2023-09-16 12:57:07 +00:00
amacocianandGitHub 5022d33ca9 Ask for restart only when detecting change in plugins (#372)
Closes #371
2023-09-15 23:21:01 +00:00
amacocianandGitHub 141f949a63 Plugin support (#370)
Adds a plugin management view
Implements a basic plugin as an example
Closes #361
2023-09-15 19:22:52 +00:00
amacocianandGitHub 3913c71fd8 Release v107 (#368)
Handle browser component missing in FocusView
Fix credit text when failing to load image
Closes #298
Closes #366
2023-09-12 13:57:12 +00:00
amacocianandGitHub 5706652c40 Pathing drawing fix (#365)
Closes #364
2023-09-11 15:08:52 +00:00
amacocianandGitHub 768b6b20f5 Pathfinding improvements (#360)
Use optimization passes to improve final paths
2023-09-11 14:04:09 +00:00
amacocianandGitHub 7066b7dbb6 GW2 style backgrounds (#359)
* Guildwars 2 styled background
Fix Screenshot provider
Closes #358

* Remove unnecessary code
2023-09-11 10:14:50 +00:00
amacocianandGitHub 17e01fc7b9 Disable launch button when loadin guildwars (#357)
Verify window placement on startup
Update as notification
Closes #349
Closes #347
Closes #346
2023-09-09 15:31:13 +00:00
amacocianandGitHub ee9fcf38b7 Release v102 (#355)
Restrict maximum notification size
Browser detects builds in downloaded files
BuildTemplate has a button to navigate to the build source
Closes #354
Closes #353
Closes #352
2023-09-08 10:32:47 +00:00
amacocianandGitHub 5abb24bdd4 Append source url for builds loaded through the browser (#351)
Closes #350
2023-09-07 23:48:13 +00:00
amacocianandGitHub 53f17d3f22 Display event remaining time (#348)
* Event notifications display remaining time
Closes #344

* Increment version
2023-09-03 20:06:41 +00:00
amacocianandGitHub b728c38dc1 Map fixes & Npcs (#345) 2023-09-01 12:33:52 +00:00
amacocianandGitHub 5e57b6ecd0 Notification fixes (#343)
* Notification fixes
Closes #342

* Update version
2023-08-31 22:26:49 +00:00
amacocianandGitHub e9ffcdf7c3 Add search capability to logs (#341)
Closes #340
2023-08-22 13:37:18 +00:00
amacocian 118657de7c Allow CD pipeline to be run manually 2023-08-09 23:57:14 +02:00
amacocian 34c332504b Fix cd pipeline 2023-08-09 23:54:45 +02:00
amacocianandGitHub d7a29d68a1 Release v96 (#338)
Add support for non-persistent notifications
LogsView message limit
Tradechat exponential backoff
Possibility to revert to normal mode

Closes #335
Closes #322
Closes #337
2023-08-09 20:56:24 +00:00
amacocianandGitHub eefa02837d Replace Moq with NSubstitute (#336)
* Replace Moq with NSubstitute

* CI fixes
Pin .net 7.0.9 in CI
Update test dependencies

* CI fixes
2023-08-09 15:23:50 +02:00
amacocianandGitHub 5b2812fa61 Release 95 (#334)
Fix mods launching regardless of status
LogsView improvements
NotificationsView improvements
Decrease frequency of ConnectivityStatus
Guildwars array validation
Chromium build template trim selection
Build template viewer improve skill dropdown
Update dependencies

Closes #333
Closes #332
Closes #318
Closes #319
2023-08-08 15:17:16 +00:00
amacocianandGitHub e3ddfb88c1 Add ETA to update view (#331)
Closes #205
2023-08-07 13:14:52 +00:00
amacocianandGitHub 664d94aeaa PreGameContext read implementation (#328)
Fixes for memory scanner
Closes #327
Closes #326
2023-08-07 11:02:36 +00:00
amacocianandGitHub e2baf63099 Events notifications (#325)
Closes #324
2023-08-06 22:29:21 +00:00
amacocianandGitHub c1b7000139 Connection data (#317) 2023-07-24 09:44:43 +00:00
amacocianandGitHub 6e8333d76f CD fixes 2 (#314)
* CD fixes 2

* Increment version
2023-07-19 13:16:12 +03:00
amacocianandGitHub c67eddc9d4 Re-enable CD pipeline (#313) 2023-07-19 10:09:21 +00:00
amacocianandGitHub 292cf6fd94 Screen placer fixes (#312)
* Screen placer fixes
Make screen placer as a mod
Validate login data

* Remove restrictions on CD pipeline
2023-07-19 12:43:15 +03:00
amacocianandGitHub 69038bca43 Revert "Screen placer fixes (#310)" (#311)
This reverts commit f05d15ce36.
2023-07-19 12:25:36 +03:00
amacocianandGitHub f05d15ce36 Screen placer fixes (#310)
Make screen placer as a mod
Validate login data
2023-07-19 12:08:02 +03:00
amacocianandGitHub 98cb349d67 Screen placer fixes (#310)
Make screen placer as a mod
Validate login data
2023-07-19 09:06:57 +00:00
amacocianandGitHub a4314ee4d5 Permission fixes for DSOAL (#309)
* Permission fixes for DSOAL
Registry service fixes

* Show notification on fix
2023-07-16 18:08:03 +00:00
amacocianandGitHub 96097bd25a Unify UI margins (#308) 2023-07-16 16:52:20 +00:00
amacocianandGitHub 14029e6af2 Cloudflare iaum mitigation (#307)
* Cloudflare iaum mitigation
Closes #305

* Increment version
2023-07-15 11:05:27 +00:00
amacocianandGitHub 365777141d Daybreak v84 (#306)
DSOAL installation requiring admin mode
DOSAL installation overwriting existing symbollic link
DSOAL check symbollic link before launching
MenuList can be scrollable
RegisterService implementation
Notifications show date of notification
Performance improvements for picture loading
Allow metric streaming to MetricsView
Handle cloudflare blocking responses
Closes #304
Closes #303
Closes #301
Closes #300
Mitigates #305
2023-07-15 10:32:30 +00:00
amacocianandGitHub aaa5da8d9b More nightfall endgame npcs (#299) 2023-06-30 20:00:52 +00:00
amacocianandGitHub b898ab5679 Fix cache size calculation (#297) 2023-06-29 18:32:18 +00:00
amacocianandGitHub b896fa5cc7 Bugfix for image memory cache (#296)
Improved memory usage monitor
2023-06-29 16:21:19 +00:00
amacocianandGitHub bca9f6d66b Fix loading screen flickering (#295)
* Fix loading screen flickering

* Increment version
2023-06-29 15:01:41 +00:00
amacocianandGitHub d6f71cb2eb Performance improvements (#293)
Ability to disable minimap
Closes #291
Fix main player health/energy proper values
Better granularity for memory access
Focus view uses less memory footprint to gather necesarry resources
2023-06-29 13:38:28 +00:00
amacocianandGitHub 986d09867c Cache duplication fix (#292)
* Cache duplication fix
Performance improvement
Closes #290

* Increment version
2023-06-29 10:08:00 +02:00
amacocianandGitHub 97cb2151f9 Memory reader cache to better control memory access (#289)
Expand guildwars locations, campaign, continent
Make bloogum use image cache
Make bloogum show images based on character location
Link Campaign -> Continent -> Region -> Map
2023-06-28 15:32:19 +00:00
amacocianandGitHub 338ac100a1 Realm of torment npcs (#288) 2023-06-27 16:55:44 +00:00
amacocianandGitHub 01badcee6a More nightfall npcs (#287) 2023-06-27 11:26:25 +00:00
amacocianandGitHub 302cf1114c Fix bug with trader messages options (#286)
Reorder options
2023-06-26 19:05:13 +00:00
amacocianandGitHub 613e03dc75 High def skill icons (#284)
* Use high-definition skill icons

* Increment version
2023-06-26 17:42:38 +00:00
amacocianandGitHub 4267359cb2 DSOAL support (#282)
Reworked mod support
2023-06-26 13:16:29 +00:00
amacocianandGitHub daac8ec787 More nightfall npcs (#281)
Fixes for quests and npcs
UTs for quests, npcs and items
2023-06-26 10:00:17 +00:00
amacocianandGitHub 857adec459 More vabbian npcs (#280)
* More vabbian npcs

* Increment version
2023-06-23 08:50:56 +00:00
amacocianandGitHub b46e10a1a6 Vabbian npcs (#279) 2023-06-22 12:50:18 +00:00
amacocianandGitHub 10799b7e9c Fix modifiers view (#278)
Show modifiers for unknown items
Fix enemy drawing
Added some npcs
2023-06-21 20:19:44 +00:00
amacocianandGitHub 6bbeaa1738 More Kournan npcs (#277) 2023-06-21 17:36:43 +00:00
amacocianandGitHub 1a0f1cbf9a Performance improvements to images (#275) 2023-06-21 09:20:23 +00:00
amacocianandGitHub 68e708fe96 Price history improvements (#274)
Inventory view improvements
Base for inscriptions
Base for runes
2023-06-20 00:32:28 +00:00
amacocianandGitHub a2166f2327 Add Guild Hall npcs (#272) 2023-06-18 15:24:23 +00:00
amacocianandGitHub 6d55366bdc Mark unknown entities with a question mark (#271) 2023-06-18 11:58:39 +00:00
amacocianandGitHub 28fcaa81cf Installer fix (#270)
Closes #269
2023-06-17 18:52:01 +00:00
amacocianandGitHub c5efc2990e Guildwars copy service (#268)
Fix one focus view bug
2023-06-17 14:18:35 +02:00
amacocianandGitHub 597010b9a1 Memory reader base pointer fixes (#267)
Installer fixes
2023-06-16 16:17:20 +00:00
amacocianandGitHub 1f71816256 Make regex trade searches case-insensitive (#264) 2023-06-16 13:09:37 +00:00
amacocianandGitHub 7c3618d2b5 Trade alerts (#263)
Notifications improvements
2023-06-16 11:06:18 +00:00
amacocianandGitHub df9038039c Highlight color specific trading words (#262) 2023-06-09 10:13:36 +00:00
amacocianandGitHub 9a4b23976a Release v56 (#259)
Inventory Component toggle
Fix inventory component visibility
Inventory component performance improvements
2023-06-08 21:33:13 +00:00
amacocianandGitHub 8732e0fe5e Release v55 (#256)
Sounds
Split FocusView into components
Focus view inventory component
Notifications view
Internet connectivity
2023-06-08 19:58:26 +00:00
amacocianandGitHub c23b84c4d5 Improve pathfinding algorithm (#255)
Improve minimap color contrasts
2023-06-01 20:56:49 +00:00
amacocianandGitHub 345b6a792a Color coded boss icons based on primary profession (#254) 2023-05-30 22:00:00 +00:00
amacocianandGitHub d9ae52165d Show npc profession in context menu (#252) 2023-05-30 20:54:03 +00:00
amacocianandGitHub 4f40e8328b Release v51 (#251)
* Release v51
Fixes duplicate npcs
Adds npcs for all games
Improve focus view memory reading latency
Clean up metrics
Closes #202
Closes #203
Closes #201

* Small fixes
2023-05-30 17:08:25 +00:00
amacocianandGitHub ebdd828b06 Improve the price chart date visibility (#250) 2023-05-22 18:49:41 +00:00
amacocianandGitHub 711acb55db Release v49 (#249)
Cache loaded images to improve performance
Separate quotes from trade views
Implement price history view
Local price history database
Fix notification popups
Fix icon caching

Closes #248
Closes #247
Closes #246
Closes #245
2023-05-19 20:08:43 +00:00
amacocianandGitHub 7dfc21bfe0 Setup NotificationService (#244)
Separate price quotes from trades
2023-05-16 20:05:28 +00:00
amacocianandGitHub 78644a6eec Add Flag and Person minimap icons (#242) 2023-05-15 20:41:11 +00:00
amacocianandGitHub 30b1d77575 Implement trade chat views (#239)
More UI improvements
Much better IconCache implementation
Re-enable screen selector
2023-05-15 17:55:25 +00:00
amacocianandGitHub a9626d03a4 Update README.md (#238)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2023-05-14 20:57:05 +02:00
amacocianandGitHub be731bbffb Unify UI buttons (#237)
* Unify UI buttons
Use Theme Accent as highlight
Fix browser concurrency issues
Fix browser enable/disable during runtime
Unify button sizes and looks

Closes #235
Closes #236

* Increment version
2023-05-10 20:35:24 +00:00
amacocianandGitHub 8cd40e2de3 Dynamic Options View (#234)
Import MahApps Metro
2023-05-08 18:52:10 +00:00
amacocianandGitHub 7745594bb6 Draw engagement area for party (#233)
Clean uMod installation
Closes #232
Closes #220
2023-05-07 13:07:37 +00:00
amacocianandGitHub e43bedf1c2 Fix auto-load template procedure (#229)
Fix character name loading on guildwars startup
Closes #126
2023-05-03 21:22:32 +00:00
amacocianandGitHub d6623a8646 Automatically add downloaded tpfs to mod list (#228)
* Automatically add downloaded tpfs to mod list
Closes #227

* Increment version
2023-05-03 16:33:25 +00:00
amacocianandGitHub 403ba51a7d Add possibility to download tpf files from the uMod browser page (#226)
* Add possibility to download tpf files from the uMod browser page
* Organize menu by sections
* Limit logs to improve performance
* Fix screenmanageroptions name
* Fix focusview default webpage
2023-05-03 15:04:40 +00:00
amacocianandGitHub b185a606e6 Integrate with mods (#223)
* Integrate with GWToolboxpp and uMod
* Separate configuration into multiple extensible parts
* Generalize downloading service
2023-05-03 11:10:18 +00:00
amacocianandGitHub 342a9a4826 Add more minimap icons (#221)
Support for team colored icons
2023-05-01 11:36:22 +00:00
amacocianandGitHub 2df2e8a65e Added support for multi-color SVG icons (#219) 2023-04-30 16:04:09 +00:00
amacocianandGitHub 2d5e9231a9 Minimap improvements (#217)
Map icons have overload for minimum size
Pathfinding now finds paths even when start/end points are not on the map
Map can now draw SVGs
2023-04-30 12:31:11 +00:00
amacocianandGitHub 818e2bf1d0 Drawing bugfixes (#210) 2023-04-10 18:47:03 +02:00
amacocianandGitHub f0a876f7cc Rework minimap drawing (#208)
Setup extensible drawing modules
2023-04-10 13:57:35 +00:00
amacocianandGitHub 3fae8f91cb Add support for map icons (#207)
Add res-shrine icons to minimap
2023-04-09 20:26:15 +00:00
amacocianandGitHub 8b08056ca7 Show targeted npc details (#206) 2023-04-09 18:30:25 +00:00
amacocianandGitHub 39eaf53deb Setup startup actions (#204)
Rename temp installer on startup
2023-04-09 16:21:11 +00:00
amacocianandGitHub 4674d1d7e5 Add support for npc types and names (#200)
* Introduce npcs and support for npc names

* Update version
2023-04-07 10:34:11 +00:00
amacocianandGitHub b8b5cc8b1d Optimize pathfinding (#198)
* Optimize pathfinding
Simplified initial search to no longer take distance into account
Metrics for pathfinding

* Increment version
2023-04-05 15:30:48 +00:00
amacocianandGitHub dc987233a7 Retry pathfinding loading on failure (#197) 2023-04-04 21:35:16 +00:00
amacocianandGitHub 266b400c22 Pathfinding v1 (#196)
* Pathfinding algorithm for minimap

* Fix adjacency lists

* Better adjancency list generation

* Improve pathfinding algorithm

* Some other tries

* Pathfinding algorithm for minimap

* Fix adjacency lists

* Better adjancency list generation

* Improve pathfinding algorithm

* Some other tries

* Improvements to the minimap and pathing

* Improve pathfinder

* Draw path forests

* Implement working pathfinding solution
2023-04-04 20:43:14 +00:00
amacocianandGitHub a4ac083fd0 General client improvements (#195)
Hit-target for off-screen quest markers
Save window position on every move instead of only on closing
2023-04-03 20:21:12 +00:00
amacocianandGitHub 320dd16838 Show objective direction for loaded quests (#194) 2023-04-02 19:22:17 +00:00
amacocianandGitHub 5c05c636bc Add quest marker functionality to the minimap (#192) 2023-04-02 14:57:49 +00:00
amacocianandGitHub 7103a8a83a Show party member context (#190)
Fix one Map id
2023-03-25 18:30:29 +00:00
amacocianandGitHub ad749d801a Minimap improvements (#189)
Closes #187
Closes #188

Minimap performance improvements
Add context menues for main player and living entities
2023-03-25 08:36:15 +00:00
amacocianandGitHub 747e9a0490 Minimap improvements (#186)
Closes #185
Closes #184
Closes #183
Show player information when right clicking on minimap
Allow minimap fullscreen
Decouple scrolling from memory reading frequency
Ignore task cancelled exceptions
2023-03-24 22:39:52 +00:00
amacocianandGitHub 836402bd0f Draw player position history on the minimap (#182) 2023-03-21 15:19:25 +00:00
amacocianandGitHub a3d5e1ce1f Add another check for corrupted images (#181)
* Add another check for corrupted images

* Check validity of icons on startup
Improve screen position saving
2023-03-21 08:45:22 +00:00
f2cc59a4ab Daybreak 0.9.8.18 (#179)
* Minimap implementation

* Fix minimap

* Add debounce logic to entities positions

* Cleanup

* Fix x64 build memory reading
Move build to x64
Fix entity debouncing
Added some backstops to prevent reading corrupted memory

* Add minimap zooming and panning functionality
Add main window position saving procedure

* Increment version

---------

Co-authored-by: Alex Macocian <amacocian@hotmail.com>
2023-03-20 21:38:53 +02:00
amacocianandGitHub 6ea77f7e05 Bugfixes and improvements (#176)
Closes #175
Closes #174
Closes #173
2023-03-09 19:53:28 +00:00
amacocianandGitHub 5ea8df7cb9 Bugfixes to memory reader (#172) 2023-03-08 20:17:58 +00:00
amacocianandGitHub c70e8ec90d Implement Guildwars downloader (#171) 2023-03-08 13:24:14 +00:00
amacocianandGitHub 210e3a8cb1 Add title data to memory retrieval (#169)
Add metrics for diagnostics
Add metrics view
2023-03-07 17:20:20 +00:00
amacocianandGitHub a0c3bb094c Performance improvements (#168)
* Performance improvements and bugfixes

* Increment version

* Better builds links

* Unify loading widget size
2023-03-05 08:55:21 +01:00
amacocianandGitHub b4d932692a Reduce words in FocusView character line (#167) 2023-03-04 17:18:03 +00:00
amacocianandGitHub 667534a07a Fix version parsing (#166) 2023-03-04 18:06:53 +01:00
amacocianandGitHub 159928f24b Integrate builds with Focus view (#164)
* Integrate builds with Focus view
Detect when GameData is invalid
Detect when reading memory is faulty
Show current character data in Focus View
Closes #161
Closes #162
Closes #163

* Increment version

* Bugfix version comparison script
2023-03-04 17:21:08 +01:00
amacocianandGitHub 8a91ded610 Fix buildtemplate skill removal (#160)
Closes #159
2023-03-03 22:28:27 +00:00
amacocianandGitHub 4226b7eb32 Improve performance of buildtemplateview (#158) 2023-03-03 21:50:25 +00:00
amacocianandGitHub b61c99dd1c Fix build template failing to load secondary attributes (#157)
Improve build template handling logic
Add attribute points calculator
2023-03-03 20:51:03 +00:00
amacocianandGitHub 1ccb0d5334 Add map entry to FocusView (#155)
Fix Icon Downloader
Improve settings navigation
Change multiple views to match the design of the rest of the application
2023-03-02 20:11:00 +01:00
amacocianandGitHub 7fa01353dd UX Bugfixes and improvements (#151)
Fix memory reading issues when restarting the game
Fixed current energy and health retrieval and calculation
Added health and energy bars in focus view
Fixed focus view size calculation
Improved buildtemplate performance
Improved buildtemplate navigation
Added loading widgets to menus that are loading asynchronously
2023-03-02 16:49:00 +00:00
amacocianandGitHub 4f849d880b Ux improvements (#148)
Rearrange quest log layout
Fix some quest names
Fix some buttons ux
Mitigate issues when reading guildwars memory
2023-03-02 01:58:07 +00:00
amacocianandGitHub 4b60c96aad Closes #146 #145 (#147) 2023-03-02 00:39:52 +00:00
amacocianandGitHub a4dc0d65cf Implement Focus View (#144)
* Implement Focus View
Read extra information from guildwars

* Prever focusview browser from being disposed
2023-03-01 18:42:53 +00:00
amacocianandGitHub 85e017423f #142 - Fix Vanguard Quests Missing (#143)
Closes #142
2023-02-28 18:24:37 +00:00
amacocianandGitHub b38488937b Alexmacocian/gwca integration (#141)
Setup memory scanner
Setup Focus View
Scan for player information and show in the Focus View
Setup Quest parsing
2023-02-27 17:22:04 +01:00
amacocianandGitHub d291d5825a #125 - Setup onboarding flow (#133)
* #125 - Setup onboarding flow
Closes #125

* Fix MenuService logging
2023-02-03 15:27:06 +00:00
amacocianandGitHub 9d6162f65f #131 - Downgrade to .net6 (#132) 2023-02-02 20:50:35 +00:00
amacocianandGitHub f54ab42a92 #129 - Change hashing algorithm to SHA512 (#130) 2023-02-02 18:31:07 +00:00
amacocianandGitHub 78c8ae3e8f #127 - Introduce Nullable Support (#128) 2023-02-02 18:05:10 +00:00
amacocianandGitHub c64830c5b6 #123 #113 #105 Build Templates Improvements (#124) 2023-01-21 21:35:16 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
561c7cf7b1 Bump microsoft/setup-msbuild from 1.0.1 to 1.1.3 (#111)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.0.1 to 1.1.3.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/master/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.0.1...v1.1.3)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 15:22:44 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2c6862de41 Bump coverlet.collector from 3.1.2 to 3.2.0 (#112)
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 3.1.2 to 3.2.0.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/commits/v3.2.0)

---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 15:10:18 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
69842cbadf Bump Microsoft.NET.Test.Sdk from 17.4.0-preview-20221003-03 to 17.4.0 (#116)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0-preview-20221003-03 to 17.4.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0-preview-20221003-03...v17.4.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-09 14:24:26 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c7327e7bb2 Bump Microsoft.NET.Test.Sdk (#108)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0-preview-20220726-02 to 17.4.0-preview-20221003-03.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0-preview-20220726-02...v17.4.0-preview-20221003-03)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 10:18:44 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
de33ed7fc4 Bump actions/setup-dotnet from 2 to 3 (#107)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 10:09:29 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
c99bb8d074 Bump Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28 (#109)
* Bump Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28

Bumps Microsoft.Web.WebView2 from 1.0.1343.22 to 1.0.1370.28.

---
updated-dependencies:
- dependency-name: Microsoft.Web.WebView2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update daybreak version

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandru Macocian <amacocian@yahoo.com>
2022-10-11 10:02:33 +00:00
amacocianandGitHub a98e54ebc6 Sync namespaces (#104) 2022-09-23 09:56:47 +00:00
amacocianandGitHub a832aa1925 Minor fixes (#102) 2022-09-21 13:55:37 +00:00
amacocianandGitHub c7eb5debcb Refresh access token (#100) 2022-09-20 11:46:36 +00:00
amacocianandGitHub 91f0cb4e31 Implement expandable menu view (#95) 2022-09-19 19:08:34 +00:00
amacocianandGitHub 5e8407a6ab Make main view maintain its state (#94)
* Make main view maintain its state

* Stop checking game state when mainview is not loaded
2022-09-15 17:05:30 +02:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
243c2c1536 Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2 (#91)
* Bump Microsoft.Extensions.Logging.Abstractions from 6.0.1 to 6.0.2

Bumps [Microsoft.Extensions.Logging.Abstractions](https://github.com/dotnet/runtime) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/dotnet/runtime/releases)
- [Commits](https://github.com/dotnet/runtime/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: Microsoft.Extensions.Logging.Abstractions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump version

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexandru Macocian <amacocian@yahoo.com>
2022-09-15 14:34:21 +00:00
amacocianandGitHub 930aca4ffe Bump dependencies (#90)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2022-09-12 18:11:36 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1639cabe8e Bump Slim from 1.7.3 to 1.8.1 (#86)
Bumps [Slim](https://github.com/AlexMacocian/Slim) from 1.7.3 to 1.8.1.
- [Release notes](https://github.com/AlexMacocian/Slim/releases)
- [Commits](https://github.com/AlexMacocian/Slim/commits)

---
updated-dependencies:
- dependency-name: Slim
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 08:38:17 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5961a57c5b Bump Microsoft.Web.WebView2 from 1.0.1293.44 to 1.0.1343.22 (#85)
Bumps Microsoft.Web.WebView2 from 1.0.1293.44 to 1.0.1343.22.

---
updated-dependencies:
- dependency-name: Microsoft.Web.WebView2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-07 10:29:18 +02:00
amacocianandGitHub 75d9bc6dec Add checkmark when builds are synchronized (#84) 2022-08-27 12:54:48 +00:00
amacocianandGitHub 4ccca32881 Build synchronization speedups (#82) 2022-08-27 12:15:21 +00:00
amacocianandGitHub a7f5d9dd96 Fix deletion error when installing (#80)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2022-08-25 20:55:01 +00:00
amacocianandGitHub c8ba125abc Allow individual uploads/downloads for backed up builds (#77) 2022-08-25 20:44:20 +00:00
1083 changed files with 70981 additions and 13625 deletions
+5 -2
View File
@@ -6,8 +6,8 @@ csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:error
csharp_prefer_simple_using_statement = true:error
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_namespace_declarations = file_scoped:error
csharp_style_prefer_method_group_conversion = true:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
@@ -46,6 +46,9 @@ csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_style_prefer_top_level_statements = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
[*.{cs,vb}]
#### Naming styles ####
+72 -21
View File
@@ -12,6 +12,11 @@ on:
paths:
- "Daybreak/**"
- "Daybreak.Installer/**"
- "Daybreak.API/**"
- "Daybreak.7ZipExtractor/**"
- "Daybreak.Installer/**"
- "Daybreak.Shared/**"
workflow_dispatch:
jobs:
@@ -19,7 +24,7 @@ jobs:
strategy:
matrix:
targetplatform: [x64]
targetplatform: [x86]
runs-on: windows-latest
@@ -32,7 +37,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -45,33 +50,33 @@ jobs:
run: |
$changeLog = git log --no-merges --pretty="%h - %s (%an)<br />" ${{ env.LatestReleaseTag }}..HEAD
echo "::set-env name=Changelog::$changeLog"
mkdir Publish
echo $changeLog > .\Publish\changelog.txt
env:
LatestReleaseTag: ${{steps.getLatestTag.outputs.tag}}
- name: Print changelog
run: |
echo "${{ env.Changelog }}"
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.x'
dotnet-version: '9.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
uses: microsoft/setup-msbuild@v2.0.0
- name: Setup project secrets
run: |
dotnet user-secrets --project Daybreak\Daybreak.csproj set AadApplicationId "${{ secrets.AadApplicationId }}"
dotnet user-secrets --project Daybreak\Daybreak.csproj set AadTenantId "${{ secrets.AadTenantId }}"
dotnet user-secrets --project Daybreak\Daybreak.csproj set ApmApiKey "${{ secrets.ApmApiKey }}"
dotnet user-secrets --project Daybreak\Daybreak.csproj set ApmUri "${{ secrets.ApmUri }}"
- name: Restore project
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier --property:SolutionDir=$GITHUB_WORKSPACE
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- name: Build Daybreak project
run: dotnet build Daybreak -c $env:Configuration
run: dotnet build Daybreak -c $env:Configuration --property:SolutionDir=$env:GITHUB_WORKSPACE
- name: Set version variable
run: |
@@ -79,12 +84,7 @@ jobs:
echo "::set-env name=Version::$version"
- name: Create publish launcher files
run: dotnet publish .\Daybreak\Daybreak.csproj -c $env:Configuration -r $env:RuntimeIdentifier -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained true -o .\Publish
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- name: Create publish installer files
run: dotnet publish .\Daybreak.Installer\Daybreak.Installer.csproj -c $env:Configuration -r $env:RuntimeIdentifier -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained true -o .\Publish
run: dotnet publish .\Daybreak\Daybreak.csproj -c $env:Configuration -r $env:RuntimeIdentifier --property:SolutionDir=$env:GITHUB_WORKSPACE -p:PublishReadyToRun=true -p:PublishSingleFile=false --self-contained true -o .\Publish
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
@@ -94,16 +94,67 @@ jobs:
.\Scripts\BuildRelease.ps1 -version $env:Version
shell: pwsh
- name: Publish release
uses: Xotl/cool-github-releases@v1.1.7
- name: Submit virustotal scan
shell: pwsh
run: |
cd Publish
$apiKey = "${{ secrets.VIRUSTOTAL_APIKEY }}"
$archiveName = "daybreakv${{ env.Version }}.zip"
$uploadUrlResponse = curl https://www.virustotal.com/api/v3/files/upload_url -H x-apikey:"$apiKey"
$uploadUrlResponse
$uploadUrlJson = $uploadUrlResponse | ConvertFrom-Json
$uploadUrl = $uploadUrlJson.data
$uploadResponse = curl --request POST --header x-apikey:"$apiKey" --header 'content-type: multipart/form-data' --form file="@$archiveName" --url $uploadUrl
$uploadResponse
$uploadResponseJson = $uploadResponse | ConvertFrom-Json
$resultsUrl = "https://www.virustotal.com/api/v3/analyses/$($uploadResponseJson.data.id)"
$resultsUrl
# Poll the results until the status is "completed"
$status = ""
while ($status -ne "completed") {
Start-Sleep -Seconds 10 # Wait for 10 seconds between checks
$results = curl --header x-apikey:"$apiKey" --url $resultsUrl
$results
$resultsJson = $results | ConvertFrom-Json
$status = $resultsJson.data.attributes.status
Write-Host "Current status: $status"
}
$resultsJson.data.attributes.results | ConvertTo-Json | Out-File -FilePath "VirusTotal.Results.json" -Encoding UTF8
$resultsJson.data.attributes.stats | ConvertTo-Json | Out-File -FilePath "VirusTotal.Stats.json" -Encoding UTF8
$stats = $resultsJson.data.attributes.stats
echo "::set-env name=VirusTotalStats::$stats"
- name: Publish blob files
run: |
Write-Host $env
.\Scripts\PushFilesToBlobStorage.ps1 -version $env:Version -sourcePath .\Publish\ -connectionString "${{ secrets.BLOBSTORAGE_CONNECTIONSTRING }}"
shell: pwsh
- name: Create release draft
uses: Xotl/cool-github-releases@v1.1.10
with:
mode: update
tag_name: v${{ env.Version }}
release_name: Daybreak v${{ env.Version }}
assets: .\Publish\daybreakv${{ env.Version }}.zip
assets: .\Publish\daybreakv${{ env.Version }}.zip;.\Publish\VirusTotal.Results.json;.\Publish\VirusTotal.Stats.json
github_token: ${{ env.GITHUB_TOKEN }}
replace_assets: true
body_mrkdwn: ${{ env.Changelog }}
body_mrkdwn: |
${{ env.Changelog }}
** VirustTotal Stats: **
```
${{ env.VirusTotalStats }}
```
isDraft: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish release
run: |
gh release edit v${{ env.Version }} --draft=false
shell: powershell
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+8 -10
View File
@@ -19,35 +19,33 @@ jobs:
strategy:
matrix:
targetplatform: [x64]
targetplatform: [x86]
runs-on: windows-latest
env:
Solution_Path: Daybreak.sln
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
Wpf_Project_Path: Daybreak\Daybreal.csproj
Wpf_Project_Path: Daybreak\Daybreak.csproj
Actions_Allow_Unsecure_Commands: true
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.x'
dotnet-version: '9.x'
architecture: x86
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
- name: Execute Unit Tests
run: dotnet test $env:Test_Project_Path
uses: microsoft/setup-msbuild@v2.0.0
- name: Restore the Wpf application to populate the obj folder
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier --property:SolutionDir=$env:GITHUB_WORKSPACE
env:
Configuration: Debug
RuntimeIdentifier: win-${{ matrix.targetplatform }}
+20 -8
View File
@@ -36,13 +36,22 @@ jobs:
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
env:
Configuration: Release
Solution_Path: Daybreak.sln
Test_Project_Path: Daybreak.Tests\Daybreak.Tests.csproj
Wpf_Project_Path: Daybreak\Daybreak.csproj
Actions_Allow_Unsecure_Commands: true
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,12 +66,12 @@ jobs:
# If this step fails, then you should remove it and run the build manually (see below)
- name: Install .NET Core
uses: actions/setup-dotnet@v2
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.x'
dotnet-version: '9.x'
- name: Autobuild
uses: github/codeql-action/autobuild@v2
#- name: Autobuild
# uses: github/codeql-action/autobuild@v2
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -74,5 +83,8 @@ jobs:
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Build Daybreak project
run: dotnet build Daybreak -c $env:Configuration --property:SolutionDir=$env:GITHUB_WORKSPACE
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
+9 -4
View File
@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
targetplatform: [x64]
targetplatform: [x86]
runs-on: windows-latest
@@ -25,16 +25,21 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.x'
- name: Get Latest Tag
id: getLatestTag
uses: WyriHaximus/github-action-get-previous-tag@v1
- name: Build Daybreak project
run: dotnet build Daybreak -c $env:Configuration
run: dotnet build Daybreak -c $env:Configuration --property:SolutionDir=$env:GITHUB_WORKSPACE
- name: Set version variable
run: |
+4 -1
View File
@@ -28,6 +28,7 @@ x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
build/
[Bb]in/
[Oo]bj/
[Oo]ut/
@@ -361,4 +362,6 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
.idea
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<RootNamespace>Daybreak._7ZipExtractor</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpCompress" />
</ItemGroup>
</Project>
+47
View File
@@ -0,0 +1,47 @@
/*
* Extractor is in a different executable because of memory limitations. Daybreak needs to be compiled for x86 and as such has a memory limit of 2GB.
* 7Zip algorithm may require more than 2GB memory to unpack large zip files.
* As such, Daybreak.7ZipExtractor is x64.
*/
using SharpCompress.Archives;
if (args.Length != 2)
{
return -1;
}
var archivePath = args[0];
var destinationDirectory = args[1];
using var fileStream = new FileStream(archivePath, FileMode.Open);
using var archive = ArchiveFactory.Open(fileStream);
var progress = 0d;
var count = archive.Entries.Count();
var reader = archive.ExtractAllEntries();
while (reader.MoveToNextEntry())
{
var entry = reader.Entry;
if (entry.Key is null)
{
continue;
}
if (entry.IsDirectory)
{
var directoryName = Path.Combine(Path.GetFullPath(destinationDirectory), entry.Key);
Directory.CreateDirectory(directoryName);
continue;
}
var fileName = Path.Combine(Path.GetFullPath(destinationDirectory), entry.Key);
using var entryStream = new FileStream(fileName, FileMode.Create);
using (var stream = reader.OpenEntryStream())
{
stream.CopyTo(entryStream);
}
progress += 1d / count;
Console.WriteLine($"{progress} {fileName}");
}
return 0;
+11
View File
@@ -0,0 +1,11 @@
namespace Daybreak.API.Configuration;
public static class BuildInfo
{
public const string Configuration =
#if DEBUG
"Debug";
#else
"Release";
#endif
}
@@ -0,0 +1,31 @@
namespace Daybreak.API.Configuration;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithConfiguration(this WebApplicationBuilder builder)
{
var config = BuildInfo.Configuration;
var appSettingsStream = GetManifestResourceStream($"Daybreak.API.Configuration.appsettings.json") ?? throw new InvalidOperationException("Failed to load appsettings.json");
var appSettingsConfigStream = GetManifestResourceStream($"Daybreak.API.Configuration.appsettings.{config}.json") ?? throw new InvalidOperationException($"Failed to load appsettings.{config}.json");
builder.Configuration
.AddJsonStream(appSettingsStream)
.AddJsonStream(appSettingsConfigStream)
.AddEnvironmentVariables();
builder.WebHost.UseSetting(WebHostDefaults.PreventHostingStartupKey, "true");
return builder;
}
private static Stream? GetManifestResourceStream(string name)
{
var assembly = typeof(WebApplicationBuilderExtensions).Assembly;
var resourceName = assembly.GetManifestResourceNames()
.FirstOrDefault(n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
if (resourceName is null)
{
return default;
}
return assembly.GetManifestResourceStream(resourceName);
}
}
@@ -0,0 +1,2 @@
{
}
@@ -0,0 +1,10 @@
{
"Logging": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning"
}
}
}
}
@@ -0,0 +1,2 @@
{
}
@@ -0,0 +1,13 @@
{
"Logging": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Debug"
}
}
},
"Correlation": {
"Header": "X-Correlation-Vector"
}
}
@@ -0,0 +1,51 @@
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Services;
using Daybreak.Shared.Models.Api;
using Microsoft.AspNetCore.Mvc;
using Net.Sdk.Web;
using System.Core.Extensions;
namespace Daybreak.API.Controllers.Rest;
[GenerateController("api/v1/rest/character-select")]
[Tags("Character Select")]
public sealed class CharacterSelectController(
CharacterSelectService characterSelectService)
{
private readonly CharacterSelectService characterSelectService = characterSelectService.ThrowIfNull();
[GenerateGet]
[EndpointName("GetCharacterSelectInformation")]
[EndpointSummary("Get Character Select Information")]
[EndpointDescription("Get the character select information for the current player. Returns a serialized CharacterSelectInformation object")]
[ProducesResponseType<CharacterSelectInformation>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetCharacterSelectInformation(
CancellationToken cancellationToken)
{
var characterSelectInfo = await this.characterSelectService.GetCharacterSelectInformation(cancellationToken);
return characterSelectInfo is null ?
Results.StatusCode(StatusCodes.Status503ServiceUnavailable) :
Results.Ok(characterSelectInfo);
}
[GeneratePost("{identifier}")]
[EndpointName("ChangeCharacter")]
[EndpointSummary("Change Character by identifier")]
[EndpointDescription("Logs out from the current character and tries to log in with the desired character. Identifier can be either uuid or character name")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> ChangeCharacter(
[FromRoute] string identifier,
CancellationToken cancellationToken)
{
var isUuid = Uuid.TryParse(identifier, out _);
var result = isUuid
? await this.characterSelectService.ChangeCharacterByUuid(identifier, cancellationToken)
: await this.characterSelectService.ChangeCharacterByName(identifier, cancellationToken);
return result ?
Results.Ok() :
Results.StatusCode(StatusCodes.Status500InternalServerError);
}
}
@@ -0,0 +1,30 @@
using Daybreak.API.Services;
using Daybreak.Shared.Models.Api;
using Microsoft.AspNetCore.Mvc;
using Net.Sdk.Web;
using System.Core.Extensions;
namespace Daybreak.API.Controllers.Rest;
[GenerateController("api/v1/rest/login")]
[Tags("Login")]
public sealed class LoginController(
LoginService loginService)
{
private readonly LoginService loginService = loginService.ThrowIfNull();
[GenerateGet]
[EndpointName("GetLoginInfo")]
[EndpointSummary("Get Login Information")]
[EndpointDescription("Get the current login information. Returns a serialized LoginInfo object")]
[ProducesResponseType<LoginInfo>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetLoginInfo(
CancellationToken cancellationToken)
{
var loginInfo = await this.loginService.GetLoginInformation(cancellationToken);
return loginInfo is null ?
Results.StatusCode(StatusCodes.Status503ServiceUnavailable) :
Results.Ok(loginInfo);
}
}
@@ -0,0 +1,114 @@
using Daybreak.API.Services;
using Daybreak.Shared.Models.Api;
using Microsoft.AspNetCore.Mvc;
using Net.Sdk.Web;
using System.Core.Extensions;
namespace Daybreak.API.Controllers.Rest;
[GenerateController("api/v1/rest/main-player")]
[Tags("Main Player")]
public sealed class MainPlayerController(
MainPlayerService mainPlayerService,
ILogger<MainPlayerController> logger)
{
private readonly MainPlayerService mainPlayerService = mainPlayerService.ThrowIfNull();
private readonly ILogger<MainPlayerController> logger = logger.ThrowIfNull();
[GenerateGet("state")]
[EndpointName("GetMainPlayerState")]
[EndpointSummary("Get the current MainPlayerState")]
[EndpointDescription("Get the current MainPlayerState. Returns a json serialized MainPlayerState object. You can use the websocket endpoint to subscribe to MainPlayerState updates on each game thread proc")]
[ProducesResponseType<MainPlayerState>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerState(CancellationToken cancellationToken)
{
var mainPlayerState = await this.mainPlayerService.GetMainPlayerState(cancellationToken);
return mainPlayerState is not null ? Results.Ok(mainPlayerState) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("quest-log")]
[EndpointName("GetQuestLog")]
[EndpointSummary("Get the current Quest Log")]
[EndpointDescription("Get the current Quest Log. Returns a json serialized QuestLogInformation object")]
[ProducesResponseType<QuestLogInformation>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetQuestLog(CancellationToken cancellationToken)
{
var questLog = await this.mainPlayerService.GetQuestLog(cancellationToken);
return questLog is not null ? Results.Ok(questLog) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("info")]
[EndpointName("GetMainPlayerInformation")]
[EndpointSummary("Get the current MainPlayerInformation")]
[EndpointDescription("Get the current MainPlayerInformation. Returns a json serialized MainPlayerInformation object")]
[ProducesResponseType<MainPlayerInformation>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerInformation(CancellationToken cancellationToken)
{
var mainPlayerInformation = await this.mainPlayerService.GetMainPlayerInformation(cancellationToken);
return mainPlayerInformation is not null ? Results.Ok(mainPlayerInformation) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("build")]
[EndpointName("GetMainPlayerBuild")]
[EndpointSummary("Get the current build")]
[EndpointDescription("Get the current build. Returns a json serialized BuildEntry object")]
[ProducesResponseType<BuildEntry>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerBuild(CancellationToken cancellationToken)
{
var build = await this.mainPlayerService.GetCurrentBuild(cancellationToken);
return build is not null ? Results.Ok(build) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GeneratePost("build")]
[EndpointName("SetMainPlayerBuild")]
[EndpointSummary("Set the current build")]
[EndpointDescription("Set the current build. Returns 200 is the operation has succeeded")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> SetMainPlayerBuild([FromQuery(Name = "code")] string? code, CancellationToken cancellationToken)
{
var result = await this.mainPlayerService.SetCurrentBuild(code ?? string.Empty, cancellationToken);
return result ? Results.Ok() : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("instance-info")]
[EndpointName("GetMainPlayerInstanceInfo")]
[EndpointSummary("Get the current instance info")]
[EndpointDescription("Get the current instance info. Returns a json serialized MainPlayerInstanceInfo object")]
[ProducesResponseType<InstanceInfo>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerInstanceInfo(CancellationToken cancellationToken)
{
var instanceInfo = await this.mainPlayerService.GetMainPlayerInstance(cancellationToken);
return instanceInfo is not null ? Results.Ok(instanceInfo) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("title")]
[EndpointName("GetMainPlayerTitleInfo")]
[EndpointSummary("Get the current title info")]
[EndpointDescription("Get the current title info. Returns a json serialized TitleInfo object")]
[ProducesResponseType<TitleInfo>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerTitleInfo(CancellationToken cancellationToken)
{
var titleInfo = await this.mainPlayerService.GetTitleInfo(cancellationToken);
return titleInfo is not null ? Results.Ok(titleInfo) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GenerateGet("build-context")]
[EndpointName("GetMainPlayerBuildContext")]
[EndpointSummary("Get the current build context")]
[EndpointDescription("Get the current build context. Returns a json serialized MainPlayerBuildContext object")]
[ProducesResponseType<BuildEntry>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetMainPlayerBuildContext(CancellationToken cancellationToken)
{
var context = await this.mainPlayerService.GetMainPlayerBuildContext(cancellationToken);
return context is not null ? Results.Ok(context) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
}
@@ -0,0 +1,38 @@
using Daybreak.API.Services;
using Daybreak.Shared.Models.Api;
using Microsoft.AspNetCore.Mvc;
using Net.Sdk.Web;
namespace Daybreak.API.Controllers.Rest;
[GenerateController("api/v1/rest/party")]
[Tags("Party")]
public sealed class PartyController(PartyService partyService)
{
private readonly PartyService partyService = partyService;
[GenerateGet("loadout")]
[EndpointName("GetPartyLoadout")]
[EndpointSummary("Get the current party loadout")]
[EndpointDescription("Get the current party loadout. Returns a json serialized PartyLoadout object")]
[ProducesResponseType<PartyLoadout>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> GetPartyLoadout(
CancellationToken cancellationToken)
{
var partyLoadout = await this.partyService.GetPartyLoadout(cancellationToken);
return partyLoadout is not null ? Results.Ok(partyLoadout) : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
[GeneratePost("loadout")]
[EndpointName("SetPartyLoadout")]
[EndpointSummary("Set the current party loadout")]
[EndpointDescription("Set the current party loadout")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<IResult> SetPartyLoadout([FromBody] PartyLoadout partyLoadout, CancellationToken cancellationToken)
{
var result = await this.partyService.SetPartyLoadout(partyLoadout, cancellationToken);
return result ? Results.Ok() : Results.StatusCode(StatusCodes.Status503ServiceUnavailable);
}
}
@@ -0,0 +1,67 @@
using Daybreak.API.Models;
using Daybreak.API.Services;
using Daybreak.API.WebSockets;
using Daybreak.Shared.Models.Api;
using System.Buffers;
using System.Core.Extensions;
using System.Extensions.Core;
using System.Net.WebSockets;
namespace Daybreak.API.Controllers.WebSocket.MainPlayer;
public sealed class MainPlayerStateRoute(
MainPlayerService mainPlayerStateService,
ChatService chatService,
ILogger<MainPlayerStateRoute> logger)
: UpdateWebSocketRoute
{
private const int DefaultFrequency = 1000;
private readonly MainPlayerService mainPlayerStateService = mainPlayerStateService.ThrowIfNull();
private readonly ChatService chatService = chatService.ThrowIfNull();
private readonly ILogger<MainPlayerStateRoute> logger = logger.ThrowIfNull();
private CallbackRegistration? mainPlayerStateRegistration;
protected override int BufferSize => 256;
public override Task ExecuteAsync(WebSocketMessageType type, ReadOnlySequence<byte> data, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public override async Task SocketAccepted(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
if (!int.TryParse(this.Context?.Request.Query["f"].FirstOrDefault(), out var freq))
{
freq = DefaultFrequency;
}
var freqTimeSpan = TimeSpan.FromMilliseconds(freq);
scopedLogger.LogInformation("WebSocket accepted {id} with frequency {frequency}", this.Context?.Connection.Id ?? string.Empty, freqTimeSpan);
this.mainPlayerStateRegistration = this.mainPlayerStateService.RegisterMainStateConsumer(freqTimeSpan, this.SendUpdate);
await this.chatService.AddMessageAsync(
message: $"{nameof(MainPlayerState)} subscriber added: {this.Context?.Connection.RemoteIpAddress?.ToString()}:{this.Context?.Connection.RemotePort} with frequency {freq}ms",
sender: "Daybreak.API",
channel: Channel.Whisper,
cancellationToken: cancellationToken);
}
public override async Task SocketClosed()
{
var scopedLogger = this.logger.CreateScopedLogger();
this.mainPlayerStateRegistration?.Dispose();
this.mainPlayerStateRegistration = default;
scopedLogger.LogInformation("WebSocket closed {id}", this.Context?.Connection.Id ?? string.Empty);
await this.chatService.AddMessageAsync(
message: $"{nameof(MainPlayerState)} subscriber removed: {this.Context?.Connection.RemoteIpAddress?.ToString()}:{this.Context?.Connection.RemotePort}",
sender: "Daybreak.API",
channel: Channel.Whisper,
cancellationToken: CancellationToken.None);
}
}
@@ -0,0 +1,19 @@
using Daybreak.API.Interop;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Daybreak.API.Converters;
public sealed class PointerValueConverter : JsonConverter<PointerValue>
{
public override PointerValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var s = reader.GetString() ?? throw new JsonException("Expected string");
return PointerValue.Parse(s);
}
public override void Write(Utf8JsonWriter writer, PointerValue value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
+64
View File
@@ -0,0 +1,64 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<StripSymbols>true</StripSymbols>
<InteropExports>true</InteropExports>
<EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator>
<PublishAot>true</PublishAot>
<SelfContained>true</SelfContained>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
<UseAppHost>false</UseAppHost>
<EnableIISSupport>false</EnableIISSupport>
<InvariantGlobalization>true</InvariantGlobalization>
<Platforms>x86</Platforms>
</PropertyGroup>
<PropertyGroup>
<!--Ignore trim warnings. NativeAOT support is still experimental so it should be tested with a Publish build generated from Daybreak build-->
<NoWarn>IL2104</NoWarn>
<NoWarn>IL3053</NoWarn>
<!--MinHook.NET does not contain sources for .NET, only .Net Framework. This has no impact on the functionality of the project-->
<NoWarn>NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Configuration\appsettings.Debug.json" />
<EmbeddedResource Include="Configuration\appsettings.json" />
<EmbeddedResource Include="Configuration\appsettings.Release.json" />
</ItemGroup>
<ItemGroup>
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
<PackageReference Include="MinHook.NET" />
<PackageReference Include="Net.Sdk.Web.Extensions" />
<PackageReference Include="Net.Sdk.Web.Extensions.SourceGenerators" />
<PackageReference Include="PeNet" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" />
<PackageReference Include="System.Private.Uri" />
<PackageReference Include="SystemExtensions.NetStandard.Generators" PrivateAssets="all" />
<PackageReference Include="ZLinq" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Daybreak.Shared\Daybreak.Shared.csproj" />
</ItemGroup>
</Project>
+145
View File
@@ -0,0 +1,145 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Extensions.Core;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using Daybreak.API.Configuration;
using Daybreak.API.Extensions;
using Daybreak.API.Health;
using Daybreak.API.Hosting;
using Daybreak.API.Logging;
using Daybreak.API.Serialization;
using Daybreak.API.Services;
using Daybreak.API.Swagger;
using Daybreak.API.WebSockets;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Net.Sdk.Web;
namespace Daybreak.API;
public class EntryPoint
{
private const int StartPort = 5080;
private static readonly TimeSpan InitializationTimeout = TimeSpan.FromSeconds(5);
private static readonly CancellationTokenSource CancellationTokenSource = new();
[UnmanagedCallersOnly(EntryPoint = "ThreadInit"), STAThread]
[RequiresUnreferencedCode("The handler uses a static method that gets referenced, so there's no unreferenced code to worry about")]
[RequiresDynamicCode("The handler uses a static method, so there's no dynamic code to worry about")]
public static int ThreadInit(IntPtr _, int __)
{
Environment.SetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", null, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("ASPNETCORE_PREVENTHOSTINGSTARTUP", "true", EnvironmentVariableTarget.Process);
#if DEBUG
ConsoleExtensions.AllocateAnsiConsole();
#endif
var port = FindAvailablePort(StartPort);
var app = CreateApplication(port);
var runTask = Task.Run(() => StartServer(app), CancellationTokenSource.Token);
var scopedLogger = app.Services.GetRequiredService<ILogger<EntryPoint>>().CreateScopedLogger();
var healthCheck = app.Services.GetRequiredService<HealthCheckService>();
var sw = Stopwatch.StartNew();
var healthy = false;
while (sw.Elapsed < InitializationTimeout)
{
var status = Task.Run(() => healthCheck.CheckHealthAsync()).Result;
scopedLogger.LogWarning("HealthCheck status: {status} in {duration}. Report: {report}", status.Status, status.TotalDuration, string.Join("\n", status.Entries.Select(e => $"{e.Key}: {e.Value.Status}")));
if (status.Status is HealthStatus.Healthy)
{
healthy = true;
break;
}
else
{
Thread.Sleep(100);
}
}
if (healthy)
{
scopedLogger.LogInformation("Daybreak API is healthy on port {port}. Initialization succeeded in {duration}", port, sw.Elapsed);
return port;
}
else
{
scopedLogger.LogError("Daybreak API failed to initialize in {duration}", sw.Elapsed);
CancellationTokenSource.Cancel();
return -1;
}
}
[RequiresUnreferencedCode("The handler uses a static method that gets referenced, so there's no unreferenced code to worry about")]
[RequiresDynamicCode("The handler uses a static method, so there's no dynamic code to worry about")]
private static WebApplication CreateApplication(int port)
{
var builder = WebApplication.CreateBuilder()
.WithConfiguration()
.WithHosting(port)
.WithSwagger()
.WithSerializationContext()
.WithLogging()
.WithDaybreakServices()
.WithWebSocketRoutes()
.WithRoutes()
.WithHealthChecks();
builder.Services.AddOpenApi();
var app = builder.Build();
app.UseWebSockets(new WebSocketOptions { KeepAliveInterval = TimeSpan.FromSeconds(30) });
return app
.UseHealthChecks()
.UseLogging()
.UseRoutes()
.UseWebSocketRoutes()
.UseSwaggerWithUI();
}
private static async Task StartServer(WebApplication app)
{
try
{
await app.RunAsync();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
}
private static int FindAvailablePort(int startPort)
{
var port = startPort;
while (!IsPortAvailable(port))
{
port++;
if (port > 65535 || port < 0)
{
return -1;
}
}
return port;
}
private static bool IsPortAvailable(int port)
{
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
var tcpListeners = ipProperties.GetActiveTcpListeners();
if (tcpListeners.Any(endpoint => endpoint.Port == port))
{
return false;
}
var tcpConnections = ipProperties.GetActiveTcpConnections();
if (tcpConnections.Any(conn => conn.LocalEndPoint.Port == port))
{
return false;
}
return true;
}
}
@@ -0,0 +1,17 @@
using Daybreak.API.Interop.GuildWars;
using Daybreak.Shared.Models.Api;
using ZLinq;
namespace Daybreak.API.Extensions;
public static class AttributeContextExtensions
{
public static List<AttributeEntry> GetAttributeEntryList(this AttributeContext[] attributes)
{
return attributes
.AsValueEnumerable()
.Take(45) // There are only 45 maximum attributes in game
.Where(a => a.LevelBase > 0 && a.Level >= a.LevelBase) // Select only attributes with points in them
.Select(a => new AttributeEntry(a.Id, a.LevelBase, a.Level)).ToList();
}
}
@@ -0,0 +1,19 @@
namespace Daybreak.API.Extensions;
public static class ConsoleExtensions
{
public static void AllocateAnsiConsole()
{
NativeMethods.AllocConsole();
var handle = NativeMethods.GetStdHandle(NativeMethods.STD_OUTPUT_HANDLE);
if (!NativeMethods.GetConsoleMode(handle, out var mode))
{
Console.WriteLine("Failed to get console mode");
}
if (!NativeMethods.SetConsoleMode(handle, mode | NativeMethods.ENABLE_VIRTUAL_TERMINAL_PROCESSING | NativeMethods.ENABLE_PROCESSED_OUTPUT))
{
Console.WriteLine("Failed to enable virtual terminal processing");
}
}
}
@@ -0,0 +1,101 @@
using Daybreak.API.Interop.GuildWars;
using System.Diagnostics.CodeAnalysis;
namespace Daybreak.API.Extensions;
public static unsafe class GameContextExtensions
{
public static bool TryGetPlayerId(
this WrappedPointer<GameContext> gameContext,
[NotNullWhen(true)] out uint? playerId)
{
playerId = 0;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null ||
gameContext.Pointer->WorldContext->PlayerControlledChar is null)
{
return false;
}
playerId = gameContext.Pointer->WorldContext->PlayerControlledChar->AgentId;
return true;
}
public static bool TryGetBuildContext(
this WrappedPointer<GameContext> gameContext,
[NotNullWhen(true)] out GuildWarsArray<SkillbarContext>? skillbars,
[NotNullWhen(true)] out GuildWarsArray<PartyAttribute>? attributes,
[NotNullWhen(true)] out GuildWarsArray<ProfessionsContext>? professions,
[NotNullWhen(true)] out GuildWarsArray<uint>? unlockedSkills)
{
skillbars = default;
attributes = default;
professions = default;
unlockedSkills = default;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
{
return false;
}
skillbars = gameContext.Pointer->WorldContext->Skillbars;
attributes = gameContext.Pointer->WorldContext->Attributes;
professions = gameContext.Pointer->WorldContext->Professions;
unlockedSkills = gameContext.Pointer->WorldContext->UnlockedCharacterSkills;
return true;
}
public static bool TryGetPlayerParty(
this WrappedPointer<GameContext> gameContext,
[NotNullWhen(true)] out uint? partyId,
[NotNullWhen(true)] out GuildWarsArray<PlayerPartyMember>? players,
[NotNullWhen(true)] out GuildWarsArray<HeroPartyMember>? heroes,
[NotNullWhen(true)] out GuildWarsArray<HenchmanPartyMember>? henchmen)
{
partyId = default;
players = default;
heroes = default;
henchmen = default;
if (gameContext.IsNull ||
gameContext.Pointer->PartyContext is null)
{
return false;
}
partyId = gameContext.Pointer->PartyContext->PlayerParty->PartyId;
players = gameContext.Pointer->PartyContext->PlayerParty->Players;
heroes = gameContext.Pointer->PartyContext->PlayerParty->Heroes;
henchmen = gameContext.Pointer->PartyContext->PlayerParty->Henchmen;
return true;
}
public static bool TryGetHeroFlags(
this WrappedPointer<GameContext> gameContext,
[NotNullWhen(true)]out GuildWarsArray<HeroFlag>? heroFlags)
{
heroFlags = default;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
{
return false;
}
heroFlags = gameContext.Pointer->WorldContext->HeroFlags;
return true;
}
public static bool TryGetAccountContext(
this WrappedPointer<GameContext> gameContext,
out WrappedPointer<AccountGameContext> accountContext)
{
accountContext = default;
if (gameContext.IsNull ||
gameContext.Pointer->AccountContext is null)
{
return false;
}
accountContext = gameContext.Pointer->AccountContext;
return true;
}
}
+31
View File
@@ -0,0 +1,31 @@
using Daybreak.API.Serialization;
using Daybreak.API.Services.Interop;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Text.Json;
using ZLinq;
namespace Daybreak.API.Health;
public sealed class AddressHealthCheck(IEnumerable<IAddressHealthService> addressHealthServices)
: IHealthCheck
{
private readonly IEnumerable<IAddressHealthService> addressHealthServices = addressHealthServices;
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
var states = this.addressHealthServices.AsValueEnumerable().SelectMany(addressService => addressService.GetAddressStates());
var healthy = !states.Any(s => s.Address == 0x0);
using var statesArray = states.ToArrayPool();
var serialized = JsonSerializer.SerializeToElement(statesArray.ArraySegment, ApiJsonSerializerContext.Default.ArraySegmentAddressState);
var meta = new Dictionary<string, object>
{
["Count"] = statesArray.Size,
["Invalid"] = states.Count(s => s.Address == 0x0),
["States"] = serialized,
};
return Task.FromResult(healthy
? HealthCheckResult.Healthy("Addresses are initialized", meta)
: HealthCheckResult.Unhealthy("Addresses are not initialized", data: meta));
}
}
+31
View File
@@ -0,0 +1,31 @@
using Daybreak.API.Serialization;
using Daybreak.API.Services.Interop;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Text.Json;
using ZLinq;
namespace Daybreak.API.Health;
public sealed class HooksHealthCheck(IEnumerable<IHookHealthService> hookHealthServices)
: IHealthCheck
{
private readonly IEnumerable<IHookHealthService> hookHealthServices = hookHealthServices;
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
var states = this.hookHealthServices.AsValueEnumerable().SelectMany(hookService => hookService.GetHookStates());
var healthy = !states.Any(s => !s.Hooked);
var statesArray = states.ToArrayPool();
var serialized = JsonSerializer.SerializeToElement(statesArray.ArraySegment, ApiJsonSerializerContext.Default.ArraySegmentHookState);
var meta = new Dictionary<string, object>
{
["Count"] = statesArray.Size,
["Unhooked"] = states.Count(s => !s.Hooked),
["States"] = serialized,
};
return Task.FromResult(healthy
? HealthCheckResult.Healthy("Hooks are initialized", meta)
: HealthCheckResult.Unhealthy("Hooks are not initialized", data: meta));
}
}
@@ -0,0 +1,12 @@
namespace Daybreak.API.Health;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithHealthChecks(this WebApplicationBuilder builder)
{
builder.Services.AddHealthChecks()
.AddCheck<HooksHealthCheck>(nameof(HooksHealthCheck))
.AddCheck<AddressHealthCheck>(nameof(AddressHealthCheck));
return builder;
}
}
@@ -0,0 +1,48 @@
using Daybreak.API.Models;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using ZLinq;
namespace Daybreak.API.Health;
public static class WebApplicationExtensions
{
[RequiresUnreferencedCode("The handler uses a static method that gets referenced, so there's no unreferenced code to worry about")]
[RequiresDynamicCode("The handler uses a static method, so there's no dynamic code to worry about")]
public static WebApplication UseHealthChecks(this WebApplication app)
{
app.MapGet("/api/v1/rest/health", static async (HealthCheckService healthCheckService, CancellationToken cancellationToken) =>
{
var report = await healthCheckService.CheckHealthAsync(cancellationToken);
var response = new HealthCheckResponse
{
ProcessId = Environment.ProcessId,
Status = report.Status,
TotalDuration = report.TotalDuration,
Entries = report.Entries.ToDictionary(
e => e.Key,
e => new HealthCheckEntryResponse
{
Status = e.Value.Status,
Description = e.Value.Description ?? string.Empty,
Tags = [.. e.Value.Tags],
Data = e.Value.Data
.Where(kvp => kvp.Value is JsonElement je)
.ToDictionary(kvp => kvp.Key,
kvp => (JsonElement)kvp.Value)
})
};
return Results.Json(response);
})
.WithName("Health")
.WithSummary("Service health")
.WithDescription($"Current health status of the service. Returns a serialized object of type {nameof(HealthCheckResponse)}")
.WithTags("Service")
.Produces<HealthCheckResponse>(StatusCodes.Status200OK)
.WithOpenApi();
return app;
}
}
@@ -0,0 +1,11 @@
namespace Daybreak.API.Hosting;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithHosting(this WebApplicationBuilder builder, int port)
{
builder.WebHost.ConfigureKestrel(o => o.ListenAnyIP(port));
return builder;
}
}
+23
View File
@@ -0,0 +1,23 @@
#pragma warning disable CS0436 // Type conflicts with imported type
using System.Extensions;
[assembly: GenerateFixedArray<uint>(Size = 4)]
[assembly: GenerateFixedArray<char>(Size = 0x14)]
[assembly: GenerateFixedArray<char>(Size = 0x40)]
[assembly: GenerateFixedArray<byte>(Size = 0x18)]
[assembly: GenerateFixedArray<char>(Size = 256)]
[assembly: GenerateFixedArray<char>(Size = 32)]
[assembly: GenerateFixedArray<char>(Size = 5)]
[assembly: GenerateFixedArray<char>(Size = 8)]
[assembly: GenerateFixedArray<byte>(Size = 56)]
[assembly: GenerateFixedArray<uint>(Size = 17)]
[assembly: GenerateFixedArray<uint>(Size = 12)]
[assembly: GenerateFixedArray<uint>(Size = 13)]
[assembly: GenerateFixedArray<uint>(Size = 8)]
#pragma warning restore CS0436 // Type conflicts with imported type
namespace Daybreak.API.Interop;
public static class FixedArrays
{
}
+26
View File
@@ -0,0 +1,26 @@
using System.Core.Extensions;
namespace Daybreak.API.Interop;
public sealed class GWAddressCache(Func<nuint> provider)
{
private readonly Func<nuint> provider = provider.ThrowIfNull();
private nuint? cachedAddress;
public nuint? GetAddress()
{
if (this.cachedAddress.HasValue)
{
return this.cachedAddress.Value;
}
var addr = this.provider();
if (addr is 0x0)
{
return null;
}
this.cachedAddress = addr;
return addr;
}
}
+19
View File
@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
public sealed class GWDelegateCache<TDelegate>(GWAddressCache cache)
where TDelegate : Delegate
{
public GWAddressCache Cache { get; } = cache;
public TDelegate? GetDelegate()
{
if (this.Cache.GetAddress() is not nuint address)
{
return default;
}
return Marshal.GetDelegateForFunctionPointer<TDelegate>((nint)address);
}
}
+240
View File
@@ -0,0 +1,240 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
public abstract class GWFastCall(GWAddressCache target)
{
public readonly struct Void { }
public GWAddressCache Cache { get; } = target;
public abstract void Initialize();
protected unsafe static nint BuildFastCallThunk(nuint target)
{
if (target is 0)
{
return 0;
}
var code = stackalloc byte[13]
{
0x58, // pop eax (ret)
0x59, // pop ecx (ctx)
0x5A, // pop edx (edxVal)
0x5B, // pop ebx (wparam)
0x53, // push ebx (wparam)
0x50, // push eax (ret)
0xB8,0,0,0,0, // mov eax, TARGET
0xFF,0xE0 // jmp eax
}; // 13 bytes total
const int TARGET_OFFSET = 7;
Unsafe.WriteUnaligned(ref code[TARGET_OFFSET], (uint)target);
var mem = NativeMethods.VirtualAlloc(0, 13, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE);
if (mem is 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "VirtualAlloc");
}
Buffer.MemoryCopy(code, (void*)mem, 13, 13);
return mem;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T3? Invoke(T1 t1, T2 t2)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T3) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, void>)this.func)(t1, t2);
return default;
}
return this.func(t1, t2);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T4? Invoke(T1 t1, T2 t2, T3 t3)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T4) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, void>)this.func)(t1, t2, t3);
return default;
}
return this.func(t1, t2, t3);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T5? Invoke(T1 t1, T2 t2, T3 t3, T4 t4)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T5) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, void>)this.func)(t1, t2, t3, t4);
return default;
}
return this.func(t1, t2, t3, t4);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5, T6>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T6? Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T6) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, void>)this.func)(t1, t2, t3, t4, t5);
return default;
}
return this.func(t1, t2, t3, t4, t5);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5, T6, T7>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, T7> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T7? Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T7) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, void>)this.func)(t1, t2, t3, t4, t5, t6);
return default;
}
return this.func(t1, t2, t3, t4, t5, t6);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, T7>)call;
}
}
+122
View File
@@ -0,0 +1,122 @@
using System.Runtime.InteropServices;
using MinHook;
namespace Daybreak.API.Interop;
/// <summary>
/// A thin convenience wrapper around MinHook that
/// resolves the target address lazily through <see cref="GWAddressCache"/>
/// unwraps an existing “CALL/JMP rel32” stub so we patch the real code
/// exposes <see cref="Original"/> so your detour can chain
/// </summary>
public sealed class GWHook<T>(
GWAddressCache funcAddress,
T detour,
bool bypassPreviousHooks = false) : IHook<T>
where T : Delegate
{
private readonly bool bypassPreviousHooks = bypassPreviousHooks;
private readonly GWAddressCache addressCache = funcAddress ?? throw new ArgumentNullException(nameof(funcAddress));
private readonly T detour = detour ?? throw new ArgumentNullException(nameof(detour));
private readonly SemaphoreSlim semaphore = new(1, 1);
private T? cont; // trampoline returned by MinHook
private HookEngine? engine;
/// <summary>Delegate that calls the next hook / real function.</summary>
public T Continue => this.cont ??
throw new InvalidOperationException("Hook not initialised call EnsureInitialized() first.");
public bool Hooked { get; private set; } = false;
public nuint TargetAddress { get; private set; }
public nuint ContinueAddress { get; private set; }
public nuint DetourAddress { get; private set; }
/// <summary>Installs the hook exactly once (thread-safe).</summary>
public bool EnsureInitialized()
{
if (this.Hooked)
{
return true;
}
this.semaphore.Wait();
try
{
if (this.Hooked)
{
return true;
}
var addr = this.addressCache.GetAddress();
if (addr.HasValue is false ||
addr.Value is 0)
{
return false;
}
var target = this.bypassPreviousHooks
? UnwrapNearBranch(addr.Value)
: addr.Value;
if (target is 0)
{
return false;
}
this.engine = new HookEngine();
this.cont = this.engine.CreateHook((nint)target, this.detour);
this.engine.EnableHooks();
this.TargetAddress = target;
this.ContinueAddress = (nuint)this.cont.Method.MethodHandle.GetFunctionPointer();
this.DetourAddress = (nuint)this.detour.Method.MethodHandle.GetFunctionPointer();
this.Hooked = true;
return true;
}
catch (Exception)
{
return false;
}
finally
{
this.semaphore.Release();
}
}
public void Dispose()
{
this.semaphore.Wait();
try
{
if (!this.Hooked ||
this.engine is null)
{
return;
}
this.engine.DisableHooks();
this.engine.Dispose();
this.cont = null;
this.Hooked = false;
}
finally
{
this.semaphore.Release();
}
}
/// <summary>
/// Follows a <c>CALL rel32</c> (<c>E8</c>) **or** <c>JMP rel32</c> (<c>E9</c>)
/// trampoline that may have been planted by a previous hook.
/// </summary>
private static unsafe nuint UnwrapNearBranch(nuint addr)
{
byte op = Marshal.ReadByte((IntPtr)addr);
if (op != 0xE8 && op != 0xE9) // not CALL/JMP rel32
return addr;
int rel = Marshal.ReadInt32((IntPtr)(addr + 1));
long dst = (long)addr + 5 + rel; // 5-byte instruction
return (nuint)dst;
}
}
@@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC)]
public readonly struct AccountUnlockedCount
{
[FieldOffset(0x0000)]
public readonly uint Id;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x138)]
public readonly struct AccountGameContext
{
[FieldOffset(0x0000)]
public readonly GuildWarsArray<AccountUnlockedCount> AccountUnlockedCounts;
[FieldOffset(0x00b4)]
public readonly GuildWarsArray<uint> UnlockedPvpHeroes;
[FieldOffset(0x0124)]
public readonly GuildWarsArray<uint> UnlockedAccountSkills;
[FieldOffset(0x0134)]
public readonly uint AccountFlags;
}
@@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x1C)]
public readonly unsafe struct AccountInfoContext
{
public readonly char* AccountName;
public readonly uint Wins;
public readonly uint Losses;
public readonly uint Rating;
public readonly uint QualifierPoints;
public readonly uint Rank;
public readonly uint TournamentRewardPoints;
}
+246
View File
@@ -0,0 +1,246 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum AgentType : uint
{
Living = 0xDB,
Gadget = 0x200,
Item = 0x400
}
[Flags]
public enum AgentModelType : ushort
{
NPC = 0x2000,
Player = 0x3000
}
[Flags]
public enum LivingAgentState : uint
{
None = 0,
InCombatStance = 0x000001,
HasQuest = 0x000002,
Dead = 0x000008,
Female = 0x000200,
HasBossGlow = 0x000400,
HidingCape = 0x001000,
CanBeViewedInPartyWindow = 0x020000,
SpawnedSpirit = 0x040000,
BeingObservedOrPlayer = 0x400000
}
public enum LivingAgentAllegiance : byte
{
Ally_NonAttackable = 0x1,
Neutral = 0x2,
Enemy = 0x3,
Spirit_Pet = 0x4,
Minion = 0x5,
Npc_Minipet = 0x6
};
[Flags]
public enum LivingAgentEffects : uint
{
None = 0,
Bleeding = 0x0001,
Conditioned = 0x0002,
Crippled1 = 0x0008, // Part of the Crippled check
Crippled = 0x000A, // Combined flag for crippled (0x0008 | 0x0002)
Dead = 0x0010,
DeepWound = 0x0020,
Poisoned = 0x0040,
Enchanted = 0x0080,
DegenHexed = 0x0400,
Hexed = 0x0800,
WeaponSpelled = 0x8000
}
public enum LivingAgentModelState : ushort
{
// Idle states
Idle1 = 64,
Idle2 = 68,
Idle3 = 100,
// Casting states
Casting1 = 65,
Casting2 = 581,
// Moving states
Moving1 = 12,
Moving2 = 76,
Moving3 = 204,
// Attacking states
Attacking1 = 96,
Attacking2 = 1088,
Attacking3 = 1120,
// Other states
KnockedDown = 1104
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct AgentEffects
{
public readonly uint AgentId;
public readonly GuildWarsArray<Buff> Buffs;
public readonly GuildWarsArray<Effect> Effects;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct AgentGameContext
{
[FieldOffset(0x01AC)]
public readonly uint InstanceTimer;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 196)]
public readonly struct AgentContext
{
[FieldOffset(0x0014)]
public readonly uint AgentInstanceTimer;
[FieldOffset(0x002C)]
public readonly uint AgentId;
[FieldOffset(0x0074)]
public readonly GamePos Pos;
[FieldOffset(0x009C)]
public readonly AgentType Type;
[FieldOffset(0x00A0)]
public readonly Vector2 Velocity;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 212)]
public readonly struct AgentItemContext
{
[FieldOffset(0x00C4)]
public readonly uint OwnerId;
[FieldOffset(0x00C8)]
public readonly uint ItemId;
[FieldOffset(0x00D0)]
public readonly uint ExtraType;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 228)]
public readonly struct AgentGadgetContext
{
[FieldOffset(0x00CC)]
public readonly uint ExtraType;
[FieldOffset(0x00D0)]
public readonly uint GadgetId;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1C0)]
public readonly unsafe struct AgentLivingContext
{
[FieldOffset(0x00C4)]
public readonly uint OwnerId;
[FieldOffset(0x00F4)]
public readonly ushort AgentType; // All non-player have an identifier. Two of the same mob have the same identifier;
[FieldOffset(0x00F6)]
public readonly AgentModelType AgentModelType;
[FieldOffset(0x0104)]
public readonly TagInfo* Tags;
[FieldOffset(0x010A)]
public readonly byte Primary;
[FieldOffset(0x010B)]
public readonly byte Secondary;
[FieldOffset(0x010C)]
public readonly byte Level;
[FieldOffset(0x010D)]
public readonly byte TeamId;
[FieldOffset(0x0114)]
public readonly float EnergyRegen;
[FieldOffset(0x011C)]
public readonly float Energy;
[FieldOffset(0x0120)]
public readonly uint MaxEnergy;
[FieldOffset(0x0128)]
public readonly float HealthRegen;
[FieldOffset(0x0130)]
public readonly float Health;
[FieldOffset(0x0134)]
public readonly uint MaxHealth;
[FieldOffset(0x0138)]
public readonly uint Effects;
[FieldOffset(0x0154)]
public readonly LivingAgentModelState ModelState;
[FieldOffset(0x0158)]
public readonly LivingAgentState State;
[FieldOffset(0x0180)]
public readonly uint LoginNumber;
[FieldOffset(0x01B1)]
public readonly LivingAgentAllegiance Allegiance;
public readonly bool IsAlive => (!this.State.HasFlag(LivingAgentState.Dead)) && this.Health > 0f;
public readonly bool IsPlayer => this.LoginNumber is not 0;
public readonly bool IsNpc => this.LoginNumber is 0;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 52)]
public readonly struct MapAgentContext
{
[FieldOffset(0x0000)]
public readonly float CurrentEnergy;
[FieldOffset(0x0004)]
public readonly float MaxEnergy;
[FieldOffset(0x0008)]
public readonly float EnergyRegen;
[FieldOffset(0x000C)]
public readonly uint SkillTimestamp;
[FieldOffset(0x0020)]
public readonly float CurrentHealth;
[FieldOffset(0x0024)]
public readonly float MaxHealth;
[FieldOffset(0x0028)]
public readonly float HealthRegen;
[FieldOffset(0x0030)]
public readonly LivingAgentEffects Effects;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x38)]
public readonly unsafe struct AgentInfo
{
[FieldOffset(0x0034)]
public readonly char* NameEncoded;
}
@@ -0,0 +1,93 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct AttributeContext
{
public readonly uint Id;
public readonly uint LevelBase;
public readonly uint Level;
public readonly uint DecrementPoints;
public readonly uint IncrementPoints;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct AttributeInfo
{
public readonly uint ProfessionId;
public readonly uint AttributeId;
public readonly uint NameId;
public readonly uint DescriptionId;
public readonly uint IsPve;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct PartyAttribute
{
public readonly uint AgentId;
public readonly AttributeContext Attribute0;
public readonly AttributeContext Attribute1;
public readonly AttributeContext Attribute2;
public readonly AttributeContext Attribute3;
public readonly AttributeContext Attribute4;
public readonly AttributeContext Attribute5;
public readonly AttributeContext Attribute6;
public readonly AttributeContext Attribute7;
public readonly AttributeContext Attribute8;
public readonly AttributeContext Attribute9;
public readonly AttributeContext Attribute10;
public readonly AttributeContext Attribute11;
public readonly AttributeContext Attribute12;
public readonly AttributeContext Attribute13;
public readonly AttributeContext Attribute14;
public readonly AttributeContext Attribute15;
public readonly AttributeContext Attribute16;
public readonly AttributeContext Attribute17;
public readonly AttributeContext Attribute18;
public readonly AttributeContext Attribute19;
public readonly AttributeContext Attribute20;
public readonly AttributeContext Attribute21;
public readonly AttributeContext Attribute22;
public readonly AttributeContext Attribute23;
public readonly AttributeContext Attribute24;
public readonly AttributeContext Attribute25;
public readonly AttributeContext Attribute26;
public readonly AttributeContext Attribute27;
public readonly AttributeContext Attribute28;
public readonly AttributeContext Attribute29;
public readonly AttributeContext Attribute30;
public readonly AttributeContext Attribute31;
public readonly AttributeContext Attribute32;
public readonly AttributeContext Attribute33;
public readonly AttributeContext Attribute34;
public readonly AttributeContext Attribute35;
public readonly AttributeContext Attribute36;
public readonly AttributeContext Attribute37;
public readonly AttributeContext Attribute38;
public readonly AttributeContext Attribute39;
public readonly AttributeContext Attribute40;
public readonly AttributeContext Attribute41;
public readonly AttributeContext Attribute42;
public readonly AttributeContext Attribute43;
public readonly AttributeContext Attribute44;
public readonly AttributeContext Attribute45;
public readonly AttributeContext Attribute46;
public readonly AttributeContext Attribute47;
public readonly AttributeContext Attribute48;
public readonly AttributeContext Attribute49;
public readonly AttributeContext Attribute50;
public readonly AttributeContext Attribute51;
public readonly AttributeContext Attribute52;
public readonly AttributeContext Attribute53;
public readonly AttributeContext[] Attributes =>
[
this.Attribute0, this.Attribute1, this.Attribute2, this.Attribute3, this.Attribute4, this.Attribute5, this.Attribute6, this.Attribute7, this.Attribute8, this.Attribute9,
this.Attribute10, this.Attribute11, this.Attribute12, this.Attribute13, this.Attribute14, this.Attribute15, this.Attribute16, this.Attribute17, this.Attribute18, this.Attribute19,
this.Attribute20, this.Attribute21, this.Attribute22, this.Attribute23, this.Attribute24, this.Attribute25, this.Attribute26, this.Attribute27, this.Attribute28, this.Attribute29,
this.Attribute30, this.Attribute31, this.Attribute32, this.Attribute33, this.Attribute34, this.Attribute35, this.Attribute36, this.Attribute37, this.Attribute38, this.Attribute39,
this.Attribute40, this.Attribute41, this.Attribute42, this.Attribute43, this.Attribute44, this.Attribute45, this.Attribute46, this.Attribute47, this.Attribute48, this.Attribute49,
this.Attribute50, this.Attribute51, this.Attribute52, this.Attribute53
];
}
@@ -0,0 +1,8 @@
namespace Daybreak.API.Interop.GuildWars;
public enum Behavior
{
Fight,
Guard,
AvoidCombat
}
@@ -0,0 +1,50 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct CharContext
{
[FieldOffset(0x0064)]
public readonly Uuid PlayerUuid;
[FieldOffset(0x0074)]
public readonly Array20Char PlayerName;
[FieldOffset(0x0198)]
public readonly uint MapId;
[FieldOffset(0x019C)]
public readonly uint IsExporable;
[FieldOffset(0x01A0)]
public readonly Array24Byte Host;
[FieldOffset(0x01B8)]
public readonly uint PlayerId;
[FieldOffset(0x0220)]
public readonly uint DistrictNumber;
[FieldOffset(0x0224)]
public readonly Language Language;
[FieldOffset(0x0228)]
public readonly uint ObserveMapId;
[FieldOffset(0x022C)]
public readonly uint CurrentMapId;
[FieldOffset(0x0230)]
public readonly uint ObserveMapType;
[FieldOffset(0x0234)]
public readonly uint CurrentMapType;
[FieldOffset(0x02A4)]
public readonly uint PlayerNumber;
[FieldOffset(0x03B8)]
public readonly Array64Char PlayerEmail;
}
@@ -0,0 +1,22 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct CharInfoContext
{
[FieldOffset(0x0008)]
public readonly Uuid Uuid;
[FieldOffset(0x0018)]
public readonly Array20Char Name;
[FieldOffset(0x0040)]
public readonly Array17Uint Props;
public readonly uint MapId => (this.Props[0] >> 16) & 0xFFFF;
public readonly uint Primary => (this.Props[2] >> 20) & 0xF;
public readonly uint Secondary => (this.Props[7] >> 10) & 0xF;
public readonly uint Campaign => this.Props[7] & 0xF;
public readonly uint Level => (this.Props[7] >> 4) & 0x3F;
public readonly bool IsPvp => ((this.Props[7] >> 9) & 0x1) == 0x1;
}
@@ -0,0 +1,19 @@
namespace Daybreak.API.Interop.GuildWars;
public enum District
{
Current,
International,
American,
EuropeEnglish,
EuropeFrench,
EuropeGerman,
EuropeItalian,
EuropeSpanish,
EuropePolish,
EuropeRussian,
AsiaKorean,
AsiaChinese,
AsiaJapanese,
Unknown = 0xff
}
+68
View File
@@ -0,0 +1,68 @@
using Daybreak.API.Models;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
// Very incomplete. GWCA contains a much better definition
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1AC)]
public readonly struct Frame
{
[FieldOffset(0x0008)]
public readonly uint FrameLayout;
[FieldOffset(0x0018)]
public readonly uint VisibilityFlags;
[FieldOffset(0x0020)]
public readonly uint Type;
[FieldOffset(0x0024)]
public readonly uint TemplateType;
[FieldOffset(0x00A0)]
public readonly GuildWarsArray<FrameInteractionCallback> FrameCallbacks;
[FieldOffset(0x00b0)]
public readonly uint ChildOffsetId;
[FieldOffset(0x00b4)]
public readonly uint FrameId;
[FieldOffset(0x0120)]
public readonly FrameRelation Relation;
[FieldOffset(0x0184)]
public readonly uint Field91;
public bool IsCreated => (this.Field91 & 0x4) != 0;
public bool IsHidden => (this.Field91 & 0x200) != 0;
public bool IsVisible => !this.IsHidden;
public bool IsDIsabled => (this.Field91 & 0x10) != 0;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1C)]
public readonly unsafe struct FrameRelation
{
[FieldOffset(0x0000)]
public readonly FrameRelation* Parent;
[FieldOffset(0x000C)]
public readonly uint FrameHashId;
}
[StructLayout(LayoutKind.Sequential, Pack =1)]
public readonly unsafe struct InteractionMessage
{
public readonly uint FrameId;
public readonly UIMessage MessageId;
public readonly void** WParam;
}
public unsafe delegate void UIInteractionCallback(InteractionMessage* message, void* wParam, void* lParam);
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public unsafe readonly struct FrameInteractionCallback
{
public readonly void* Callback; // UIInteractionCallback
public readonly void* UiCtl_Context;
}
@@ -0,0 +1,28 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct GameContext
{
[FieldOffset(0x0008)]
public readonly AgentGameContext* AgentGameContext;
[FieldOffset(0x0014)]
public readonly MapContext* MapContext;
[FieldOffset(0x0028)]
public readonly AccountGameContext* AccountContext;
[FieldOffset(0x002C)]
public readonly WorldContext* WorldContext;
[FieldOffset(0x003C)]
public readonly GuildContext* GuildContext;
[FieldOffset(0x0044)]
public readonly CharContext* CharContext;
[FieldOffset(0x004C)]
public readonly PartyContext* PartyContext;
}
+11
View File
@@ -0,0 +1,11 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct GamePos
{
public readonly float X;
public readonly float Y;
public readonly uint Plane;
}
+110
View File
@@ -0,0 +1,110 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct GuildContext
{
[FieldOffset(0x0034)]
public readonly Array20Char PlayerName;
[FieldOffset(0x0060)]
public readonly uint PlayerBuildIndex;
[FieldOffset(0x0078)]
public readonly Array256Char Announcement;
[FieldOffset(0x0287)]
public readonly Array20Char AnnouncementAuthor;
[FieldOffset(0x02A0)]
public readonly uint PlayerGuildRank;
[FieldOffset(0x02A8)]
public readonly GuildWarsArray<TownAlliance> FactionOutpostGuilds;
[FieldOffset(0x02B8)]
public readonly uint KurzickTownCount;
[FieldOffset(0x02BC)]
public readonly uint LuxonTownCount;
[FieldOffset(0x02F8)]
public readonly GuildWarsArray<WrappedPointer<Guild>> Guilds;
[FieldOffset(0x0358)]
public readonly GuildWarsArray<WrappedPointer<GuildPlayer>> PlayerRoster;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct TownAlliance
{
public readonly uint Rank;
public readonly uint Allegiance;
public readonly uint Faction;
public readonly Array32Char Name;
public readonly Array5Char Tag;
public readonly CapeDesign Cape;
public readonly uint MapId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct CapeDesign
{
public readonly uint CapeBgColor;
public readonly uint CapeDetailColor;
public readonly uint CapeEmblemColor;
public readonly uint CapeShape;
public readonly uint CapeDetail;
public readonly uint CapeEmblem;
public readonly uint CapeTrim;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x174)]
public readonly unsafe struct GuildPlayer
{
[FieldOffset(0x0004)]
public readonly char* NamePtr;
[FieldOffset(0x0008)]
public readonly Array20Char InvitedName;
[FieldOffset(0x0030)]
public readonly Array20Char CurrentName;
[FieldOffset(0x0050)]
public readonly Array20Char InviterName;
[FieldOffset(0x0080)]
public readonly uint InviteTime;
[FieldOffset(0x0084)]
public readonly Array20Char PromoterName;
[FieldOffset(0x00DC)]
public readonly uint Offline;
[FieldOffset(0x00E0)]
public readonly uint MemberType;
[FieldOffset(0x00E4)]
public readonly uint Status;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xAC)]
public readonly struct Guild
{
[FieldOffset(0x0024)]
public readonly uint Index;
[FieldOffset(0x0028)]
public readonly uint Rank;
[FieldOffset(0x002C)]
public readonly uint Features;
[FieldOffset(0x0030)]
public readonly Array32Char Name;
[FieldOffset(0x0070)]
public readonly uint Rating;
[FieldOffset(0x0074)]
public readonly uint Faction;
[FieldOffset(0x0078)]
public readonly uint FactionPoints;
[FieldOffset(0x007C)]
public readonly uint QualifierPoints;
[FieldOffset(0x0080)]
public readonly Array8Char Tag;
[FieldOffset(0x0090)]
public readonly CapeDesign Cape;
}
@@ -0,0 +1,52 @@
using System.Collections;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly unsafe struct GuildWarsArray<T> : IEnumerable<T>
where T : unmanaged
{
public readonly T* Buffer;
public readonly uint Capacity;
public readonly uint Size;
public readonly uint Param;
public Enumerator GetEnumerator() => new(this.Buffer, this.Size);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => this.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
public unsafe struct Enumerator : IEnumerator, IEnumerator<T>
{
private readonly T* buffer;
private readonly uint size;
private int index;
internal Enumerator(T* buffer, uint size)
{
this.buffer = buffer;
this.size = size;
this.index = -1;
}
public bool MoveNext()
{
int next = this.index + 1;
if (next >= this.size)
{
return false;
}
this.index = next;
return true;
}
public T Current => this.buffer[this.index];
object IEnumerator.Current => this.Current;
public void Reset() => this.index = -1;
public readonly void Dispose() { }
}
}
+43
View File
@@ -0,0 +1,43 @@
using System.Extensions;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x24)]
public readonly struct HeroFlag
{
[FieldOffset(0x0000)]
public readonly uint HeroId;
[FieldOffset(0x0004)]
public readonly uint AgentId;
[FieldOffset(0x0008)]
public readonly uint Level;
[FieldOffset(0x000C)]
public readonly Behavior Behavior;
[FieldOffset(0x0010)]
public readonly Vector2 Flag;
[FieldOffset(0x0020)]
public readonly uint LockedTargetId;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x78)]
public readonly struct HeroInfo
{
[FieldOffset(0x0000)]
public readonly uint HeroId;
[FieldOffset(0x0004)]
public readonly uint AgentId;
[FieldOffset(0x0008)]
public readonly uint Level;
[FieldOffset(0x000C)]
public readonly Profession Primary;
[FieldOffset(0x0010)]
public readonly Profession Secondary;
[FieldOffset(0x0014)]
public readonly uint HeroFileId;
[FieldOffset(0x0018)]
public readonly uint ModelFileId;
[FieldOffset(0x0050)]
public readonly Array20Char Name;
}
@@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
public enum InstanceType
{
Outpost,
Explorable,
Loading
};
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct InstanceInfoContext
{
[FieldOffset(0x0004)]
public readonly InstanceType InstanceType;
[FieldOffset(0x0008)]
public readonly AreaInfo* CurrentMapInfo;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct AreaInfo
{
public readonly uint Campaign;
public readonly uint Continent;
public readonly uint Region;
public readonly uint RegionType;
public readonly uint Flags;
}
@@ -0,0 +1,17 @@
namespace Daybreak.API.Interop.GuildWars;
public enum Language
{
English,
Korean,
French,
German,
Italian,
Spanish,
TraditionalChinese,
Japanese = 8,
Polish,
Russian,
BorkBorkBork = 17,
Unknown = 0xff
}
@@ -0,0 +1,8 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct MapContext
{
}
+61
View File
@@ -0,0 +1,61 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit)]
public readonly unsafe struct NpcContext
{
[FieldOffset(0x0000)]
public readonly uint ModelId;
[FieldOffset(0x000C)]
public readonly uint Sex;
[FieldOffset(0x0010)]
public readonly NpcFlags Flags;
[FieldOffset(0x0014)]
public readonly uint Primary;
[FieldOffset(0x001C)]
public readonly byte DefaultLevel;
[FieldOffset(0x0020)]
public readonly char* EncName;
[FieldOffset(0x0028)]
public readonly int FilesCount;
[FieldOffset(0x002C)]
public readonly int FilesCapacity;
[Flags]
public enum NpcFlags : uint
{
None = 0x0000,
Henchman = 0x0010,
Hero = 0x0020,
Spirit = 0x4000,
Minion = 0x0100,
Pet = 0x000D
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly unsafe struct PetContext
{
public readonly uint AgentId;
public readonly uint OwnerAgentId;
public readonly char* PetName;
public readonly uint ModelFileId1;
public readonly uint ModelFileId2;
public readonly Behavior Behavior;
public readonly uint LockedTargetId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct ControlledMinion
{
public readonly uint AgentId;
public readonly uint MinionCount;
}
+124
View File
@@ -0,0 +1,124 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum PlayerPartyMemberState : uint
{
None = 0,
Connected = 1,
Ticked = 2
}
public enum PartySearchType
{
PartySearchType_Hunting = 0,
PartySearchType_Mission = 1,
PartySearchType_Quest = 2,
PartySearchType_Trade = 3,
PartySearchType_Guild = 4,
};
[Flags]
public enum PartyFlags : uint
{
None = 0,
HardMode = 0x10, // Bit 4
Defeated = 0x20, // Bit 5
PartyLeader = 0x80 // Bit 7 (0x7th position)
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC)]
public readonly unsafe struct PartyMoraleContext
{
[FieldOffset(0x000C)]
public readonly PartyMemberMoraleInfo* MoraleInfo;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct PartyMemberMoraleInfo
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x0014)]
public readonly uint Morale;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x58)]
public readonly unsafe struct PartyContext
{
[FieldOffset(0x0014)]
public readonly PartyFlags Flags;
[FieldOffset(0x0040)]
public readonly GuildWarsArray<WrappedPointer<PartyInfo>> Parties;
[FieldOffset(0x0054)]
public readonly PartyInfo* PlayerParty;
[FieldOffset(0x00C0)]
public readonly GuildWarsArray<WrappedPointer<PartySearch>> PartySearches;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public readonly struct PlayerPartyMember
{
public readonly uint LogingNumber;
public readonly uint CalledTargetId;
public readonly PlayerPartyMemberState State;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x18)]
public readonly struct HeroPartyMember
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x0004)]
public readonly uint OwnerPlayerId;
[FieldOffset(0x0008)]
public readonly uint HeroId;
[FieldOffset(0x0014)]
public readonly uint Level;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x34)]
public readonly struct HenchmanPartyMember
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x002C)]
public readonly Profession Profession;
[FieldOffset(0x0030)]
public readonly uint Level;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x84)]
public readonly struct PartyInfo
{
[FieldOffset(0x0000)]
public readonly uint PartyId;
[FieldOffset(0x0004)]
public readonly GuildWarsArray<PlayerPartyMember> Players;
[FieldOffset(0x0014)]
public readonly GuildWarsArray<HenchmanPartyMember> Henchmen;
[FieldOffset(0x0024)]
public readonly GuildWarsArray<HeroPartyMember> Heroes;
[FieldOffset(0x0034)]
public readonly GuildWarsArray<uint> Others;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct PartySearch
{
public readonly uint PartySearchId;
public readonly PartySearchType PartySearchType;
public readonly uint HardMode;
public readonly uint District;
public readonly uint Language;
public readonly uint PartySize;
public readonly uint HeroCount;
public readonly Array32Char Message;
public readonly Array20Char PartyLeader;
public readonly Profession Primary;
public readonly Profession Secondary;
public readonly uint Level;
public readonly uint Timestamp;
}
@@ -0,0 +1,44 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum PlayerContextFlags : uint
{
None = 0,
PvP = 0x800
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x4C)]
public readonly unsafe struct PlayerContext
{
[FieldOffset(0x0000)]
public readonly int AgentId;
[FieldOffset(0x0014)]
public readonly PlayerContextFlags Flags;
[FieldOffset(0x0018)]
public readonly uint PrimaryProfession;
[FieldOffset(0x001C)]
public readonly uint SecondaryProfession;
[FieldOffset(0x0024)]
public readonly char* NameEncoded;
[FieldOffset(0x0028)]
public readonly char* Name;
[FieldOffset(0x002C)]
public readonly uint PartyLeaderPlayerNumber;
[FieldOffset(0x0030)]
public readonly uint ActiveTitleTier;
[FieldOffset(0x0034)]
public readonly uint PlayerNumber;
[FieldOffset(0x0038)]
public readonly uint PartySize;
}
@@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct PlayerControlledCharContext
{
[FieldOffset(0x0014)]
public readonly uint AgentId;
}
@@ -0,0 +1,30 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct PreGameContext
{
[FieldOffset(0x0000)]
public readonly uint FrameId;
[FieldOffset(0x0124)]
public readonly uint ChosenCharacterIndex;
[FieldOffset(0x0140)]
public readonly uint Index1;
[FieldOffset(0x0144)]
public readonly uint Index2;
[FieldOffset(0x00148)]
public readonly GuildWarsArray<LoginCharacterContext> LoginCharacters;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct LoginCharacterContext
{
[FieldOffset(0x0004)]
public readonly Array20Char CharacterName;
}
@@ -0,0 +1,47 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
public enum Profession : uint
{
None,
Warrior,
Ranger,
Monk,
Necromancer,
Mesmer,
Elementalist,
Assassin,
Ritualist,
Paragon,
Dervish
}
public enum ProfessionByte : byte
{
None,
Warrior,
Ranger,
Monk,
Necromancer,
Mesmer,
Elementalist,
Assassin,
Ritualist,
Paragon,
Dervish
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x0014)]
public readonly struct ProfessionsContext
{
public readonly uint AgentId;
public readonly Profession CurrentPrimary;
public readonly Profession CurrentSecondary;
public readonly uint UnlockedProfessionsFlags;
public bool ProfessionUnlocked(int professionId)
{
return (this.UnlockedProfessionsFlags & (1U << professionId)) != 0;
}
}
+28
View File
@@ -0,0 +1,28 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x0034)]
public readonly struct QuestContext
{
[FieldOffset(0x0000)]
public readonly uint QuestId;
[FieldOffset(0x0014)]
public readonly uint MapFrom;
[FieldOffset(0x0018)]
public readonly Vector3 Marker;
[FieldOffset(0x0028)]
public readonly uint MapTo;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public readonly unsafe struct MissionObjectiveContext
{
public readonly uint ObjectiveId;
public readonly char* EncodedString;
public readonly uint Type;
}
@@ -0,0 +1,12 @@
namespace Daybreak.API.Interop.GuildWars;
public enum ServerRegion
{
International = -2,
America = 0,
Korea,
Europe,
China,
Japan,
Unknown = 0xff
}
+58
View File
@@ -0,0 +1,58 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct SkillContext
{
public readonly uint Adrenaline1;
public readonly uint Adrenaline2;
public readonly uint Recharge;
public readonly uint Id;
public readonly uint Event;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xBC)]
public readonly struct SkillbarContext
{
public readonly uint AgentId;
public readonly SkillContext Skill0;
public readonly SkillContext Skill1;
public readonly SkillContext Skill2;
public readonly SkillContext Skill3;
public readonly SkillContext Skill4;
public readonly SkillContext Skill5;
public readonly SkillContext Skill6;
public readonly SkillContext Skill7;
public readonly uint Disabled;
public readonly uint Casting;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x10)]
public readonly struct Buff
{
[FieldOffset(0x0000)]
public readonly uint SkillId;
[FieldOffset(0x0008)]
public readonly uint BuffId;
[FieldOffset(0x000C)]
public readonly uint TargetAgentId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
public readonly struct Effect
{
public readonly uint SkillId;
public readonly uint AttributeLevel;
public readonly uint EffectId;
public readonly uint AgentId;
public readonly float Duration;
public readonly uint Timestamp;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct DupeSkill
{
public readonly uint SkillId;
public readonly uint Count;
}
@@ -0,0 +1,13 @@
using System.Extensions;
namespace Daybreak.API.Interop.GuildWars;
public readonly struct SkillTemplate(uint primary, uint secondary, uint attributeCount, Array12Uint attributeIds, Array12Uint attributeValues, Array8Uint skills)
{
public readonly uint Primary = primary;
public readonly uint Secondary = secondary;
public readonly uint AttributesCount = attributeCount;
public readonly Array12Uint AttributeIds = attributeIds;
public readonly Array12Uint AttributeValues = attributeValues;
public readonly Array8Uint Skills = skills;
}
+12
View File
@@ -0,0 +1,12 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct TagInfo
{
public readonly ushort GuildId;
public readonly byte Primary;
public readonly byte Secondary;
public readonly ushort Level;
}
+40
View File
@@ -0,0 +1,40 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum TitleProps : uint
{
None = 0,
PercentageBased = 1,
HasTiers = 2
// Note: The HasTiers check uses (props & 3) == 2, which means bit 1 is set but bit 0 is not
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 40)]
public readonly struct TitleContext
{
public readonly TitleProps Props;
public readonly uint CurrentPoints;
public readonly uint CurrentTitleTierIndex;
public readonly uint PointsNeededForCurrentRank;
public readonly uint NextTitleTierIndex;
public readonly uint PointsNeededForNextRank;
public readonly uint MaxTitleRank;
public readonly uint MaxTitleTierIndex;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public readonly unsafe struct TitleTier
{
public readonly TitleProps Props;
public readonly uint TierNumber;
public readonly char* TierNameEncoded;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct TitleClientData
{
public readonly uint TitleId;
public readonly uint NameId;
}
+183
View File
@@ -0,0 +1,183 @@
namespace Daybreak.API.Interop.GuildWars;
public enum UIAction : uint
{
None = 0,
Screenshot = 0xAE,
// Panels
CloseAllPanels = 0x85,
ToggleInventoryWindow = 0x8B,
OpenScoreChart = 0xBD,
OpenTemplateManager = 0xD3,
OpenSaveEquipmentTemplate = 0xD4,
OpenSaveSkillTemplate = 0xD5,
OpenParty = 0xBF,
OpenGuild = 0xBA,
OpenFriends = 0xB9,
ToggleAllBags = 0xB8,
OpenMissionMap = 0xB6,
OpenBag2 = 0xB5,
OpenBag1 = 0xB4,
OpenBelt = 0xB3,
OpenBackpack = 0xB2,
OpenSkillsAndAttributes = 0x8F,
OpenQuestLog = 0x8E,
OpenWorldMap = 0x8C,
OpenOptions = 0x8D,
OpenHero = 0x8A,
// Weapon sets
CycleEquipment = 0x86,
ActivateWeaponSet1 = 0x81,
ActivateWeaponSet2,
ActivateWeaponSet3,
ActivateWeaponSet4,
DropItem = 0xCD, // drops bundle item >> flags, ashes, etc
// Chat
CharReply = 0xBE,
OpenChat = 0xA1,
OpenAlliance = 0x88,
ReverseCamera = 0x90,
StrafeLeft = 0x91,
StrafeRight = 0x92,
TurnLeft = 0xA2,
TurnRight = 0xA3,
MoveBackward = 0xAC,
MoveForward = 0xAD,
CancelAction = 0xAF,
Interact = 0x80,
ReverseDirection = 0xB1,
Autorun = 0xB7,
Follow = 0xCC,
// Targeting
TargetPartyMember1 = 0x96,
TargetPartyMember2,
TargetPartyMember3,
TargetPartyMember4,
TargetPartyMember5,
TargetPartyMember6,
TargetPartyMember7,
TargetPartyMember8,
TargetPartyMember9 = 0xC6,
TargetPartyMember10,
TargetPartyMember11,
TargetPartyMember12,
TargetNearestItem = 0xC3,
TargetNextItem = 0xC4,
TargetPreviousItem = 0xC5,
TargetPartyMemberNext = 0xCA,
TargetPartyMemberPrevious = 0xCB,
TargetAllyNearest = 0xBC,
ClearTarget = 0xE3,
TargetSelf = 0xA0, // also 0x96
TargetPriorityTarget = 0x9F,
TargetNearestEnemy = 0x93,
TargetNextEnemy = 0x95,
TargetPreviousEnemy = 0x9E,
ShowOthers = 0x89,
ShowTargets = 0x94,
CameraZoomIn = 0xCE,
CameraZoomOut = 0xCF,
// Party/Hero commands
ClearPartyCommands = 0xDB,
CommandParty = 0xD6,
CommandHero1,
CommandHero2,
CommandHero3,
CommandHero4 = 0x102,
CommandHero5,
CommandHero6,
CommandHero7,
OpenHero1PetCommander = 0xE0,
OpenHero2PetCommander,
OpenHero3PetCommander,
OpenHero4PetCommander = 0xFE,
OpenHero5PetCommander,
OpenHero6PetCommander,
OpenHero7PetCommander,
OpenHeroCommander1 = 0xDC,
OpenHeroCommander2,
OpenHeroCommander3,
OpenPetCommander,
OpenHeroCommander4 = 0x126,
OpenHeroCommander5,
OpenHeroCommander6,
OpenHeroCommander7,
Hero1Skill1 = 0xE5,
Hero1Skill2,
Hero1Skill3,
Hero1Skill4,
Hero1Skill5,
Hero1Skill6,
Hero1Skill7,
Hero1Skill8,
Hero2Skill1,
Hero2Skill2,
Hero2Skill3,
Hero2Skill4,
Hero2Skill5,
Hero2Skill6,
Hero2Skill7,
Hero2Skill8,
Hero3Skill1,
Hero3Skill2,
Hero3Skill3,
Hero3Skill4,
Hero3Skill5,
Hero3Skill6,
Hero3Skill7,
Hero3Skill8,
Hero4Skill1 = 0x106,
Hero4Skill2,
Hero4Skill3,
Hero4Skill4,
Hero4Skill5,
Hero4Skill6,
Hero4Skill7,
Hero4Skill8,
Hero5Skill1,
Hero5Skill2,
Hero5Skill3,
Hero5Skill4,
Hero5Skill5,
Hero5Skill6,
Hero5Skill7,
Hero5Skill8,
Hero6Skill1,
Hero6Skill2,
Hero6Skill3,
Hero6Skill4,
Hero6Skill5,
Hero6Skill6,
Hero6Skill7,
Hero6Skill8,
Hero7Skill1,
Hero7Skill2,
Hero7Skill3,
Hero7Skill4,
Hero7Skill5,
Hero7Skill6,
Hero7Skill7,
Hero7Skill8,
// Skills
UseSkill1 = 0xA4,
UseSkill2,
UseSkill3,
UseSkill4,
UseSkill5,
UseSkill6,
UseSkill7,
UseSkill8
};
+108
View File
@@ -0,0 +1,108 @@
using System.Globalization;
namespace Daybreak.API.Interop.GuildWars;
public readonly struct Uuid(
uint timeLow,
ushort timeMid,
ushort timeHighAndVersion,
byte clockSqHi,
byte clockSqLow,
byte node0,
byte node1,
byte node2,
byte node3,
byte node4,
byte node5) : IEquatable<Uuid>
{
public const uint NodeLength = 6;
public readonly uint TimeLow = timeLow;
public readonly ushort TimeMid = timeMid;
public readonly ushort TimeHighAndVersion = timeHighAndVersion;
public readonly byte ClockSqHiAndRes = clockSqHi;
public readonly byte ClockSqLow = clockSqLow;
public readonly byte Node0 = node0;
public readonly byte Node1 = node1;
public readonly byte Node2 = node2;
public readonly byte Node3 = node3;
public readonly byte Node4 = node4;
public readonly byte Node5 = node5;
public bool Equals(Uuid other)
{
return this.TimeLow == other.TimeLow &&
this.TimeMid == other.TimeMid &&
this.TimeHighAndVersion == other.TimeHighAndVersion &&
this.ClockSqHiAndRes == other.ClockSqHiAndRes &&
this.ClockSqLow == other.ClockSqLow &&
this.Node0 == other.Node0 &&
this.Node1 == other.Node1 &&
this.Node2 == other.Node2 &&
this.Node3 == other.Node3 &&
this.Node4 == other.Node4 &&
this.Node5 == other.Node5;
}
public override bool Equals(object? obj)
{
return obj is Uuid uuid && this.Equals(uuid);
}
public static bool operator ==(Uuid left, Uuid right)
{
return left.Equals(right);
}
public static bool operator !=(Uuid left, Uuid right)
{
return !(left == right);
}
public static bool TryParse(string uuidString, out Uuid uuid)
{
if (uuidString.Length != 36)
{
uuid = default;
return false;
}
var parts = uuidString.Split('-');
if (parts.Length != 5)
{
uuid = default;
return false;
}
if (!uint.TryParse(parts[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var timeLow) ||
!ushort.TryParse(parts[1], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var timeMid) ||
!ushort.TryParse(parts[2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var timeHighAndVersion) ||
!byte.TryParse(parts[3].AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clockSqHi) ||
!byte.TryParse(parts[3].AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clockSqLow) ||
!byte.TryParse(parts[4].AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node0) ||
!byte.TryParse(parts[4].AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node1) ||
!byte.TryParse(parts[4].AsSpan(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node2) ||
!byte.TryParse(parts[4].AsSpan(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node3) ||
!byte.TryParse(parts[4].AsSpan(8, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node4) ||
!byte.TryParse(parts[4].AsSpan(10, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var node5))
{
uuid = default;
return false;
}
uuid = new Uuid(timeLow, timeMid, timeHighAndVersion, clockSqHi, clockSqLow, node0, node1, node2, node3, node4, node5);
return true;
}
public override int GetHashCode()
{
return HashCode.Combine<uint, ushort, ushort, byte, byte, byte[]>(this.TimeLow, this.TimeMid, this.TimeHighAndVersion, this.ClockSqHiAndRes, this.ClockSqLow, [this.Node0, this.Node1, this.Node2, this.Node3, this.Node4, this.Node5]);
}
public override string ToString()
{
return $"{this.TimeLow:X8}-{this.TimeMid:X4}-{this.TimeHighAndVersion:X4}-{this.ClockSqHiAndRes:X2}{this.ClockSqLow:X2}-{this.Node0:X2}{this.Node1:X2}{this.Node2:X2}{this.Node3:X2}{this.Node4:X2}{this.Node5:X2}";
}
public readonly static Uuid Zero = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
@@ -0,0 +1,173 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct WorldContext
{
[FieldOffset(0x0000)]
public readonly AccountInfoContext* AccountInfo;
[FieldOffset(0x0024)]
public readonly GuildWarsArray<uint> MerchItems;
[FieldOffset(0x0034)]
public readonly GuildWarsArray<uint> MerchItems2;
[FieldOffset(0x007C)]
public readonly GuildWarsArray<MapAgentContext> MapAgents;
[FieldOffset(0x009C)]
public readonly Vector3 AllFlag;
[FieldOffset(0x00AC)]
public readonly GuildWarsArray<PartyAttribute> Attributes;
[FieldOffset(0x0508)]
public readonly GuildWarsArray<AgentEffects> PartyEffects;
[FieldOffset(0x0528)]
public readonly uint ActiveQuestId;
[FieldOffset(0x052C)]
public readonly GuildWarsArray<QuestContext> QuestLog;
[FieldOffset(0x0564)]
public readonly GuildWarsArray<MissionObjectiveContext> MissionObjectives;
[FieldOffset(0x0574)]
public readonly GuildWarsArray<uint> HenchmenAgentIds;
[FieldOffset(0x0584)]
public readonly GuildWarsArray<HeroFlag> HeroFlags;
[FieldOffset(0x0594)]
public readonly GuildWarsArray<HeroInfo> HeroInfos;
[FieldOffset(0x05A4)]
public readonly GuildWarsArray<nuint> CartographedAreas;
[FieldOffset(0x05BC)]
public readonly GuildWarsArray<ControlledMinion> ControlledMinions;
[FieldOffset(0x05CC)]
public readonly GuildWarsArray<uint> MissionsCompleted;
[FieldOffset(0x05DC)]
public readonly GuildWarsArray<uint> MissionsBonus;
[FieldOffset(0x05EC)]
public readonly GuildWarsArray<uint> MissionsCompletedHardMode;
[FieldOffset(0x05FC)]
public readonly GuildWarsArray<uint> MissionsBonusHardMode;
[FieldOffset(0x060C)]
public readonly GuildWarsArray<uint> UnlockedMap;
[FieldOffset(0x062C)]
public readonly GuildWarsArray<PartyMoraleContext> PartyMorale;
[FieldOffset(0x067C)]
public readonly uint PlayerNumber;
[FieldOffset(0x0680)]
public readonly PlayerControlledCharContext* PlayerControlledChar;
[FieldOffset(0x0684)]
public readonly uint HardModeUnlocked;
[FieldOffset(0x06AC)]
public readonly GuildWarsArray<PetContext> Pets;
[FieldOffset(0x06BC)]
public readonly GuildWarsArray<ProfessionsContext> Professions;
[FieldOffset(0x06F0)]
public readonly GuildWarsArray<SkillbarContext> Skillbars;
[FieldOffset(0x0700)]
public readonly GuildWarsArray<uint> LearnableCharacterSkills;
[FieldOffset(0x0710)]
public readonly GuildWarsArray<uint> UnlockedCharacterSkills;
[FieldOffset(0x0720)]
public readonly GuildWarsArray<DupeSkill> DupeSkills;
[FieldOffset(0x0740)]
public readonly uint Experience;
[FieldOffset(0x0748)]
public readonly uint CurrentKurzick;
[FieldOffset(0x0750)]
public readonly uint TotalKurzick;
[FieldOffset(0x0758)]
public readonly uint CurrentLuxon;
[FieldOffset(0x0760)]
public readonly uint TotalLuxon;
[FieldOffset(0x0768)]
public readonly uint CurrentImperial;
[FieldOffset(0x0770)]
public readonly uint TotalImperial;
[FieldOffset(0x0788)]
public readonly uint Level;
[FieldOffset(0x0790)]
public readonly uint Morale;
[FieldOffset(0x0798)]
public readonly uint CurrentBalthazar;
[FieldOffset(0x07A0)]
public readonly uint TotalBalthazar;
[FieldOffset(0x07A8)]
public readonly uint CurrentSkillPoints;
[FieldOffset(0x07B0)]
public readonly uint TotalSkillPoints;
[FieldOffset(0x07B8)]
public readonly uint MaxKurzick;
[FieldOffset(0x07BC)]
public readonly uint MaxLuxon;
[FieldOffset(0x07C0)]
public readonly uint MaxBalthazar;
[FieldOffset(0x07C4)]
public readonly uint MaxImperial;
[FieldOffset(0x07CC)]
public readonly GuildWarsArray<AgentInfo> AgentInfos;
[FieldOffset(0x07FC)]
public readonly GuildWarsArray<NpcContext> Npcs;
[FieldOffset(0x080C)]
public readonly GuildWarsArray<PlayerContext> Players;
[FieldOffset(0x081C)]
public readonly GuildWarsArray<TitleContext> Titles;
[FieldOffset(0x082C)]
public readonly GuildWarsArray<TitleTier> TitleTiers;
[FieldOffset(0x083C)]
public readonly GuildWarsArray<uint> VanquishedAreas;
[FieldOffset(0x084C)]
public readonly uint FoesKilled;
[FieldOffset(0x0850)]
public readonly uint FoesToKill;
}
@@ -0,0 +1,37 @@
namespace Daybreak.API.Interop.GuildWars;
public readonly unsafe struct WrappedPointer<T>(T* pointer)
where T : unmanaged
{
public readonly T* Pointer = pointer;
public bool IsNull => this.Pointer is null;
public static implicit operator WrappedPointer<T>(T* pointer) => new(pointer);
public static implicit operator T*(WrappedPointer<T> wrappedPointer) => wrappedPointer.Pointer;
public static bool operator ==(T* left, WrappedPointer<T> right) => left == right.Pointer;
public static bool operator !=(T* left, WrappedPointer<T> right) => left != right.Pointer;
public static bool operator ==(WrappedPointer<T> left, T* right) => left.Pointer == right;
public static bool operator !=(WrappedPointer<T> left, T* right) => left.Pointer != right;
public static bool operator ==(WrappedPointer<T> left, WrappedPointer<T> right) => left.Pointer == right.Pointer;
public static bool operator !=(WrappedPointer<T> left, WrappedPointer<T> right) => left.Pointer != right.Pointer;
public override int GetHashCode() => this.Pointer is null ? 0 : this.Pointer->GetHashCode();
public override bool Equals(object? obj)
{
if (obj is WrappedPointer<T> wrappedPointer)
{
return this.Pointer == wrappedPointer.Pointer;
}
return false;
}
}
+9
View File
@@ -0,0 +1,9 @@
namespace Daybreak.API.Interop;
public interface IHook<T>
: IDisposable where T : Delegate
{
public T Continue { get; }
public nuint ContinueAddress { get; }
}
+19
View File
@@ -0,0 +1,19 @@
using Daybreak.API.Interop.GuildWars;
namespace Daybreak.API.Interop;
public readonly struct ManagedFrame(
WrappedPointer<Frame> frame,
Action closeFrame) : IDisposable
{
private readonly Action closeFrame = closeFrame;
public static ManagedFrame Null => new(null, () => { });
public readonly WrappedPointer<Frame> Frame = frame;
public void Dispose()
{
this.closeFrame();
}
}
@@ -0,0 +1,31 @@
using MemoryPack;
using System.Buffers;
namespace Daybreak.API.Interop;
public static class MemoryPackExtensions
{
/// <summary>Serialize <paramref name="value"/> into <paramref name="destination"/>.</summary>
/// <returns>Number of bytes written.</returns>
public static int SerializeToSpan<T>(T value, Span<byte> destination)
{
var dummy = DummyBufferWriter.Instance;
using var state = MemoryPackWriterOptionalStatePool.Rent(null);
var writer = new MemoryPackWriter<DummyBufferWriter>(ref dummy, destination, state);
writer.WriteValue(value);
writer.Flush();
return writer.WrittenCount;
}
private sealed class DummyBufferWriter : IBufferWriter<byte>
{
public static readonly DummyBufferWriter Instance = new();
public void Advance(int count) { /* ignore we never overflow destination */ }
public Memory<byte> GetMemory(int sizeHint = 0) => throw new NotSupportedException();
public Span<byte> GetSpan(int sizeHint = 0) => throw new NotSupportedException();
}
}
+78
View File
@@ -0,0 +1,78 @@
using Daybreak.API.Converters;
using System.Text.Json.Serialization;
namespace Daybreak.API.Interop;
[JsonConverter(typeof(PointerValueConverter))]
public readonly struct PointerValue(nuint address) : IEquatable<PointerValue>, IEquatable<nuint>, IEquatable<nint>
{
public readonly nuint Address = address;
public bool Equals(PointerValue other)
{
return this.Address.Equals(other.Address);
}
public bool Equals(nuint other)
{
return this.Address.Equals(other);
}
public bool Equals(nint other)
{
return this.Address.Equals((nuint)other);
}
public override string ToString()
{
return $"0x{this.Address:X8}";
}
public override bool Equals(object? obj) => obj is PointerValue value && this.Equals(value);
public override int GetHashCode()
{
return HashCode.Combine(this.Address);
}
public static bool operator ==(PointerValue left, PointerValue right)
{
return left.Equals(right);
}
public static bool operator !=(PointerValue left, PointerValue right)
{
return !(left == right);
}
public static implicit operator PointerValue(nuint address)
{
return new PointerValue(address);
}
public static implicit operator PointerValue(nint address)
{
return new PointerValue((nuint)address);
}
public static implicit operator PointerValue(int address)
{
return new PointerValue((nuint)address);
}
public static implicit operator PointerValue(uint address)
{
return new PointerValue(address);
}
public static PointerValue Parse(string s)
{
if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)
&& nuint.TryParse(s.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out var v))
{
return new PointerValue(v);
}
throw new FormatException($"Invalid pointer format: {s}");
}
}
+22
View File
@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
public sealed class UnmanagedStruct<T> : IDisposable
where T : struct
{
public nint Address { get; private set; }
public UnmanagedStruct(T value)
{
this.Address = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
Marshal.StructureToPtr(value, this.Address, false);
}
public void Dispose()
{
Marshal.DestroyStructure<T>(this.Address);
Marshal.FreeHGlobal(this.Address);
this.Address = 0x0;
}
}
@@ -0,0 +1,29 @@
using Microsoft.CorrelationVector;
using Microsoft.Extensions.Options;
using Net.Sdk.Web;
using Net.Sdk.Web.Options;
using Serilog.Context;
namespace Daybreak.API.Logging;
public sealed class LoggingEnrichmentMiddleware(IOptions<CorrelationVectorOptions> options)
: IMiddleware
{
private readonly CorrelationVectorOptions options = options.Value;
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (!context.Request.Headers.TryGetValue(this.options.Header, out var value) ||
value.FirstOrDefault() is not string cvString ||
CorrelationVector.Parse(cvString) is not CorrelationVector cv)
{
cv = new CorrelationVector();
}
cv = CorrelationVector.Extend(cv.ToString());
context.SetCorrelationVector(cv);
LogContext.PushProperty("CorrelationVector", cv);
HeaderDictionaryExtensions.Append(context.Response.Headers, this.options.Header, context.GetCorrelationVector().ToString());
return next(context);
}
}
@@ -0,0 +1,31 @@
using Serilog.Settings.Configuration;
using Serilog.Sinks.SystemConsole.Themes;
using Serilog;
using Net.Sdk.Web.Options;
namespace Daybreak.API.Logging;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithLogging(this WebApplicationBuilder builder)
{
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration, new ConfigurationReaderOptions
{
SectionName = "Logging"
})
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] {Level:u4}: [{SourceContext}] [{CorrelationVector}] {NewLine}{Message:lj}{NewLine}{Exception}",
theme: AnsiConsoleTheme.Sixteen)
.WriteTo.File(
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss}] {Level:u4}: [{SourceContext}] [{CorrelationVector}] {NewLine}{Message:lj}{NewLine}{Exception}",
path: "Daybreak.API.log")
.CreateLogger());
builder.Services.AddScoped<LoggingEnrichmentMiddleware>();
builder.Services.AddOptions<CorrelationVectorOptions>()
.Bind(builder.Configuration.GetSection("Correlation"));
return builder;
}
}
@@ -0,0 +1,10 @@
namespace Daybreak.API.Logging;
public static class WebApplicationExtensions
{
public static WebApplication UseLogging(this WebApplication app)
{
app.UseMiddleware<LoggingEnrichmentMiddleware>();
return app;
}
}
+9
View File
@@ -0,0 +1,9 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
public sealed class AddressState
{
public required string Name { get; init; }
public required PointerValue Address { get; init; }
}
+38
View File
@@ -0,0 +1,38 @@
using System.Core.Extensions;
using System.Extensions;
namespace Daybreak.API.Models;
public sealed class AsyncRefreshCache<T>(TimeSpan refreshRate, Func<CancellationToken, ValueTask<T>> refreshFunc)
{
private readonly Func<CancellationToken, ValueTask<T>> refreshFunc = refreshFunc.ThrowIfNull();
private readonly SemaphoreSlim semaphoreSlim = new(1);
private T? cache;
private DateTime lastUpdateDateTime = DateTime.MinValue;
private TimeSpan refreshRate = refreshRate;
public async ValueTask<T> GetAsync(CancellationToken cancellationToken)
{
if (this.cache is not null && DateTime.UtcNow - this.lastUpdateDateTime < this.refreshRate)
{
return this.cache;
}
using var ctx = await this.semaphoreSlim.Acquire(cancellationToken);
if (DateTime.UtcNow - this.lastUpdateDateTime > this.refreshRate ||
this.cache is null)
{
this.lastUpdateDateTime = DateTime.UtcNow;
this.cache = await this.refreshFunc(cancellationToken);
}
return this.cache;
}
public void UpdateRefreshRate(TimeSpan newRefreshRate)
{
this.refreshRate = newRefreshRate;
}
}
+23
View File
@@ -0,0 +1,23 @@
using System.Core.Extensions;
namespace Daybreak.API.Models;
public sealed class ByteConsumerEntry(
Guid id, TimeSpan freq, Action<ReadOnlySpan<byte>> handler)
{
private readonly Action<ReadOnlySpan<byte>> handler = handler.ThrowIfNull();
private DateTimeOffset lastConsume = DateTimeOffset.MinValue;
public Guid Id { get; } = id;
public TimeSpan Frequency { get; } = freq;
public void TryConsume(DateTimeOffset currentTime, ReadOnlySpan<byte> value)
{
if (currentTime - this.lastConsume >= this.Frequency)
{
this.handler(value);
this.lastConsume = currentTime;
}
}
}
@@ -0,0 +1,14 @@
using System.Core.Extensions;
namespace Daybreak.API.Models;
public sealed class CallbackRegistration(Guid uid, Action onDispose) : IDisposable
{
private readonly Guid uid = uid;
private readonly Action onDispose = onDispose.ThrowIfNull();
public void Dispose()
{
this.onDispose();
}
}
+24
View File
@@ -0,0 +1,24 @@
namespace Daybreak.API.Models;
public enum Channel : uint
{
Alliance = 0,
Allies = 1, // coop with two groups for instance.
GWCA1 = 2,
All = 3,
GWCA2 = 4,
Moderator = 5,
Emote = 6,
Warning = 7, // shows in the middle of the screen and does not parse <c> tags
GWCA3 = 8,
Guild = 9,
Global = 10,
Group = 11,
Trade = 12,
Advisory = 13,
Whisper = 14,
Count,
// non-standard channel, but useful.
Command
};
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Daybreak.API.Models;
public sealed class HealthCheckEntryResponse
{
[JsonConverter(typeof(JsonStringEnumConverter<HealthStatus>))]
public required HealthStatus Status { get; set; }
public required string Description { get; set; }
public required Dictionary<string, JsonElement> Data { get; set; }
public required List<string> Tags { get; set; }
}
@@ -0,0 +1,13 @@
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Text.Json.Serialization;
namespace Daybreak.API.Models;
public sealed class HealthCheckResponse
{
public required int ProcessId { get; set; }
[JsonConverter(typeof(JsonStringEnumConverter<HealthStatus>))]
public required HealthStatus Status { get; set; }
public required TimeSpan TotalDuration { get; set; }
public required Dictionary<string, HealthCheckEntryResponse> Entries { get; set; }
}
+12
View File
@@ -0,0 +1,12 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
public sealed class HookState
{
public required bool Hooked { get; init; }
public required string Name { get; init; }
public required PointerValue TargetAddress { get; init; }
public required PointerValue ContinueAddress { get; init; }
public required PointerValue DetourAddress { get; init; }
}
+9
View File
@@ -0,0 +1,9 @@
namespace Daybreak.API.Models;
public interface IWorkItem
{
CancellationToken CancellationToken { get; }
void Execute();
void Cancel();
void Exception(Exception ex);
}
+154
View File
@@ -0,0 +1,154 @@
namespace Daybreak.API.Models;
public enum UIMessage : uint
{
None = 0x0,
Resize = 0x8,
InitFrame = 0x9,
DestroyFrame = 0xb,
KeyDown = 0x1e, // wparam = UIPacket::kKeyAction*
KeyUp = 0x20, // wparam = UIPacket::kKeyAction*
MouseClick = 0x22, // wparam = UIPacket::kMouseClick*
MouseClick2 = 0x2e, // wparam = UIPacket::kMouseAction*
MouseAction = 0x2f, // wparam = UIPacket::kMouseAction*
SetLayout = 0x33,
FrameMessage_0x47 = 0x47, // Multiple uses depending on frame
UpdateAgentEffects = 0x10000000 | 0x9,
RerenderAgentModel = 0x10000000 | 0x7, // wparam = uint32_t agent_id
AgentSpeechBubble = 0x10000000 | 0x17,
ShowAgentNameTag = 0x10000000 | 0x19, // wparam = AgentNameTagInfo*
HideAgentNameTag = 0x10000000 | 0x1A,
SetAgentNameTagAttribs = 0x10000000 | 0x1B, // wparam = AgentNameTagInfo*
ChangeTarget = 0x10000000 | 0x20, // wparam = UIPacket::kChangeTarget*
AgentStartCasting = 0x10000000 | 0x27, // wparam = UIPacket::kAgentStartCasting*
ShowMapEntryMessage = 0x10000000 | 0x29, // wparam = { wchar_t* title, wchar_t* subtitle }
SetCurrentPlayerData = 0x10000000 | 0x2A, // fired after setting the worldcontext player name
PostProcessingEffect = 0x10000000 | 0x34, // Triggered when drunk. wparam = UIPacket::kPostProcessingEffect
HeroAgentAdded = 0x10000000 | 0x38, // hero assigned to agent/inventory/ai mode
HeroDataAdded = 0x10000000 | 0x39, // hero info received from server (name, level etc)
ShowXunlaiChest = 0x10000000 | 0x40,
MinionCountUpdated = 0x10000000 | 0x46,
MoraleChange = 0x10000000 | 0x47, // wparam = {agent id, morale percent }
LoginStateChanged = 0x10000000 | 0x50, // wparam = {bool is_logged_in, bool unk }
EffectAdd = 0x10000000 | 0x55, // wparam = {agent_id, GW::Effect*}
EffectRenew = 0x10000000 | 0x56, // wparam = GW::Effect*
EffectRemove = 0x10000000 | 0x57, // wparam = effect id
SkillActivated = 0x10000000 | 0x5b, // wparam ={ uint32_t agent_id , uint32_t skill_id }
UpdateSkillbar = 0x10000000 | 0x5E, // wparam ={ uint32_t agent_id , ... }
UpdateSkillsAvailable = 0x10000000 | 0x5f, // Triggered on a skill unlock, profession change or map load
TitleProgressUpdated = 0x10000000 | 0x65, // wparam = title_id
ExperienceGained = 0x10000000 | 0x66, // wparam = experience amount
WriteToChatLog = 0x10000000 | 0x7E, // wparam = UIPacket::kWriteToChatLog*. Triggered by the game when it wants to add a new message to chat.
WriteToChatLogWithSender = 0x10000000 | 0x7f, // wparam = UIPacket::kWriteToChatLogWithSender*. Triggered by the game when it wants to add a new message to chat.
AllyOrGuildMessage = 0x10000000 | 0x80, // wparam = UIPacket::kAllyOrGuildMessage*
PlayerChatMessage = 0x10000000 | 0x81, // wparam = UIPacket::kPlayerChatMessage*
FloatingWindowMoved = 0x10000000 | 0x83, // wparam = frame_id
FriendUpdated = 0x10000000 | 0x89, // wparam = { GW::Friend*, ... }
MapLoaded = 0x10000000 | 0x8A,
OpenWhisper = 0x10000000 | 0x90, // wparam = wchar* name
Logout = 0x10000000 | 0x9b, // wparam = { bool unknown, bool character_select }
CompassDraw = 0x10000000 | 0x9c, // wparam = UIPacket::kCompassDraw*
OnScreenMessage = 0x10000000 | 0xA0, // wparam = wchar_** encoded_string
DialogBody = 0x10000000 | 0xA4, // wparam = DialogBodyInfo*
DialogButton = 0x10000000 | 0xA1, // wparam = DialogButtonInfo*
TargetNPCPartyMember = 0x10000000 | 0xB1, // wparam = { uint32_t unk, uint32_t agent_id }
TargetPlayerPartyMember = 0x10000000 | 0xB2, // wparam = { uint32_t unk, uint32_t player_number }
VendorWindow = 0x10000000 | 0xB3, // wparam = UIPacket::kVendorWindow
VendorItems = 0x10000000 | 0xB7, // wparam = UIPacket::kVendorItems
VendorTransComplete = 0x10000000 | 0xB9, // wparam = *TransactionType
VendorQuote = 0x10000000 | 0xBB, // wparam = UIPacket::kVendorQuote
StartMapLoad = 0x10000000 | 0xC0, // wparam = { uint32_t map_id, ...}
WorldMapUpdated = 0x10000000 | 0xC5, // Triggered when an area in the world map has been discovered/updated
GuildMemberUpdated = 0x10000000 | 0xD8, // wparam = { GuildPlayer::name_ptr }
ShowHint = 0x10000000 | 0xDF, // wparam = { uint32_t icon_type, wchar_t* message_enc }
WeaponSetSwapComplete = 0x10000000 | 0xE7, // wparam = UIPacket::kWeaponSwap*
WeaponSetSwapCancel = 0x10000000 | 0xE8,
WeaponSetUpdated = 0x10000000 | 0xE9,
UpdateGoldCharacter = 0x10000000 | 0xEA, // wparam = { uint32_t unk, uint32_t gold_character }
UpdateGoldStorage = 0x10000000 | 0xEB, // wparam = { uint32_t unk, uint32_t gold_storage }
InventorySlotUpdated = 0x10000000 | 0xEC, // undocumented. Triggered when an item is moved into a slot
EquipmentSlotUpdated = 0x10000000 | 0xED, // undocumented. Triggered when an item is moved into a slot
InventorySlotCleared = 0x10000000 | 0xEF, // undocumented. Triggered when an item has been removed from a slot
EquipmentSlotCleared = 0x10000000 | 0xF0, // undocumented. Triggered when an item has been removed from a slot
PvPWindowContent = 0x10000000 | 0xF8,
PreStartSalvage = 0x10000000 | 0x100, // { uint32_t item_id, uint32_t kit_id }
TradePlayerUpdated = 0x10000000 | 0x103, // wparam = GW::TraderPlayer*
ItemUpdated = 0x10000000 | 0x104, // wparam = UIPacket::kItemUpdated*
MapChange = 0x10000000 | 0x10F, // wparam = map id
CalledTargetChange = 0x10000000 | 0x113, // wparam = { player_number, target_id }
ErrorMessage = 0x10000000 | 0x117, // wparam = { int error_index, wchar_t* error_encoded_string }
PartyHardModeChanged = 0x10000000 | 0x118, // wparam = { int is_hard_mode }
PartyAddHenchman = 0x10000000 | 0x119,
PartyRemoveHenchman = 0x10000000 | 0x11a,
PartyAddHero = 0x10000000 | 0x11c,
PartyRemoveHero = 0x10000000 | 0x11d,
PartyAddPlayer = 0x10000000 | 0x122,
PartyRemovePlayer = 0x10000000 | 0x124,
DisableEnterMissionBtn = 0x10000000 | 0x128, // wparam = boolean (1 = disabled, 0 = enabled)
ShowCancelEnterMissionBtn = 0x10000000 | 0x12b,
PartyDefeated = 0x10000000 | 0x12d,
PartySearchInviteReceived = 0x10000000 | 0x135, // wparam = UIPacket::kPartySearchInviteReceived*
PartySearchInviteSent = 0x10000000 | 0x137,
PartyShowConfirmDialog = 0x10000000 | 0x138, // wparam = UIPacket::kPartyShowConfirmDialog
PreferenceEnumChanged = 0x10000000 | 0x13E, // wparam = UiPacket::kPreferenceEnumChanged
PreferenceFlagChanged = 0x10000000 | 0x13F, // wparam = UiPacket::kPreferenceFlagChanged
PreferenceValueChanged = 0x10000000 | 0x140, // wparam = UiPacket::kPreferenceValueChanged
UIPositionChanged = 0x10000000 | 0x141, // wparam = UIPacket::kUIPositionChanged
QuestAdded = 0x10000000 | 0x149, // wparam = { quest_id, ... }
QuestDetailsChanged = 0x10000000 | 0x14A, // wparam = { quest_id, ... }
ClientActiveQuestChanged = 0x10000000 | 0x14C, // wparam = { quest_id, ... }. Triggered when the game requests the current quest to change
ServerActiveQuestChanged = 0x10000000 | 0x14E, // wparam = UIPacket::kServerActiveQuestChanged*. Triggered when the server requests the current quest to change
UnknownQuestRelated = 0x10000000 | 0x14F,
DungeonComplete = 0x10000000 | 0x151, // undocumented
MissionComplete = 0x10000000 | 0x152, // undocumented
VanquishComplete = 0x10000000 | 0x154, // undocumented
ObjectiveAdd = 0x10000000 | 0x155, // wparam = UIPacket::kObjectiveAdd*
ObjectiveComplete = 0x10000000 | 0x156, // wparam = UIPacket::kObjectiveComplete*
ObjectiveUpdated = 0x10000000 | 0x157, // wparam = UIPacket::kObjectiveUpdated*
TradeSessionStart = 0x10000000 | 0x160, // wparam = { trade_state, player_number }
TradeSessionUpdated = 0x10000000 | 0x166, // no args
TriggerLogoutPrompt = 0x10000000 | 0x16C, // no args
ToggleOptionsWindow = 0x10000000 | 0x16D, // no args
CheckUIState = 0x10000000 | 0x170, // Undocumented
RedrawItem = 0x10000000 | 0x172, // wparam = uint32_t item_id
CloseSettings = 0x10000000 | 0x174, // Undocumented
ChangeSettingsTab = 0x10000000 | 0x175, // wparam = uint32_t is_interface_tab
GuildHall = 0x10000000 | 0x177, // wparam = gh key (uint32_t[4])
LeaveGuildHall = 0x10000000 | 0x179,
Travel = 0x10000000 | 0x17A,
OpenWikiUrl = 0x10000000 | 0x17B, // wparam = char* url
AppendMessageToChat = 0x10000000 | 0x189, // wparam = wchar_t* message
HideHeroPanel = 0x10000000 | 0x197, // wparam = hero_id
ShowHeroPanel = 0x10000000 | 0x198, // wparam = hero_id
GetInventoryAgentId = 0x10000000 | 0x19c, // wparam = 0, lparam = uint32_t* agent_id_out. Used to fetch which agent is selected
EquipItem = 0x10000000 | 0x19d, // wparam = { item_id, agent_id }
MoveItem = 0x10000000 | 0x19e, // wparam = { item_id, to_bag, to_slot, bool prompt }
InitiateTrade = 0x10000000 | 0x1A0,
InventoryAgentChanged = 0x10000000 | 0x1b0, // Triggered when inventory needs updating due to agent change; no args
OpenTemplate = 0x10000000 | 0x1B9, // wparam = GW::UI::ChatTemplate*
// GWCA Client to Server commands. Only added the ones that are used for hooks, everything else goes straight into GW
SendLoadSkillTemplate = 0x30000000 | 0x3, // wparam = SkillbarMgr::SkillTemplate*
SendPingWeaponSet = 0x30000000 | 0x4, // wparam = UIPacket::kSendPingWeaponSet*
SendMoveItem = 0x30000000 | 0x5, // wparam = UIPacket::kSendMoveItem*
SendMerchantRequestQuote = 0x30000000 | 0x6, // wparam = UIPacket::kSendMerchantRequestQuote*
SendMerchantTransactItem = 0x30000000 | 0x7, // wparam = UIPacket::kSendMerchantTransactItem*
SendUseItem = 0x30000000 | 0x8, // wparam = UIPacket::kSendUseItem*
SendSetActiveQuest = 0x30000000 | 0x9, // wparam = uint32_t quest_id
SendAbandonQuest = 0x30000000 | 0xA, // wparam = uint32_t quest_id
SendChangeTarget = 0x30000000 | 0xB, // wparam = UIPacket::kSendChangeTarget* // e.g. tell the gw client to focus on a different target
SendCallTarget = 0x30000000 | 0x13, // wparam = { uint32_t call_type, uint32_t agent_id } // also used to broadcast morale, death penalty, "I'm following X", etc
SendDialog = 0x30000000 | 0x16, // wparam = dialog_id // internal use
StartWhisper = 0x30000000 | 0x17, // wparam = UIPacket::kStartWhisper*
GetSenderColor = 0x30000000 | 0x18, // wparam = UIPacket::kGetColor* // Get chat sender color depending on channel, output object passed by reference
GetMessageColor = 0x30000000 | 0x19, // wparam = UIPacket::kGetColor* // Get chat message color depending on channel, output object passed by reference
SendChatMessage = 0x30000000 | 0x1B, // wparam = UIPacket::kSendChatMessage*
LogChatMessage = 0x30000000 | 0x1D, // wparam = UIPacket::kLogChatMessage*. Triggered when a message wants to be added to the persistent chat log.
RecvWhisper = 0x30000000 | 0x1E, // wparam = UIPacket::kRecvWhisper*
PrintChatMessage = 0x30000000 | 0x1F, // wparam = UIPacket::kPrintChatMessage*. Triggered when a message wants to be added to the in-game chat window.
SendWorldAction = 0x30000000 | 0x20, // wparam = UIPacket::kSendWorldAction*
SetRendererValue = 0x30000000 | 0x21 // wparam = UIPacket::kSetRendererValue
};
+46
View File
@@ -0,0 +1,46 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Models;
public static class UIPackets
{
public enum MouseButtons
{
Left = 0x0,
Middle = 0x1,
Right = 0x2
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct LogChatMessage(string message, Channel channel)
{
[MarshalAs(UnmanagedType.LPWStr)]
public readonly string Message = message;
public readonly Channel Channel = channel;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct UIChatMessage(Channel channel, string message, Channel channel2)
{
public readonly Channel Channel = channel;
[MarshalAs(UnmanagedType.LPWStr)]
public readonly string Message = message;
public readonly Channel Channel2 = channel2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct KeyAction(uint key)
{
public readonly uint Key = key;
public readonly uint WParam = 0x4000;
public readonly uint LParam = 0x0006;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct MouseClick(MouseButtons mouseButton, uint isDoubleClick, uint unknownTypeScreenPos)
{
public readonly MouseButtons MouseButton = mouseButton;
public readonly uint IsDoubleClick = isDoubleClick;
public readonly uint UnknownTypeScreenPos = unknownTypeScreenPos;
}
}
+80
View File
@@ -0,0 +1,80 @@
namespace Daybreak.API.Models;
public sealed class WorkItem(
Action action,
TaskCompletionSource tcs,
CancellationToken token) : IWorkItem
{
private readonly Action action = action;
private readonly TaskCompletionSource tcs = tcs;
public CancellationToken CancellationToken { get; } = token;
public void Execute()
{
if (this.CancellationToken.IsCancellationRequested)
{
this.tcs.TrySetCanceled(this.CancellationToken);
return;
}
try
{
this.action();
this.tcs.TrySetResult();
}
catch (Exception ex)
{
this.tcs.TrySetException(ex);
}
}
public void Cancel()
{
this.tcs.TrySetCanceled(this.CancellationToken);
}
public void Exception(Exception ex)
{
this.tcs.TrySetException(ex);
}
}
public sealed class WorkItem<T>(
Func<T> func,
TaskCompletionSource<T> tcs,
CancellationToken token) : IWorkItem
{
private readonly Func<T> func = func;
private readonly TaskCompletionSource<T> tcs = tcs;
public CancellationToken CancellationToken { get; } = token;
public void Execute()
{
if (this.CancellationToken.IsCancellationRequested)
{
this.tcs.TrySetCanceled(this.CancellationToken);
return;
}
try
{
this.tcs.TrySetResult(this.func());
}
catch (Exception ex)
{
this.tcs.TrySetException(ex);
}
}
public void Cancel()
{
this.tcs.TrySetCanceled(this.CancellationToken);
}
public void Exception(Exception ex)
{
this.tcs.TrySetException(ex);
}
}
+51
View File
@@ -0,0 +1,51 @@
using System.Runtime.InteropServices;
namespace Daybreak.API;
internal unsafe static partial class NativeMethods
{
public const int STD_OUTPUT_HANDLE = -11;
public const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
public const uint ENABLE_PROCESSED_OUTPUT = 0x0001;
public const int WM_KEYDOWN = 0x0100;
public const int WM_KEYUP = 0x0101;
public const int WM_CHAR = 0x0102;
public const int VK_RIGHT = 0x27;
public const uint MEM_COMMIT = 0x1000;
public const uint MEM_RESERVE = 0x2000;
public const uint PAGE_EXECUTE_READWRITE = 0x40;
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool AllocConsole();
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint GetConsoleWindow();
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint GetStdHandle(int nStdHandle);
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool GetConsoleMode(nint hConsoleHandle, out uint lpMode);
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool SetConsoleMode(nint hConsoleHandle, uint dwMode);
[LibraryImport("user32.dll")]
public static partial nint SendMessageW(nint hWnd, int Msg, nint wParam, nint lParam);
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint VirtualAlloc(
nint lpAddress, nuint dwSize,
uint flAllocationType, uint flProtect);
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool VirtualProtect(
void* lpAddress, nuint dwSize,
uint flNewProtect, out uint lpflOldProtect);
}
@@ -0,0 +1,12 @@
{
"profiles": {
"Daybreak.API": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:51249;http://localhost:51250"
}
}
}
@@ -0,0 +1,39 @@
using Daybreak.API.Models;
using Daybreak.Shared.Models.Api;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Daybreak.API.Serialization;
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(byte[]))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(uint))]
[JsonSerializable(typeof(nuint))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(List<string>))]
[JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(HealthCheckResponse))]
[JsonSerializable(typeof(HealthCheckEntryResponse))]
[JsonSerializable(typeof(HookState))]
[JsonSerializable(typeof(ArraySegment<HookState>))]
[JsonSerializable(typeof(AddressState))]
[JsonSerializable(typeof(ArraySegment<AddressState>))]
[JsonSerializable(typeof(MainPlayerState))]
[JsonSerializable(typeof(QuestLogInformation))]
[JsonSerializable(typeof(QuestInformation))]
[JsonSerializable(typeof(MainPlayerInformation))]
[JsonSerializable(typeof(CharacterSelectInformation))]
[JsonSerializable(typeof(CharacterSelectEntry))]
[JsonSerializable(typeof(BuildEntry))]
[JsonSerializable(typeof(List<BuildEntry>))]
[JsonSerializable(typeof(PartyLoadoutEntry))]
[JsonSerializable(typeof(List<PartyLoadout>))]
[JsonSerializable(typeof(InstanceInfo))]
[JsonSerializable(typeof(TitleInfo))]
[JsonSerializable(typeof(LoginInfo))]
[JsonSerializable(typeof(MainPlayerBuildContext))]
public partial class ApiJsonSerializerContext : JsonSerializerContext
{
}
@@ -0,0 +1,14 @@
namespace Daybreak.API.Serialization;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithSerializationContext(this WebApplicationBuilder builder)
{
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Insert(0, new ApiJsonSerializerContext());
});
return builder;
}
}
@@ -0,0 +1,56 @@
using Daybreak.Shared.Models;
using Daybreak.Shared.Services.MDns;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using System.Extensions.Core;
namespace Daybreak.API.Services;
public sealed class ApiAdvertisingService(
IServer server,
IMDomainNameService mDnsService,
IHostApplicationLifetime lifetime,
ILogger<ApiAdvertisingService> logger) : IHostedService
{
private const string ProcessIdPlaceholder = "{PID}";
private const string ServiceName = $"daybreak-api-{ProcessIdPlaceholder}";
private const string ServiceSubType = "daybreak-api";
private readonly IServerAddressesFeature serverAddressesFeature = server.Features.Get<IServerAddressesFeature>() ?? throw new InvalidOperationException("Server does not support addresses feature.");
private readonly IMDomainNameService mDnsService = mDnsService;
private readonly IHostApplicationLifetime lifetime = lifetime;
private readonly ILogger<ApiAdvertisingService> logger = logger;
private DnsRegistrationToken? dnsToken;
public Task StartAsync(CancellationToken cancellationToken)
{
this.lifetime.ApplicationStarted.Register(this.RegisterDomain);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.dnsToken?.Dispose();
return Task.CompletedTask;
}
private void RegisterDomain()
{
var scopedLogger = this.logger.CreateScopedLogger();
var addresses = this.serverAddressesFeature?.Addresses.ToArray() ?? [];
var port = addresses
.Select(a => new Uri(a).Port)
.Distinct()
.FirstOrDefault();
scopedLogger.LogInformation("Found port {port}", port);
if (port is 0)
{
throw new InvalidOperationException("Unable to start advertising service. No port found");
}
var dnsEntry = ServiceName.Replace(ProcessIdPlaceholder, Environment.ProcessId.ToString());
scopedLogger.LogInformation("Advertising service {service}:{port}", dnsEntry, port);
this.dnsToken = this.mDnsService.RegisterDomain(dnsEntry, (ushort)port, ServiceSubType);
}
}
@@ -0,0 +1,313 @@
using Daybreak.API.Services.Interop;
using Daybreak.Shared.Models.Api;
using System.Core.Extensions;
using System.Extensions.Core;
using System.Runtime.InteropServices;
using ZLinq;
namespace Daybreak.API.Services;
public sealed class CharacterSelectService(
InstanceContextService instanceContextService,
PlatformContextService platformContextService,
UIContextService uiContextService,
GameThreadService gameThreadService,
GameContextService gameContextService,
ILogger<CharacterSelectService> logger)
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private readonly struct LogOutMessage(uint unknown, uint characterSelect)
{
public readonly uint Unknown = unknown;
public readonly uint CharacterSelect = characterSelect;
}
private readonly InstanceContextService instanceContextService = instanceContextService.ThrowIfNull();
private readonly PlatformContextService platformContextService = platformContextService.ThrowIfNull();
private readonly UIContextService uiContextService = uiContextService.ThrowIfNull();
private readonly GameThreadService gameThreadService = gameThreadService.ThrowIfNull();
private readonly GameContextService gameContextService = gameContextService.ThrowIfNull();
private readonly ILogger<CharacterSelectService> logger = logger.ThrowIfNull();
public Task<CharacterSelectInformation?> GetCharacterSelectInformation(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
return this.gameThreadService.QueueOnGameThread(() =>
{
unsafe
{
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
var availableCharsContext = this.gameContextService.GetAvailableChars();
if (availableCharsContext.IsNull)
{
scopedLogger.LogError("Available characters context is not initialized");
return default;
}
var currentUuid = gameContext.Pointer->CharContext->PlayerUuid.ToString();
var availableChars = new List<CharacterSelectEntry>((int)availableCharsContext.Pointer->Size);
foreach(var charContext in *availableCharsContext.Pointer)
{
var nameSpan = charContext.Name.AsSpan();
var name = new string(nameSpan[..nameSpan.IndexOf('\0')]);
availableChars.Add(new CharacterSelectEntry(
charContext.Uuid.ToString(),
name,
charContext.Primary,
charContext.Secondary,
charContext.Campaign,
charContext.MapId,
charContext.Level,
charContext.IsPvp));
}
var currentCharacter = availableChars.FirstOrDefault(c => c.Uuid == currentUuid);
return new CharacterSelectInformation(currentCharacter, availableChars);
}
}, cancellationToken);
}
public async Task<bool> ChangeCharacterByName(string characterName, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
if (await this.ValidateState(cancellationToken) is false)
{
scopedLogger.LogError("Cannot change character while in loading state");
return false;
}
await this.TriggerLogOut(cancellationToken);
var indexResult = await this.WaitForLoginScreenAndGetCurrentAndDesiredIndex(characterName, cancellationToken);
if (indexResult is null)
{
scopedLogger.LogError("Failed to find index by name {name}", characterName);
return false;
}
var currentIndex = indexResult.Value.CurrentIndex;
var desiredIndex = indexResult.Value.DesiredIndex;
scopedLogger.LogInformation("Changing character to {name} with index {index}", characterName, desiredIndex);
var navigateResult = await this.NavigateToCharAndPlay(currentIndex, desiredIndex, cancellationToken);
if (!navigateResult)
{
scopedLogger.LogError("Failed to navigate to character {name} with index {index}", characterName, desiredIndex);
return false;
}
return true;
}
public async Task<bool> ChangeCharacterByUuid(string uuid, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
if (await this.ValidateState(cancellationToken) is false)
{
scopedLogger.LogError("Cannot change character while in loading state");
return false;
}
var desiredCharName = await this.GetCharNameByUuid(uuid, cancellationToken);
if (desiredCharName is null)
{
scopedLogger.LogError("Failed to find character with UUID: {uuid}", uuid);
return false;
}
await this.TriggerLogOut(cancellationToken);
var indexResult = await this.WaitForLoginScreenAndGetCurrentAndDesiredIndex(desiredCharName, cancellationToken);
if (indexResult is null)
{
scopedLogger.LogError("Resolved character by uuid {uuid} but failed to find index by name {name}", uuid, desiredCharName);
return false;
}
var currentIndex = indexResult.Value.CurrentIndex;
var desiredIndex = indexResult.Value.DesiredIndex;
scopedLogger.LogInformation("Changing character to {name} with index {index} and uuid {uuid}", desiredCharName, desiredIndex, uuid);
var navigateResult = await this.NavigateToCharAndPlay(currentIndex, desiredIndex, cancellationToken);
if (!navigateResult)
{
scopedLogger.LogError("Failed to navigate to character {name} with index {index} and uuid {uuid}", desiredCharName, desiredIndex, uuid);
return false;
}
return true;
}
private async Task<bool> ValidateState(CancellationToken cancellationToken)
{
return await this.gameThreadService.QueueOnGameThread(() =>
{
return this.instanceContextService.GetInstanceType() is not API.Interop.GuildWars.InstanceType.Loading;
}, cancellationToken);
}
private async Task<bool> NavigateToCharAndPlay(uint currentIndex, uint desiredIndex, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, cancellationToken);
var result = await this.gameThreadService.QueueOnGameThread(() =>
{
unsafe
{
var preGameContext = this.gameContextService.GetPreGameContext();
if (preGameContext.IsNull)
{
scopedLogger.LogError("Pre-game context is not initialized");
return false;
}
var hwnd = this.platformContextService.GetWindowHandle();
if (hwnd is null or 0)
{
scopedLogger.LogError("Failed to get window handle");
return false;
}
if (preGameContext.Pointer->Index1 == currentIndex)
{
return false; //Not moved yet
}
if (preGameContext.Pointer->Index1 == desiredIndex)
{
// We're on the desired character. Trigger play
NativeMethods.SendMessageW((nint)hwnd.Value, NativeMethods.WM_KEYDOWN, 0x50, 0x00190001);
NativeMethods.SendMessageW((nint)hwnd.Value, NativeMethods.WM_CHAR, 0x70, 0x00190001);
NativeMethods.SendMessageW((nint)hwnd.Value, NativeMethods.WM_KEYUP, 0x50, 0x00190001);
return true;
}
currentIndex = preGameContext.Pointer->Index1;
NativeMethods.SendMessageW((nint)hwnd.Value, NativeMethods.WM_KEYDOWN, NativeMethods.VK_RIGHT, 0x014D0001);
NativeMethods.SendMessageW((nint)hwnd.Value, NativeMethods.WM_KEYUP, NativeMethods.VK_RIGHT, 0x014D0001);
return false;
}
}, cancellationToken);
if (result)
{
return true;
}
}
return false;
}
private async Task<string?> GetCharNameByUuid(string uuid, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
return await this.gameThreadService.QueueOnGameThread(() =>
{
unsafe
{
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
var availableCharsContext = this.gameContextService.GetAvailableChars();
if (availableCharsContext.IsNull)
{
scopedLogger.LogError("Available characters context is not initialized");
return default;
}
foreach (var charContext in *availableCharsContext.Pointer)
{
if (charContext.Uuid.ToString() != uuid)
{
continue;
}
var nameSpan = charContext.Name.AsSpan();
var name = new string(nameSpan[..nameSpan.IndexOf('\0')]);
return name;
}
return default;
}
}, cancellationToken);
}
private async Task TriggerLogOut(CancellationToken cancellationToken)
{
await this.gameThreadService.QueueOnGameThread(() =>
{
var logoutMessage = new LogOutMessage(0, 0);
unsafe
{
this.uiContextService.SendMessage(Models.UIMessage.Logout, (uint)&logoutMessage, 0);
}
}, cancellationToken);
}
private async Task<(uint DesiredIndex, uint CurrentIndex)?> WaitForLoginScreenAndGetCurrentAndDesiredIndex(string desiredCharName, CancellationToken cancellationToken)
{
uint? desiredIndex = default;
uint? currentIndex = default;
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, cancellationToken);
var ready = await this.gameThreadService.QueueOnGameThread(() =>
{
unsafe
{
var preGameContext = this.gameContextService.GetPreGameContext();
if (preGameContext.IsNull)
{
return false;
}
var uiState = 10U;
this.uiContextService.SendMessage(Models.UIMessage.CheckUIState, 0, (uint)&uiState);
var loginScreen = uiState == 2;
if (!loginScreen)
{
return false;
}
for (var i = 0U; i < preGameContext.Pointer->LoginCharacters.Size; i++)
{
var loginCharacter = preGameContext.Pointer->LoginCharacters.Buffer[i];
var charNameSpan = loginCharacter.CharacterName.AsSpan();
var charName = new string(charNameSpan[..charNameSpan.IndexOf('\0')]);
if (charName == desiredCharName)
{
desiredIndex = i;
currentIndex = 0xffffffdd;
return true;
}
}
return false;
}
}, cancellationToken);
if (ready)
{
break;
}
}
if (desiredIndex is not null && currentIndex is not null)
{
return (desiredIndex.Value, currentIndex.Value);
}
return default;
}
}
+77
View File
@@ -0,0 +1,77 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
using System.Core.Extensions;
using System.Extensions;
namespace Daybreak.API.Services;
public sealed class ChatService(
InstanceContextService instanceContextService,
GameThreadService gameThreadService,
UIContextService uiContextService)
{
private readonly SemaphoreSlim semaphoreSlim = new(1);
private readonly InstanceContextService instanceContextService = instanceContextService.ThrowIfNull();
private readonly GameThreadService gameThreadService = gameThreadService.ThrowIfNull();
private readonly UIContextService uiContextService = uiContextService.ThrowIfNull();
/// <summary>
/// Adds a message to the chat. It does not send the message, it only shows it to the player.
/// </summary>
public async ValueTask AddMessageAsync(string message, string? sender, Channel channel, CancellationToken cancellationToken)
{
using var ctx = await this.semaphoreSlim.Acquire(cancellationToken);
await this.gameThreadService.QueueOnGameThread(() =>
{
if (this.instanceContextService.GetInstanceType() is InstanceType.Loading)
{
return;
}
var encodedMessage = string.Create(
3 + message.Length,
message,
static (span, msg) =>
{
span[0] = '\u0108';
span[1] = '\u0107';
msg.AsSpan().CopyTo(span[2..]);
span[^1] = '\u0001';
});
var encodedSender = sender is not null
? string.Create(
3 + sender.Length,
sender,
static (span, msg) =>
{
span[0] = '\u0108';
span[1] = '\u0107';
msg.AsSpan().CopyTo(span[2..]);
span[^1] = '\u0001';
})
: default;
var encoded = encodedSender is null
? encodedMessage
: string.Create(
encodedMessage.Length + encodedSender.Length + 6,
(encodedMessage, encodedSender),
static (span, state) =>
{
span[0] = '\u076b';
span[1] = '\u010a';
state.encodedSender.AsSpan().CopyTo(span[2..]);
span[2 + state.encodedSender.Length] = '\u0001';
span[3 + state.encodedSender.Length] = '\u010b';
state.encodedMessage.AsSpan().CopyTo(span[(4 + state.encodedSender.Length)..]);
span[^2] = '\u0001';
});
using var packet = new UnmanagedStruct<UIPackets.UIChatMessage>(new UIPackets.UIChatMessage(channel, encoded, channel));
this.uiContextService.SendMessage(UIMessage.WriteToChatLog, (nuint)packet.Address, 0x0);
}, cancellationToken);
}
}
@@ -0,0 +1,101 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using System.Core.Extensions;
using System.Extensions.Core;
namespace Daybreak.API.Services.Interop;
public unsafe sealed class AgentContextService : IAddressHealthService
{
private const string AgentArrayMask = "xxxxxxx";
private const int AgentArrayOffset = -0x4;
private static readonly byte[] AgentArrayAddressPattern = [0x8b, 0x0c, 0x90, 0x85, 0xc9, 0x74, 0x19];
private const string PlayerAgentIdMask = "xx????xxxx";
private const int PlayerAgentIdOffset = -0xE;
private static readonly byte[] PlayerAgentIdAddressPattern = [0x5d, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x55, 0x8b, 0xec, 0x53];
private readonly MemoryScanningService memoryScanningService;
private readonly GWAddressCache agentArrayAddress;
private readonly GWAddressCache playerAgentIdAddress;
private readonly ILogger<AgentContextService> logger;
public AgentContextService(
MemoryScanningService memoryScanningService,
ILogger<AgentContextService> logger)
{
this.logger = logger.ThrowIfNull();
this.memoryScanningService = memoryScanningService.ThrowIfNull();
this.agentArrayAddress = new GWAddressCache(this.GetAgentArrayAddress);
this.playerAgentIdAddress = new GWAddressCache(this.GetPreGameContextAddress);
}
public List<AddressState> GetAddressStates()
{
return [
new AddressState
{
Address = this.agentArrayAddress.GetAddress() ?? 0,
Name = nameof(this.agentArrayAddress),
},
new AddressState
{
Address = this.playerAgentIdAddress.GetAddress() ?? 0,
Name = nameof(this.playerAgentIdAddress),
},
];
}
public WrappedPointer<GuildWarsArray<WrappedPointer<AgentContext>>> GetAgentArray()
{
var agentArrayAddress = this.agentArrayAddress.GetAddress();
if (agentArrayAddress is null or 0x0)
{
this.logger.LogError("Failed to get agent array address");
return null;
}
return (GuildWarsArray<WrappedPointer<AgentContext>>*)agentArrayAddress;
}
public uint GetPlayerAgentId()
{
var playerAgentIdAddress = this.playerAgentIdAddress.GetAddress();
if (playerAgentIdAddress is null or 0x0)
{
this.logger.LogError("Failed to get player agent id address");
return 0;
}
return *(uint*)playerAgentIdAddress;
}
private nuint GetAgentArrayAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
var address = this.memoryScanningService.FindAndResolveAddress(AgentArrayAddressPattern, AgentArrayMask, AgentArrayOffset);
if (address is 0)
{
scopedLogger.LogError("Failed to find agent array address");
return 0U;
}
scopedLogger.LogInformation("Agent array address: 0x{address:X8}", address);
return address;
}
private nuint GetPreGameContextAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
var address = this.memoryScanningService.FindAndResolveAddress(PlayerAgentIdAddressPattern, PlayerAgentIdMask, PlayerAgentIdOffset);
if (address is 0)
{
scopedLogger.LogError("Failed to find player agent id address");
return 0U;
}
scopedLogger.LogInformation("Player agent id address: 0x{address:X8}", address);
return address;
}
}
@@ -0,0 +1,85 @@
using Daybreak.API.Interop;
using Daybreak.API.Models;
using System.Core.Extensions;
using System.Extensions.Core;
using System.Runtime.InteropServices;
using System.Security;
namespace Daybreak.API.Services.Interop;
public sealed class ChatHandlingService
: IHostedService, IHookHealthService
{
private const string AddToChatLogMask = "xxxxxx";
private static readonly byte[] AddToChatLogSeq = [0x40, 0x25, 0xFF, 0x01, 0x00, 0x00];
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool AddToChatLog([MarshalAs(UnmanagedType.LPWStr)] string message, Channel channel);
private readonly MemoryScanningService memoryScanningService;
private readonly GWHook<AddToChatLog> addToChatLogHook;
private readonly ILogger<ChatHandlingService> logger;
private CancellationTokenSource? cts = default;
public ChatHandlingService(
MemoryScanningService memoryScanningService,
ILogger<ChatHandlingService> logger)
{
this.logger = logger.ThrowIfNull();
this.memoryScanningService = memoryScanningService.ThrowIfNull();
this.addToChatLogHook = new GWHook<AddToChatLog>(
new GWAddressCache(() => this.memoryScanningService.ToFunctionStart(
this.memoryScanningService.FindAddress(AddToChatLogSeq, AddToChatLogMask))),
this.OnAddToChatLog);
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.cts?.Dispose();
this.cts = new CancellationTokenSource();
return Task.Factory.StartNew(() => this.InitializeHooks(this.cts.Token), this.cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.cts?.Cancel();
this.cts?.Dispose();
this.addToChatLogHook.Dispose();
return Task.CompletedTask;
}
public List<HookState> GetHookStates()
{
return
[
new HookState
{
Hooked = this.addToChatLogHook.Hooked,
Name = nameof(this.addToChatLogHook),
TargetAddress = new PointerValue(this.addToChatLogHook.TargetAddress),
ContinueAddress = new PointerValue(this.addToChatLogHook.ContinueAddress),
DetourAddress = new PointerValue(this.addToChatLogHook.DetourAddress)
}
];
}
private async ValueTask InitializeHooks(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
scopedLogger.LogInformation("Initializing hooks");
while (!this.addToChatLogHook.EnsureInitialized())
{
scopedLogger.LogDebug("Waiting for hook to initialize: {hook}", nameof(this.addToChatLogHook));
await Task.Delay(1000, cancellationToken);
}
scopedLogger.LogInformation("Hooks initialized");
}
private bool OnAddToChatLog(string message, Channel channel)
{
return this.addToChatLogHook.Continue(message, channel);
}
}
@@ -0,0 +1,181 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using System.Core.Extensions;
using System.Extensions.Core;
namespace Daybreak.API.Services.Interop;
public unsafe sealed class GameContextService : IAddressHealthService
{
private const string BaseAddressMask = "xxxxxxx";
private const int BaseAddressOffset = +7;
private static readonly byte[] BaseAddressPattern = [0x50, 0x6A, 0x0F, 0x6A, 0x00, 0xFF, 0x35];
private const string PreGameContextFile = "UiPregame.cpp";
private const string PreGameContextAssertion = "!s_scene";
private const int PreGameContextOffset = 0x34;
private const string AvailableCharsMask = "xx????xxxxxxx";
private const int AvailableCharsOffset = 0x2;
private static readonly byte[] AvailableCharsPattern = [0x8B, 0x35, 0x00, 0x00, 0x00, 0x00, 0x57, 0x69, 0xF8, 0x84, 0x00, 0x00, 0x00];
private readonly MemoryScanningService memoryScanningService;
private readonly GWAddressCache baseAddress;
private readonly GWAddressCache preGameContextAddress;
private readonly GWAddressCache availableCharsAddress;
private readonly ILogger<GameContextService> logger;
public GameContextService(
MemoryScanningService memoryScanningService,
ILogger<GameContextService> logger)
{
this.logger = logger.ThrowIfNull();
this.memoryScanningService = memoryScanningService.ThrowIfNull();
this.baseAddress = new GWAddressCache(this.GetBaseAddress);
this.preGameContextAddress = new GWAddressCache(this.GetPreGameContextAddress);
this.availableCharsAddress = new GWAddressCache(this.GetAvailableCharactersAddress);
}
public List<AddressState> GetAddressStates()
{
return [
new AddressState
{
Address = this.baseAddress.GetAddress() ?? 0,
Name = nameof(this.baseAddress),
},
new AddressState
{
Address = this.preGameContextAddress.GetAddress() ?? 0,
Name = nameof(this.preGameContextAddress),
},
new AddressState
{
Address = this.availableCharsAddress.GetAddress() ?? 0,
Name = nameof(this.availableCharsAddress),
}
];
}
public WrappedPointer<GameContext> GetGameContext()
{
var gameContextAddress = this.GetGameContextAddress();
if (gameContextAddress is 0x0)
{
this.logger.LogError("Failed to get game context address");
return null;
}
return (GameContext*)gameContextAddress;
}
public WrappedPointer<PreGameContext> GetPreGameContext()
{
var preGameContextAddress = this.preGameContextAddress.GetAddress();
if (preGameContextAddress is null or 0x0)
{
this.logger.LogError("Failed to get pre-game context address");
return null;
}
return *(PreGameContext**)preGameContextAddress;
}
public WrappedPointer<GuildWarsArray<CharInfoContext>> GetAvailableChars()
{
var availableCharsAddress = this.availableCharsAddress.GetAddress();
if (availableCharsAddress is null or 0x0)
{
this.logger.LogError("Failed to get available chars address");
return null;
}
return *(GuildWarsArray<CharInfoContext>**)availableCharsAddress;
}
public bool IsMapLoaded()
{
var gameContext = this.GetGameContext();
if (gameContext.IsNull)
{
return false;
}
if (gameContext.Pointer->MapContext is null)
{
return false;
}
return true;
}
private nuint GetGameContextAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
if (this.baseAddress.GetAddress() is not nuint basePtr)
{
scopedLogger.LogError("Failed to get base address");
return default;
}
nuint** baseContext = *(nuint***)basePtr;
if (baseContext is null)
{
scopedLogger.LogError("Failed to get base context address");
return default;
}
var globalContextPtr = baseContext[0x6];
if (globalContextPtr is null)
{
scopedLogger.LogError("Failed to get game context address");
return default;
}
return (nuint)globalContextPtr;
}
private nuint GetBaseAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
var baseAddress = this.memoryScanningService.FindAndResolveAddress(BaseAddressPattern, BaseAddressMask, BaseAddressOffset);
if (baseAddress is 0)
{
scopedLogger.LogError("Failed to find base address");
return 0U;
}
scopedLogger.LogInformation("Base address: 0x{address:X8}", baseAddress);
return baseAddress;
}
private unsafe nuint GetPreGameContextAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
var preGameContextAddress = this.memoryScanningService.FindAssertion(PreGameContextFile, PreGameContextAssertion, 0, PreGameContextOffset);
if (preGameContextAddress is 0)
{
scopedLogger.LogError("Failed to find pre-game context address");
return 0U;
}
preGameContextAddress = *(nuint*)preGameContextAddress;
scopedLogger.LogInformation("Pre-game context address: 0x{address:X8}", preGameContextAddress);
return preGameContextAddress;
}
private unsafe nuint GetAvailableCharactersAddress()
{
var scopedLogger = this.logger.CreateScopedLogger();
var availableCharsAddress = this.memoryScanningService.FindAddress(AvailableCharsPattern, AvailableCharsMask, AvailableCharsOffset);
if (availableCharsAddress is 0)
{
scopedLogger.LogError("Failed to find available chars address");
return 0U;
}
scopedLogger.LogInformation("Available chars address: 0x{address:X8}", availableCharsAddress);
return availableCharsAddress;
}
}
@@ -0,0 +1,142 @@
using Daybreak.API.Interop;
using Daybreak.API.Models;
using System.Collections.Concurrent;
using System.Core.Extensions;
using System.Extensions.Core;
using System.Runtime.InteropServices;
using System.Security;
namespace Daybreak.API.Services.Interop;
public sealed class GameThreadService
: IHostedService, IHookHealthService
{
private const string LeaveGameThreadFile = "FrApi.cpp";
private const string LeaveGameThreadAssertion = "renderElapsed >= 0";
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void LeaveGameThread(nint ctx);
private readonly MemoryScanningService memoryScanningService;
private readonly GWHook<LeaveGameThread> leaveGameThreadHook;
private readonly ILogger<GameThreadService> logger;
private readonly ConcurrentQueue<IWorkItem> queuedItems = [];
private readonly ConcurrentDictionary<Guid, Action> registeredCallbacks = [];
private CancellationTokenSource? cts = default;
public GameThreadService(
MemoryScanningService memoryScanningService,
ILogger<GameThreadService> logger)
{
this.logger = logger.ThrowIfNull();
this.memoryScanningService = memoryScanningService.ThrowIfNull();
this.leaveGameThreadHook = new GWHook<LeaveGameThread>(
new GWAddressCache(() => this.memoryScanningService.ToFunctionStart(
this.memoryScanningService.FindAssertion(LeaveGameThreadFile, LeaveGameThreadAssertion, 0, 0), 0x300)),
this.OnLeaveGameThread);
}
public Task StartAsync(CancellationToken cancellationToken)
{
this.cts?.Dispose();
this.cts = new CancellationTokenSource();
return Task.Factory.StartNew(() => this.InitializeHooks(this.cts.Token), this.cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
public Task StopAsync(CancellationToken cancellationToken)
{
this.cts?.Cancel();
this.cts?.Dispose();
this.leaveGameThreadHook.Dispose();
return Task.CompletedTask;
}
public Task QueueOnGameThread(Action action, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource();
this.queuedItems.Enqueue(new WorkItem(action, taskCompletionSource, cancellationToken));
return taskCompletionSource.Task;
}
public Task<T> QueueOnGameThread<T>(Func<T> action, CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<T>();
this.queuedItems.Enqueue(new WorkItem<T>(action, taskCompletionSource, cancellationToken));
return taskCompletionSource.Task;
}
public CallbackRegistration RegisterCallback(Action action)
{
var uid = new Guid();
var registration = new CallbackRegistration(uid, () => this.registeredCallbacks.TryRemove(uid, out _));
this.registeredCallbacks[uid] = action;
return registration;
}
public List<HookState> GetHookStates()
{
return
[
new HookState
{
Hooked = this.leaveGameThreadHook.Hooked,
Name = nameof(this.leaveGameThreadHook),
TargetAddress = new PointerValue(this.leaveGameThreadHook.TargetAddress),
ContinueAddress = new PointerValue(this.leaveGameThreadHook.ContinueAddress),
DetourAddress = new PointerValue(this.leaveGameThreadHook.DetourAddress)
}
];
}
private async ValueTask InitializeHooks(CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
scopedLogger.LogInformation("Initializing hooks");
while (!this.leaveGameThreadHook.EnsureInitialized())
{
scopedLogger.LogDebug("Waiting for hook to initialize: {hook}", nameof(this.leaveGameThreadHook));
await Task.Delay(1000, cancellationToken);
}
scopedLogger.LogInformation("Hooks initialized");
}
private void OnLeaveGameThread(nint ctx)
{
var scopedLogger = this.logger.CreateScopedLogger();
while(this.queuedItems.TryDequeue(out var item))
{
try
{
if (item.CancellationToken.IsCancellationRequested)
{
item.Cancel();
continue;
}
item.Execute();
}
catch(Exception ex)
{
scopedLogger.LogError(ex, "Error executing action on game thread");
item.Exception(ex);
}
}
foreach(var callback in this.registeredCallbacks)
{
try
{
callback.Value();
}
catch(Exception ex)
{
scopedLogger.LogError(ex, "Error executing registered callback {uid}", callback.Key);
}
}
this.leaveGameThreadHook.Continue(ctx);
}
}
@@ -0,0 +1,8 @@
using Daybreak.API.Models;
namespace Daybreak.API.Services.Interop;
public interface IAddressHealthService
{
List<AddressState> GetAddressStates();
}

Some files were not shown because too many files have changed in this diff Show More