334 Commits
0.5a ... main

Author SHA1 Message Date
3b901d8e6c 更新 .github/workflows/nightly-build.yaml
Some checks failed
Build and Push Docker Image to GHCR / build (push) Failing after 26s
Nightly-build / build (6.0.x) (push) Failing after 4m13s
2024-10-26 03:14:03 +08:00
9764484f4a 更新 .github/workflows/nightly-build.yaml
Some checks failed
Build and Push Docker Image to GHCR / build (push) Has been cancelled
2024-10-26 03:13:45 +08:00
5f781f6f48 更新 .github/workflows/nightly-build.yaml
Some checks failed
Build and Push Docker Image to GHCR / build (push) Failing after 1m28s
Nightly-build / build (6.0.x) (push) Failing after 2m6s
2024-10-26 03:08:06 +08:00
fcd7e18d9b Docker conversion of server component (#54)
* Docker conversion

* UDP not TCP

* Update docker-compose.yml
2024-04-14 10:59:43 +08:00
564d1467f6 Merge pull request #57 from RAGECOOP/dev-nightly
Dev nightly
2023-11-20 13:44:47 -03:00
8bd6ea15a3 Remove global limits
world limits will be applied to all players instead
Fix explosions when remote and local vehicles exist at same position
Fix "floating corpses" issue when remote NPCs are killed
2023-11-20 11:32:18 -03:00
3c7f16f7a4 Add ped limit menu setting
Fix vehicle limit menu setting
Fix player blip disappearing after switching character
2023-11-12 10:50:37 -03:00
393a401860 Always sync vehicles with synced peds 2023-11-07 20:27:14 -03:00
39b780e518 Merge pull request #56 from RAGECOOP/dev-nightly 2023-10-23 10:48:52 -03:00
4a54851c51 Update AssemblyInfo.cs 2023-10-23 10:36:57 -03:00
f0eefa575c Always sync player ped 2023-10-22 18:59:19 -03:00
3fc813b2d8 Fix bug disconnecting from server 2023-10-22 17:14:50 -03:00
fbff72ff14 Update LemonUI 2023-10-18 16:57:19 -03:00
4e52407591 Update nightly-build.yaml 2023-10-18 16:56:44 -03:00
4e6fde129d Add global limits 2023-10-18 10:41:34 -03:00
92e1a970a8 Show kill notification 2023-10-06 23:46:08 -03:00
d0eb0b7818 Bump version
Add kill message
2023-10-06 16:36:49 -03:00
14151d7b2c Add option to disable blip and nametag display 2023-08-14 14:08:30 -03:00
1f8d70a520 Fix player dying when switching characters in missions 2023-08-13 19:29:48 -03:00
2cb5baf5f7 Fix player dying when switching characters 2023-08-09 23:50:47 -03:00
34e33937e2 Merge pull request #49 from RAGECOOP/dev-nightly
Update README.md
2023-07-26 15:46:58 -03:00
9287aba0f9 Update README.md 2023-07-26 15:42:08 -03:00
e88e903096 Merge pull request #48 from RAGECOOP/dev-nightly
Dev nightly
2023-07-26 15:27:40 -03:00
99642fd40c Update SHVDN 2023-07-26 15:12:58 -03:00
2fbf06b504 Use Lidgren.Network release build 2023-07-24 10:40:33 -03:00
13b771ec9f Fix exception entering vehicle as passenger 2023-07-24 10:39:37 -03:00
3b987f59e0 Remove update menu 2023-07-14 09:43:00 -03:00
de96f29097 Don't delete peds in vehicle 2023-07-14 09:39:06 -03:00
6136cbfc14 Allow multiple servers on same address
with different ports
2023-07-14 09:38:02 -03:00
ed145aedd6 Update master server 2023-07-14 09:36:49 -03:00
3f3b5fd2d0 Closes #40 2022-09-30 23:40:14 +08:00
9173e9a99e Fix non-ambient peds get deleted 2022-09-30 23:27:37 +08:00
6e64c458df Merge branch 'dev-nightly' of https://github.com/RAGECOOP/RAGECOOP-V into dev-nightly 2022-09-09 11:24:02 +08:00
76f959abe9 Stuff 2022-09-09 11:23:46 +08:00
f2e85d66ab Update build-test.yaml 2022-09-09 11:15:27 +08:00
e30ef1f4bd Update actions 2022-09-08 20:09:52 -07:00
6e8f6e78f6 Add build test action 2022-09-08 20:05:26 -07:00
f28c83ccbd Merge pull request #38 from RAGECOOP/dev-nightly
Bump to 1.5.4
2022-09-07 22:44:48 -07:00
a83821b3d2 Version bump 2022-09-08 13:19:09 -07:00
3b5436064e Don't announce if already present in master server 2022-09-08 13:15:34 -07:00
c4b321324e Filter out white space for reload key parsing 2022-09-08 12:59:14 -07:00
ba8d525ddf Merge pull request #37 from RAGECOOP/dev-nightly
Rewrite packet system
2022-09-07 21:45:14 -07:00
884e2f39f0 Yet another code cleanup 2022-09-08 12:41:56 -07:00
76c529f1d1 Rewrote packet system to reduce heap allocation 2022-09-08 12:37:06 -07:00
df0064bd38 Update nightly-build.yaml 2022-09-07 21:01:22 -07:00
f1fc96bbd7 Fix weird projectile shooting 2022-09-07 11:08:25 +08:00
23e9326f5f Fix stuff, add listening address display and projectile shoot prediction 2022-09-07 09:52:40 +08:00
4621fb4987 Some code cleanup and formatting 2022-09-06 21:46:35 +08:00
6c82895fa7 code cleaned up 2022-09-05 13:02:09 +02:00
84b040766f Simplify GetBytesFromObject 2022-08-27 14:23:28 +08:00
f44558cd3b Add some client API 2022-08-27 14:17:10 +08:00
accdbbcbc6 blah 2022-08-27 13:53:03 +08:00
2d4107f35e Fix traffic 2022-08-27 12:40:47 +08:00
bac53fd769 Load scripts from main assembly only 2022-08-26 22:58:03 +08:00
8f63fee5b5 Delete temp directory 2022-08-25 22:34:54 +08:00
8d0ad0b600 Show runtime information 2022-08-25 22:32:01 +08:00
dc08c0c1f6 Added support for loading runtime-specific libraries 2022-08-25 21:51:09 +08:00
faaa856aa5 Instructional buttons for popup 2022-08-25 00:58:21 +08:00
eab64f9254 Check fixed data every 100 frames 2022-08-24 22:38:50 +08:00
6c936cb8f9 Show help in popup 2022-08-24 19:26:40 +08:00
83a37f8556 blah 2022-08-24 18:48:21 +08:00
bb4eacce26 Don't disable traffic if not on server 2022-08-24 18:47:59 +08:00
d5b71db5d4 💩 2022-08-23 23:21:35 +08:00
89ebd0305c Fix ped running not displayed 2022-08-23 18:10:52 +08:00
b209202292 Add option to show entity owner 2022-08-23 17:43:24 +08:00
f199241ed8 Time for a release 2022-08-23 16:29:43 +08:00
50229116a7 blah 2022-08-23 14:04:18 +08:00
f192caeae1 Add sdk 2022-08-23 14:03:33 +08:00
6005b2ba11 Add slipstreaming 2022-08-23 13:51:38 +08:00
6e213611cc Fix deluxo wing 2022-08-23 13:45:45 +08:00
347d65af62 Expose chat meesage API to client script 2022-08-23 13:34:12 +08:00
636ee3a33f Fix memory stream close on send 2022-08-23 13:22:14 +08:00
8ff08e0804 Add resource package support 2022-08-23 12:21:17 +08:00
6d7fe58719 Ped sync tweaks 2022-08-23 10:55:54 +08:00
c7e14ef191 Fix disable traffic not always work 2022-08-22 21:59:01 +08:00
39eb5ef80a Hmm... 2022-08-22 19:38:04 +08:00
d25c2539e0 Tweaks 2022-08-22 19:27:25 +08:00
eb31a33104 Fix vibration on stopped vehicle 2022-08-22 19:25:03 +08:00
8b3f8ffd9b Check ped weapon on full sync only 2022-08-22 19:13:52 +08:00
cca09c4178 Fixed ped weapon display after exiting vehicle 2022-08-22 19:08:09 +08:00
871bb7e64e ver 2022-08-22 17:57:43 +08:00
02da3ce8af Fix voice on P2P mode 2022-08-22 17:55:58 +08:00
711ba62fc2 Update installer 2022-08-22 17:30:15 +08:00
8f7d3135db Revert "Fix output path overlap"
This reverts commit 2af9482da9.
2022-08-22 17:27:35 +08:00
cb563a1bf8 Revert "Update nightly-build.yaml"
This reverts commit 6720636d48.
2022-08-22 17:27:29 +08:00
a84ab42a42 blah 2022-08-22 17:01:08 +08:00
b3e285f92f Wait ten minutes before updating 2022-08-22 16:59:00 +08:00
01433f0de6 Restore damage model for aircraft 2022-08-22 11:36:52 +08:00
c034cd8aa9 Fix traffic 2022-08-22 10:01:09 +08:00
363c2ccb00 Don't disable vehicle enter/exit key 2022-08-22 09:40:02 +08:00
7380c162be Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-22 09:34:52 +08:00
f1c7192029 Change kicking options 2022-08-22 09:33:54 +08:00
6720636d48 Update nightly-build.yaml 2022-08-22 00:46:46 +08:00
2af9482da9 Fix output path overlap 2022-08-22 00:36:43 +08:00
bf22c17bba Add AntiAsshole and host display 2022-08-22 00:23:46 +08:00
01492a0c55 Auto server update should work now 2022-08-21 19:56:59 +08:00
c17b234057 Update AssemblyInfo.cs 2022-08-21 19:41:25 +08:00
739577bbf1 Hmm... 2022-08-21 19:30:27 +08:00
2d2da624e4 Add automatic server update (need testing) 2022-08-21 19:13:12 +08:00
9e66762061 Add ZeroTier check to installer 2022-08-21 15:13:36 +08:00
84d578366a Fix installer 2022-08-21 14:28:37 +08:00
03bc3a7742 blah 2022-08-21 14:22:41 +08:00
5bfc7f836e Fix vehicle seat sync 2022-08-21 14:15:01 +08:00
54977c79f1 Fix unintended carjacking 2022-08-21 14:12:44 +08:00
0352bfa328 Add menyoo and directory check for installer 2022-08-21 13:42:28 +08:00
6fbc386b45 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-21 12:37:47 +08:00
203e3b2528 Add Client.EntitiesCount 2022-08-21 12:37:37 +08:00
c9aa995224 Update README.md 2022-08-21 12:17:20 +08:00
c201b94897 Better vehicle rotation display 2022-08-20 22:36:48 +08:00
87f873d1a5 blah 2022-08-20 22:22:06 +08:00
6c355b109d Fix player blip (again) 2022-08-20 22:15:01 +08:00
2ef3012672 Fix installer not overwriting files 2022-08-20 17:27:05 +08:00
7229574ff4 Hmm 2022-08-20 17:17:49 +08:00
eff9fd50b1 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-20 15:31:03 +08:00
e2cbf26360 Fix installer output 2022-08-20 15:30:54 +08:00
b0f448fd5f Fix insyaller output 2022-08-20 15:29:44 +08:00
721c4d3dee Add client installer 2022-08-20 15:20:25 +08:00
87dfc18d91 Smoother ped heading calibration 2022-08-20 12:33:55 +08:00
fa4fe2c10f Fix player blip 2022-08-20 11:32:35 +08:00
3a088ddbfc Fix blip duplication and name issue 2022-08-20 11:08:51 +08:00
52cc9b647c Vehicle duplication prevention 2022-08-19 22:31:13 +08:00
1aa23901d5 Update lidgren again 2022-08-19 22:10:01 +08:00
52584d5774 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-19 21:43:34 +08:00
62bf15dede default LogLevel to 0 2022-08-19 21:43:23 +08:00
722ca16f57 Update README.md 2022-08-19 21:39:40 +08:00
3e61498366 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-19 21:35:08 +08:00
1ff2fa5691 Update lidgren 2022-08-19 21:35:05 +08:00
5088973474 Update README.md 2022-08-19 21:29:24 +08:00
b63378f4d0 Update README.md 2022-08-19 21:28:44 +08:00
f234830c54 Fix stuff 2022-08-18 22:08:06 +08:00
5075289657 ? 2022-08-18 21:48:42 +08:00
c20118aac1 Vehicle cleanup 2022-08-18 20:44:39 +08:00
c4312faf4a blah 2022-08-18 18:59:38 +08:00
7566423889 Security tweaks 2022-08-18 17:45:08 +08:00
be13e0d102 Small projectile fix 2022-08-18 08:43:10 +08:00
490802862a Huh? 2022-08-18 08:26:41 +08:00
e369a50b89 Add vehicle parachute sync 2022-08-18 08:25:15 +08:00
13a6431248 Add rocket boost sync 2022-08-17 23:18:06 +08:00
cb73db7146 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-17 22:56:27 +08:00
9dcd6bb363 Apply prediction on server entity update 2022-08-17 22:50:17 +08:00
842e1435f2 aargh..... 2022-08-17 01:54:14 +08:00
4d03f8e371 blahblahblah 2022-08-16 23:31:17 +08:00
e040d53cc2 blah 2022-08-16 23:18:34 +08:00
22bfa43ec5 Update version 2022-08-16 23:17:04 +08:00
7123bea8e5 Small fix 2022-08-16 22:23:32 +08:00
973f6873e4 Fix some control not being ignored on chat 2022-08-16 22:16:18 +08:00
efb759b9f9 blah 2022-08-16 20:07:25 +08:00
ad3ff96f61 This is it 2022-08-16 19:51:56 +08:00
e156b2195f Display ping (round-trip time) 2022-08-16 13:44:56 +08:00
73a99e033d Update DevTool 2022-08-15 22:43:32 +08:00
b44d722b5f Closes #24 2022-08-15 22:41:17 +08:00
307b6365f7 Closes #25 2022-08-15 22:38:31 +08:00
a49ee19d3d Closes #17 2022-08-15 22:36:43 +08:00
9c5c1da4c9 Add champion weapon, (closes #21) 2022-08-15 22:35:43 +08:00
0348296dc1 Add a bunch of vehicle weapons (closes #23) 2022-08-15 22:34:35 +08:00
adb9439226 Add BRUISER3, BRUTUS3, MONSTER5 weapon sync (closes #26) 2022-08-15 22:21:47 +08:00
ae10da874b Hmm... 2022-08-15 21:25:42 +08:00
cb91486848 Update SHVDN 2022-08-15 20:50:26 +08:00
3289c03fd2 Vehicle seat fix 2022-08-15 20:04:08 +08:00
d4ffb0c929 blah 2022-08-15 19:40:24 +08:00
3c089e2c47 Fix looking coordinate in first person 2022-08-15 19:39:36 +08:00
7a4e4182b5 Add drive-by weapon display
This still have some issues:
ped not aiming consistently
sometimes not working at all
doesn't work for throwable weapons
2022-08-15 18:25:43 +08:00
aa6c45b20d Voice tweaks 2022-08-15 16:13:53 +08:00
0943a7992f blah 2022-08-15 00:19:00 +02:00
d30e1f6bb3 Added mouth animation when speaking 2022-08-14 23:35:36 +02:00
0c71d4dbaa Assign Client.Player.Owner immediately 2022-08-15 00:12:07 +08:00
c78fbd89bc Update README.md 2022-08-14 19:10:45 +08:00
dc83cd5be9 Ped sync improvements 2022-08-14 19:06:51 +08:00
b5f6fc578d Projectile refactor 2022-08-14 17:08:43 +08:00
4acf964f0a Small ragdoll tweak 2022-08-14 13:56:10 +08:00
560f092304 Don't set heading when aiming 2022-08-14 13:21:13 +08:00
8305d9997a Better vehicle weapon handling and muzzle flash 2022-08-14 13:04:39 +08:00
d68941b3ac blah 2022-08-14 10:46:42 +08:00
67c4646198 Fix update not deleting old assemblies 2022-08-14 10:45:39 +08:00
a7dab1e0e1 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-14 10:36:08 +08:00
2b8a3db6bf Retore traffic on disconnect 2022-08-14 10:35:59 +08:00
f9609afd8a Small update for voice chat 2022-08-14 02:26:59 +02:00
609fd90561 Small tweaks 2022-08-13 16:59:09 +08:00
a7d64d241d New vehicle seat sync and projectile fix 2022-08-13 16:14:18 +08:00
b436abf131 Some hint 2022-08-13 11:42:29 +08:00
c14221f1a3 Remove json attributes 2022-08-13 11:42:15 +08:00
abbc871d1a Small fix 2022-08-13 11:28:35 +08:00
edb8838455 Update libs 2022-08-13 11:05:03 +08:00
4cca75ee35 Merge pull request #32 from RAGECOOP/voice-chat
Add voice chat
2022-08-13 10:55:16 +08:00
669b457275 Don't send voice back 2022-08-13 10:54:39 +08:00
2ef0060037 Added ClearAll() for voice 2022-08-13 04:03:01 +02:00
55649f26ae Use ConcurrentDictionary for ServerEntities 2022-08-13 09:47:13 +08:00
6bb7d5d0fd Merge branch 'voice-chat' of https://github.com/RAGECOOP/RAGECOOP-V into voice-chat 2022-08-13 03:39:16 +02:00
8f5deaa33b More voice changes 2022-08-13 03:39:11 +02:00
91b0afb6ef Remove Acceleration 2022-08-13 09:31:09 +08:00
4dc3cc5c28 Fix voice and add Server.Forward() and Server.SendToAll() 2022-08-13 08:56:05 +08:00
f9a411833a small fix 2022-08-13 02:39:18 +02:00
d24d786fd3 Better packet reuse 2022-08-13 08:22:14 +08:00
edae7a075a blub 2022-08-13 02:19:40 +02:00
85b028e3e7 Added NAudio and first voice test 2022-08-13 00:52:34 +02:00
36de46a2ce Fixed warnings 2022-08-12 20:48:31 +02:00
6e5f72ee70 blah 2022-08-12 20:58:17 +08:00
8d450813e1 ZeroTier intergration 2022-08-12 20:40:50 +08:00
495e5dc6b0 Better server info handling 2022-08-12 18:10:56 +08:00
0bd1d2c2a7 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-12 18:07:06 +08:00
339f817c7e ZeroTier helper 2022-08-12 18:02:43 +08:00
b583618e6c Merge pull request #31 from xEntenKoeniqx/main
Cleaned up (Client)
2022-08-11 21:27:18 +08:00
dc0fafbe9e Fixed failed build of github-actions 2022-08-11 15:22:36 +02:00
b9e22a880f Cleaned up (Client) 2022-08-11 14:59:09 +02:00
cb7f0290ec "Entities" cleaned up 2022-08-11 14:48:19 +02:00
d70c739208 Cleaned up 2022-08-11 14:29:18 +02:00
29202906e1 Fix stuff 2022-08-11 19:42:30 +08:00
1d3e53f734 Packet recycling 2022-08-11 18:25:01 +08:00
a26a799a9f Variables renaming 2022-08-11 16:29:29 +08:00
033d0a3f2e Move some stuff to core 2022-08-11 16:25:38 +08:00
95e302a698 Fix player disconnect 2022-08-11 15:38:19 +08:00
ff9e16bd5e Restore 2022-08-10 20:42:47 +08:00
01c668b159 blah 2022-08-08 18:07:22 +08:00
74e51c8070 Display remote blip 2022-08-08 17:47:09 +08:00
01524b1796 Move CurrentWeaponHash to full sync 2022-08-08 17:29:15 +08:00
83d79b0a17 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-08 17:03:53 +08:00
c3d7c2b335 P2P connectivity 2022-08-08 17:03:41 +08:00
6b6125eb09 Update DownloadManager.cs 2022-08-08 10:19:48 +08:00
09bb121ffe blah 2022-08-06 12:33:05 +08:00
a50be5b869 Some stuff for the upcoming change 2022-08-06 12:32:04 +08:00
ca5ef8cf4c read packet with BinaryReader 2022-08-06 11:40:38 +08:00
4e33956acd Faster way to get client by username 2022-08-06 10:58:24 +08:00
9aae315e11 Simplify 2022-08-06 10:49:55 +08:00
742e7ea998 Packet refactor 2022-08-06 10:43:24 +08:00
69bd4c837c Delete dll when installing update 2022-08-05 17:24:51 +08:00
8c10d24842 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-05 16:13:56 +08:00
6b681afdee Fix wheels 2022-08-05 16:13:40 +08:00
6186497999 blah 2022-08-04 19:18:22 +08:00
2571da594e Remove ResourceDomain 2022-08-04 19:13:39 +08:00
7c52cad569 Remove mscoree reference 2022-08-04 19:07:08 +08:00
303bc940f2 Move network info option to debug menu 2022-08-04 17:47:57 +08:00
eda903abde Sync every 30 ms 2022-08-04 17:38:28 +08:00
5d5bc668eb Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-08-04 17:14:11 +08:00
6e5653a168 Add latency simulation 2022-08-04 17:14:07 +08:00
87343b8255 Update README.md 2022-08-04 15:11:57 +08:00
cb21291983 Update README.md 2022-08-04 15:02:54 +08:00
8dd2241590 Update SyncedVehicle.cs 2022-08-04 08:33:18 +08:00
858fa81ea2 blah 2022-08-02 17:43:48 +08:00
8d6c70b75e Ok, this might look junky... 2022-08-02 17:43:18 +08:00
ffd6ce23c3 Add vehicle velocity prediction/acceleration 2022-08-02 16:42:40 +08:00
5a882d0bc8 Fix announce thread exception 2022-08-02 15:55:26 +08:00
cb1b4f4970 Update Program.cs 2022-08-01 23:20:22 +08:00
0de040548d Update FindScript() 2022-08-01 17:07:08 +08:00
40f4cf88c9 Back to velocity 2022-08-01 16:56:46 +08:00
275b39fb1e Add digits 2022-08-01 16:56:32 +08:00
fe5da3cf90 Check 2022-08-01 07:14:24 +08:00
4ed4e37604 Revert "Set client to null on disconnect"
This reverts commit 10552d119f.
2022-08-01 07:13:05 +08:00
10552d119f Set client to null on disconnect 2022-07-31 21:03:47 +08:00
d4e0e6f0ab Stricter username check 2022-07-31 20:56:51 +08:00
e2fce3cf9b Add some client API back 2022-07-31 20:47:04 +08:00
ad5ffc22ac blah 2022-07-30 21:55:57 +08:00
f980790bd7 blah 2022-07-30 21:54:45 +08:00
df8519961a blah 2022-07-30 21:53:42 +08:00
171a6be736 Revert "Squared"
This reverts commit 91035d9a3f.
2022-07-30 21:51:56 +08:00
91035d9a3f Squared 2022-07-30 21:37:15 +08:00
60e7d91409 Read ped speed 2022-07-30 15:45:27 +08:00
d05d1dac85 predict projectile positioin 2022-07-30 14:09:07 +08:00
8184b4d155 Predict projectile position 2022-07-30 13:43:54 +08:00
a0d5f9d2df stricter flip check 2022-07-30 12:50:04 +08:00
49f7f9e3e1 Calibrate position with force 2022-07-30 12:39:09 +08:00
911d8d5d64 Fix player PlayerInfoUpdate 2022-07-30 12:03:57 +08:00
3015ffe3ba Revert "flush"
This reverts commit 459cfccff5.
2022-07-30 11:44:14 +08:00
eb74bcf987 Update vehicle sync 2022-07-29 22:35:20 +08:00
b63898d8b9 Update vehicle sync 2022-07-29 22:26:19 +08:00
459cfccff5 flush 2022-07-29 21:53:26 +08:00
fdb66cba6d Fix chat message 2022-07-29 21:27:04 +08:00
93d705e402 Latency fix 2022-07-29 21:15:23 +08:00
c8bbdc69d0 Update latency detection method
Plus some server code refactoring
2022-07-29 20:35:39 +08:00
9b1587f334 Ignore API assemblies 2022-07-29 19:11:31 +08:00
3e67cc519e Ignore ready 2022-07-29 18:44:17 +08:00
eee6f614f1 Encrypt chat message 2022-07-29 18:17:45 +08:00
62e5bad6ac Resource interoperability 2022-07-29 17:44:02 +08:00
163f342d7a Change Sender to Client in EventArgs 2022-07-29 16:17:33 +08:00
370247eef2 Allow raising event when sending chat message wth API 2022-07-29 15:07:52 +08:00
f22ecb03f2 Fix welcome message 2022-07-29 14:51:52 +08:00
577857ab5a Hash password at server side 2022-07-29 14:51:17 +08:00
095d16a57e rix 2022-07-29 01:31:34 +08:00
862c2c0a71 Update Server.cs 2022-07-29 01:13:32 +08:00
d7e9ce8ff1 Don't invoke chat message if it's a command 2022-07-29 01:08:34 +08:00
e657a02f7a Update RageCoop.Client.csproj 2022-07-29 00:52:57 +08:00
182ff877f0 Update RageCoop.Client.csproj 2022-07-29 00:44:31 +08:00
521358abb5 Update README.md 2022-07-29 00:36:36 +08:00
b62aed5c5a Update RageCoop.Client.csproj 2022-07-29 00:30:33 +08:00
5c4e31806d cd /d 2022-07-28 18:00:13 +08:00
9f8d695d45 cd 2022-07-28 17:55:40 +08:00
e0e5f52d36 Send message back 2022-07-28 17:50:41 +08:00
9fda563438 Invoke constructor later 2022-07-28 17:46:34 +08:00
6d5bb285c9 Fix client copy 2022-07-28 17:03:29 +08:00
7ac405b8f9 Embed referenced assemblies 2022-07-28 15:46:15 +08:00
45d53bd38c Memory patch for weapon/radio slowdown/vignetting 2022-07-25 22:22:57 +08:00
cc52f66f5d Read quaternion and rotation directly from memory 2022-07-25 19:07:58 +08:00
e072c2aee8 Read entity position directly from memory 2022-07-23 23:45:20 +08:00
e52135c343 Allow sending chat message and command in console 2022-07-22 19:43:48 +08:00
cb53e8a664 Delay vehicle repair, fix vehicle if BodyHealth increased 2022-07-21 23:10:56 +08:00
44a8445b54 Delay vehicle repair 2022-07-21 22:42:29 +08:00
b0a197cd18 Back to old latency compensation 2022-07-21 09:53:07 +08:00
9ba52b332c Revert some vehicle sync change 2022-07-21 09:43:09 +08:00
ecfc77e57a Fix resource loading, add player died notification 2022-07-21 08:41:05 +08:00
2ba3e33d39 Don't follow position if velocity is greater than 3 2022-07-20 18:02:17 +08:00
71ecdbd5b9 code cleanup 2022-07-20 17:50:01 +08:00
bbe0b21aa4 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-07-20 09:32:34 +08:00
51dd445a31 Fix resource duplication 2022-07-20 09:32:26 +08:00
264158957c Send response in same sequence channel 2022-07-20 09:11:07 +08:00
9d8963222c Update nightly-build.yaml 2022-07-20 07:50:03 +08:00
c1a35dbab3 Update nightly-build.yaml 2022-07-20 07:44:52 +08:00
d3931cc57c Update nightly-build.yaml 2022-07-20 07:37:26 +08:00
3795ced530 Update nightly-build.yaml 2022-07-20 07:35:02 +08:00
1372a5c33f Update release configuration 2022-07-20 07:34:53 +08:00
9527da9785 Add ability to share server file 2022-07-19 17:15:53 +08:00
3072db0e21 Only load top-level assembly 2022-07-18 17:27:06 +08:00
444e6d7ffa Unify resource file path 2022-07-18 17:24:31 +08:00
809c935d37 Fix entity fire 2022-07-17 20:12:25 +08:00
10bd54a785 remove testing code 2022-07-17 19:19:58 +08:00
ece899f2a1 Better vehicle movement sync 2022-07-17 19:19:23 +08:00
5d1a182e47 Better ragdoll sync (maybe) 2022-07-17 18:44:16 +08:00
228ab9e727 Ped movement change 2022-07-17 14:23:19 +08:00
2605aafc4f Vehicle flag fix 2022-07-17 12:22:11 +08:00
582bb2a7d7 Ahhh... 2022-07-17 12:00:26 +08:00
0a3dbb1f59 Merge vehicle sync packet, comment cleanup 2022-07-17 11:59:37 +08:00
5d1832fcef Merge ped packet
Back to old project family (for debugging)
Fix blip name
Delete newly spawned traffic if limit exceeded
2022-07-17 11:15:02 +08:00
30ed509c55 Prevent entity spawning from interfering Tick event 2022-07-15 13:45:43 +08:00
2ad91011c3 blah 2022-07-14 18:52:04 +08:00
e40d4695ea Add QueueAction overload 2022-07-14 18:50:15 +08:00
e4c03f8ccc Add in-game update feature 2022-07-14 17:59:41 +08:00
e10a0f5415 Add Logger property for resource script 2022-07-14 16:44:35 +08:00
2a29011015 Better projectile check 2022-07-14 15:28:03 +08:00
a37e8869ac Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-07-14 10:33:34 +08:00
038860f158 Update nametag drawing method 2022-07-14 10:33:24 +08:00
51e678d760 Update README.md 2022-07-13 20:13:29 +08:00
1e2a9992d8 Merge branch 'main' of https://github.com/RAGECOOP/RAGECOOP-V 2022-07-13 11:08:21 +08:00
5b7bb917ef add tls 2022-07-13 11:08:11 +08:00
72aff78718 Move logger 2022-07-13 11:08:04 +08:00
cdeef7279c coding style 2022-07-13 10:36:38 +08:00
9cb5c557c5 fix vehicle doors sync 2022-07-13 00:20:06 +08:00
146 changed files with 13432 additions and 7497 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [RAGECOOP] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://www.buymeacoffee.com/xEntenKoeniqx', 'https://patreon.com/Sardelka']

48
.github/workflows/build-test.yaml vendored Normal file
View File

@ -0,0 +1,48 @@
name: Build test
on:
push:
branches:
- '*' # matches every branch that doesn't contain a '/'
- '*/*' # matches every branch containing a single '/'
- '**' # matches every branch
- '!main' # excludes main
- '!dev-nightly' # excludes nightly
pull_request:
branches: [ "main", "dev-nightly" ]
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
dotnet-version: ['6.0.x']
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Restore dependencies
run: dotnet restore
- name: Restore nuget packages
run: nuget restore
- name: Build client and installer
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
- name: Build server win-x64
run: dotnet build RageCoop.Server/RageCoop.Server.csproj -o bin/Release/Server
- name: Upload server
uses: actions/upload-artifact@v3
with:
name: RageCoop.Server
path: bin/Release/Server
- name: Upload Client
uses: actions/upload-artifact@v3
with:
name: RageCoop.Client
path: bin/Release/Client
- uses: actions/checkout@v2

32
.github/workflows/docker-build.yaml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Build and Push Docker Image to GHCR
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set repo owner to lowercase
run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image to GHCR
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ghcr.io/${{ env.REPO_OWNER }}/ragecoop-v:latest
dockerfile: Dockerfile

View File

@ -1,15 +1,11 @@
name: Nightly-build name: Nightly-build
on: on: push
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs: jobs:
build: build:
runs-on: windows-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
dotnet-version: ['6.0.x'] dotnet-version: ['6.0.x']
@ -22,78 +18,28 @@ jobs:
dotnet-version: ${{ matrix.dotnet-version }} dotnet-version: ${{ matrix.dotnet-version }}
- name: Restore dependencies - name: Restore dependencies
run: dotnet restore run: dotnet restore
- name: Build client - name: Restore nuget packages
run: dotnet publish RageCoop.Client/RageCoop.Client.csproj --no-restore --configuration Release -o RageCoop.Client/bin/RageCoop -f net48 run: |
sudo apt update
sudo apt install -y nuget
nuget restore
- name: Build client and installer
run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
- name: Build server win-x64 - name: Build server win-x64
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o RageCoop.Server/bin/win-x64 -c Release run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r win-x64 -o bin/Release/Server/win-x64 -c Release
- name: Build server linux-x64 - name: Build server linux-x64
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o RageCoop.Server/bin/linux-x64 -c Release run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-x64 -o bin/Release/Server/linux-x64 -c Release
- name: Build server linux-arm
run: dotnet publish RageCoop.Server/RageCoop.Server.csproj --self-contained -p:PublishSingleFile=true -p:PublishTrimmed=false -r linux-arm -o RageCoop.Server/bin/linux-arm -c Release
- uses: vimtor/action-zip@v1 - uses: vimtor/action-zip@v1
with: with:
files: RageCoop.Client/bin files: bin/Release/Client
dest: RageCoop.Client.zip dest: RageCoop.Client.zip
- uses: vimtor/action-zip@v1 - uses: vimtor/action-zip@v1
with: with:
files: RageCoop.Server/bin/win-x64 files: bin/Release/Server/win-x64
dest: RageCoop.Server-win-x64.zip dest: RageCoop.Server-win-x64.zip
- uses: vimtor/action-zip@v1 - uses: vimtor/action-zip@v1
with: with:
files: RageCoop.Server/bin/linux-x64 files: bin/Release/Server/linux-x64
dest: RageCoop.Server-linux-x64.zip dest: RageCoop.Server-linux-x64.zip
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/linux-arm
dest: RageCoop.Server-linux-arm.zip
- uses: WebFreak001/deploy-nightly@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
with:
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
release_id: 70603992
asset_path: RageCoop.Client.zip
asset_name: RageCoop.Client.zip
asset_content_type: application/zip
max_releases: 7
- uses: WebFreak001/deploy-nightly@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
with:
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
release_id: 70603992
asset_path: RageCoop.Server-win-x64.zip
asset_name: RageCoop.Server-win-x64.zip
asset_content_type: application/zip
max_releases: 7
- uses: WebFreak001/deploy-nightly@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
with:
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
release_id: 70603992
asset_path: RageCoop.Server-linux-x64.zip
asset_name: RageCoop.Server-linux-x64.zip
asset_content_type: application/zip
max_releases: 7
- uses: WebFreak001/deploy-nightly@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
with:
upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
release_id: 70603992
asset_path: RageCoop.Server-linux-arm.zip
asset_name: RageCoop.Server-linux-arm.zip
asset_content_type: application/zip
max_releases: 7
- uses: actions/checkout@v2

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
**/obj **/obj
**/packages **/packages
**/.vs **/.vs
**/.vscode
**/.idea

28
Dockerfile Normal file
View File

@ -0,0 +1,28 @@
# Use the official image as a parent image
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
# Use the SDK image to build the app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /src
# Copy csproj and restore as distinct layers
COPY RageCoop.Server/*.csproj ./RageCoop.Server/
COPY libs/*.dll ./libs/
# Assuming RageCoop.Core is a dependency, if not, you can comment out the next line
COPY RageCoop.Core/*.csproj ./RageCoop.Core/
RUN dotnet restore RageCoop.Server/RageCoop.Server.csproj
# Copy everything else and build
COPY . .
WORKDIR /src/RageCoop.Server
RUN dotnet publish -c Release -o /app
# Build runtime image
FROM base AS final
WORKDIR /app
COPY --from=build-env /app .
ENTRYPOINT ["dotnet", "RageCoop.Server.dll"]

View File

@ -1,6 +1,8 @@
# 🌐 RAGECOOP # 🌐 RAGECOOP
[![Downloads][downloads-shield]][downloads-url]
[![Contributors][contributors-shield]][contributors-url] [![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url] [![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url] [![Stargazers][stars-shield]][stars-url]
@ -8,51 +10,56 @@
# 🧠 That's it # 🧠 That's it
RAGECOOP is a multiplayer mod to play story mode or some mods made for RAGECOOP or just drive around with your buddy.
_Old name: GTACOOP:R_ RAGECOOP brings multiplayer experience to the story mode, you can complete missions together with your friends, use mods without any restriction/getting banned, or just mess around with your fella!
# 📋 Requirements # 👁 Requirements
- Visual Studio 2022 - ScriptHookV
- .NET 6.0 - ScriptHookVDotNet 3.6.0 or later
- .NET Framework 4.8 - .NET Framework 4.8 Runtime or SDK
# 📚 Libraries # 📋 Building the project
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.4.0)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/releases/tag/v1.6) You'll need:
- Lidgren Network Custom (***PRIVATE***) - .NET 6.0 SDK
- .NET Framework 4.8 SDK
Recommended IDE:
- Visual Studio Code
- Visul Studio 2022
Then run `dotnet build` in the solution directory, built binaries are in the `bin` folder
# 📚 Third-party libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI)
- Lidgren Network Custom
- - No new features (only improvements) - - No new features (only improvements)
- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/13.0.1) - [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
- [ClearScript](https://github.com/microsoft/ClearScript) - [ClearScript](https://github.com/microsoft/ClearScript)
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) - [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
- [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins) - [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
# Features # 👋 Features
1. Synchronized bullets 1. Synchronized bullets
2. Synchronized vehicle/player/NPC 2. Synchronized vehicle/player/NPC
3. Synchronized projectiles 3. Synchronized projectiles
4. Simple ragdoll sync 4. Simple ragdoll sync
5. Smoother vehicle/ped movement. 5. Decent compatibility with other mods, set up a private modded server to have some fun!
6. Ownership based sync logic, carjacking is now working (sort of). 6. Weaponized vehicle sync(WIP).
7. Introduced SyncEvents. 7. Optimization for high-Ping condition, play with friends around the world!
8. Code refactoring and namespace cleanup 8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.com).
9. Synchronized vehicle doors, brake and throttle.
10. Weaponized vehicle sync(WIP).
11. Other improvements
# Known issues # Known issues
1. Weapon sounds are missing. See [Bugs](https://github.com/RAGECOOP/RAGECOOP-V/issues/33)
2. Cover sync is still buggy.
3. Framerate drop with high number of synchronized entities.
5. Scripting API is screwed.(will be rewritten in the future)
## Installation # 🔫 Installation
Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki) Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki)
# Downloads # 🧨 Downloads
Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest) Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
@ -61,11 +68,9 @@ You can also download nightly builds [here](https://github.com/RAGECOOP/RAGECOOP
Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing. Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
# Support us
<a href="https://patreon.com/Sardelka"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DSardelka%26type%3Dpatrons&style=for-the-badge" /></a>
# 🦆 Special thanks to # 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm) - [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
- - For testing, ideas, contributions and the first modification with the API - - For testing, ideas, contributions and the first modification with the API
- [crosire](https://github.com/crosire) - [crosire](https://github.com/crosire)
@ -74,8 +79,11 @@ Please note that this is incompatible with all previous versions of ragecoop, re
- - For the extensive work in LemonUI - - For the extensive work in LemonUI
# 📝 License # 📝 License
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE) This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
[downloads-shield]: https://img.shields.io/github/downloads/RAGECOOP/RAGECOOP-V/total?style=for-the-badge
[downloads-url]: https://github.com/RAGECOOP/RAGECOOP-V/releases
[contributors-shield]: https://img.shields.io/github/contributors/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge [contributors-shield]: https://img.shields.io/github/contributors/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[contributors-url]: https://github.com/RAGECOOP/RAGECOOP-V/graphs/contributors [contributors-url]: https://github.com/RAGECOOP/RAGECOOP-V/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge [forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
@ -85,3 +93,4 @@ This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOO
[issues-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge [issues-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues [issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues

View File

@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "RageCoop.C
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -41,6 +43,14 @@ Global
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.Build.0 = Release|Any CPU {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.Build.0 = Release|Any CPU
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.ActiveCfg = Release|Any CPU {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.ActiveCfg = Release|Any CPU
{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.Build.0 = Release|Any CPU {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.Build.0 = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.ActiveCfg = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.Build.0 = Debug|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.Build.0 = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.ActiveCfg = Release|Any CPU
{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,9 @@
<Application x:Class="RageCoop.Client.Installer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RageCoop.Client.Installer"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,11 @@
using System.Windows;
namespace RageCoop.Client.Installer
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,15 @@
<Window x:Class="RageCoop.Client.Installer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RageCoop.Client.Installer"
mc:Ignorable="d"
Title="MainWindow" Height="376" Width="617" Background="#FFBABABA">
<Grid>
<Grid.Background>
<ImageBrush ImageSource="/bg.png" Opacity="0.2"/>
</Grid.Background>
<Label x:Name="Status" FontSize="20" Foreground="#FF232323" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>

View File

@ -0,0 +1,213 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using MessageBox = System.Windows.MessageBox;
using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
using Path = System.IO.Path;
using RageCoop.Core;
namespace RageCoop.Client.Installer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Choose();
}
private void Choose()
{
var od = new OpenFileDialog()
{
Filter = "GTA 5 executable |GTA5.exe;PlayGTAV.exe",
Title = "Select you GTAV executable"
};
if (od.ShowDialog() ?? false == true)
{
Task.Run(() =>
{
try
{
Install(Directory.GetParent(od.FileName).FullName);
}
catch (Exception ex)
{
MessageBox.Show("Installation failed: " + ex.ToString());
Environment.Exit(1);
}
});
}
else
{
Environment.Exit(0);
}
}
private void Install(string root)
{
UpdateStatus("Checking requirements");
var shvPath = Path.Combine(root, "ScriptHookV.dll");
var shvdnPath = Path.Combine(root, "ScriptHookVDotNet3.dll");
var scriptsPath = Path.Combine(root, "Scripts");
var lemonPath = Path.Combine(scriptsPath, "LemonUI.SHVDN3.dll");
var installPath = Path.Combine(scriptsPath, "RageCoop");
if (Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName == installPath)
{
throw new InvalidOperationException("The installer is not meant to be run in the game folder, please extract the zip to somewhere else and run again.");
}
Directory.CreateDirectory(installPath);
if (!File.Exists(shvPath))
{
MessageBox.Show("Please install ScriptHookV first!");
Environment.Exit(1);
}
if (!File.Exists(shvdnPath))
{
MessageBox.Show("Please install ScriptHookVDotNet first!");
Environment.Exit(1);
}
var shvdnVer = GetVer(shvdnPath);
if (shvdnVer < new Version(3, 6, 0))
{
MessageBox.Show("Please update ScriptHookVDotNet to latest version!" +
$"\nCurrent version is {shvdnVer}, 3.6.0 or higher is required");
Environment.Exit(1);
}
if (File.Exists(lemonPath))
{
var lemonVer = GetVer(lemonPath);
if (lemonVer < new Version(1, 7))
{
UpdateStatus("Updating LemonUI");
File.WriteAllBytes(lemonPath, getLemon());
}
}
UpdateStatus("Removing old versions");
foreach (var f in Directory.GetFiles(scriptsPath, "RageCoop.*", SearchOption.AllDirectories))
{
if (f.EndsWith("RageCoop.Client.Settings.xml")) { continue; }
File.Delete(f);
}
foreach (var f in Directory.GetFiles(installPath, "*.dll", SearchOption.AllDirectories))
{
File.Delete(f);
}
if (File.Exists("RageCoop.Core.dll") && File.Exists("RageCoop.Client.dll"))
{
UpdateStatus("Installing...");
CoreUtils.CopyFilesRecursively(new DirectoryInfo(Directory.GetCurrentDirectory()), new DirectoryInfo(installPath));
Finish();
}
else
{
throw new Exception("Required files are missing, please re-download the zip from official website");
}
void Finish()
{
checkKeys:
UpdateStatus("Checking conflicts");
var menyooConfig = Path.Combine(root, @"menyooStuff\menyooConfig.ini");
var settingsPath = Path.Combine(installPath, @"Data\RageCoop.Client.Settings.xml");
Settings settings = null;
try
{
settings = Util.ReadSettings(settingsPath);
}
catch
{
settings = new Settings();
}
if (File.Exists(menyooConfig))
{
var lines = File.ReadAllLines(menyooConfig).Where(x => !x.StartsWith(";") && x.EndsWith(" = " + (int)settings.MenuKey));
if (lines.Any())
{
if (MessageBox.Show("Following menyoo config value will conflict with RAGECOOP menu key\n" +
string.Join("\n", lines)
+ "\nDo you wish to change the Menu Key?", "Warning!", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
var ae = new AutoResetEvent(false);
UpdateStatus("Press the key you wish to change to");
Dispatcher.BeginInvoke(new Action(() =>
KeyDown += (s, e) =>
{
settings.MenuKey = (Keys)KeyInterop.VirtualKeyFromKey(e.Key);
ae.Set();
}));
ae.WaitOne();
if (!Util.SaveSettings(settingsPath, settings))
{
MessageBox.Show("Error occurred when saving settings");
Environment.Exit(1);
}
MessageBox.Show("Menu key changed to " + settings.MenuKey);
goto checkKeys;
}
}
}
UpdateStatus("Checking ZeroTier");
try
{
ZeroTierHelper.Check();
}
catch
{
if (MessageBox.Show("You can't join ZeroTier server unless ZeroTier is installed, do you want to download and install it?", "Install ZeroTier", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
var url = "https://download.zerotier.com/dist/ZeroTier%20One.msi";
UpdateStatus("Downloading ZeroTier from " + url);
try
{
HttpHelper.DownloadFile(url, "ZeroTier.msi", (p) => UpdateStatus("Downloading ZeroTier " + p + "%"));
UpdateStatus("Installing ZeroTier");
Process.Start("ZeroTier.msi").WaitForExit();
}
catch
{
MessageBox.Show("Failed to download ZeroTier, please download it from officail website");
Process.Start(url);
}
}
}
UpdateStatus("Completed!");
MessageBox.Show("Installation sucessful!");
Environment.Exit(0);
}
}
private void UpdateStatus(string status)
{
Dispatcher.BeginInvoke(new Action(() => Status.Content = status));
}
private Version GetVer(string location)
{
return Version.Parse(FileVersionInfo.GetVersionInfo(location).FileVersion);
}
private byte[] getLemon()
{
return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3");
}
}
}

View File

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Remove="bg.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Client\RageCoop.Client.csproj" />
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="bg.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,72 @@
<Project>
<PropertyGroup>
<AssemblyName>RageCoop.Client.Installer</AssemblyName>
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\</MSBuildProjectExtensionsPath>
<_TargetAssemblyProjectName>RageCoop.Client.Installer</_TargetAssemblyProjectName>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Remove="bg.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RageCoop.Client\RageCoop.Client.csproj" />
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<Compile Update="Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\sharpziplib\1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll" />
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\microsoft.extensions.objectpool\6.0.8\lib\net461\Microsoft.Extensions.ObjectPool.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationCore.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\PresentationFramework.dll" />
<ReferencePath Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\Client\RageCoop.Client.dll" />
<ReferencePath Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\bin\Debug\Core\RageCoop.Core.dll" />
<ReferencePath Include="C:\Users\Sardelka\.nuget\packages\system.buffers\4.5.1\ref\net45\System.Buffers.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Core.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Numerics.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Runtime.Serialization.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Controls.Ribbon.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Windows.Forms.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xaml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.Linq.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClient.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationClientsideProviders.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationProvider.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\UIAutomationTypes.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsBase.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\WindowsFormsIntegration.dll" />
<ReferencePath Include="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\Facades\netstandard.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\Debug\net48\MainWindow.g.cs" />
<Compile Include="M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\Debug\net48\App.g.cs" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View File

@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RageCoop.Client.Installer {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RageCoop.Client.Installer.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] LemonUI_SHVDN3 {
get {
object obj = ResourceManager.GetObject("LemonUI_SHVDN3", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="LemonUI_SHVDN3" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\LemonUI.SHVDN3.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -21,7 +18,7 @@ namespace RageCoop.Client
CheckProjectiles, CheckProjectiles,
GetAllEntities, GetAllEntities,
Receive, Receive,
ProjectilesTotal,
} }
internal static class Debug internal static class Debug
{ {

View File

@ -1,13 +1,9 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Math; using GTA.Math;
using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms;
using System.Threading; using System.Threading;
using System.Windows.Forms;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -27,7 +23,8 @@ namespace RageCoop.Client
private void OnKeyDown(object sender, KeyEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e)
{ {
if (ToMark == null || (!ToMark.Exists())) { return; } if (ToMark == null || (!ToMark.Exists())) { return; }
if (DevToolMenu.Menu.SelectedItem==DevToolMenu.boneIndexItem) { if (DevToolMenu.Menu.SelectedItem == DevToolMenu.boneIndexItem)
{
switch (e.KeyCode) switch (e.KeyCode)
{ {
@ -130,8 +127,7 @@ namespace RageCoop.Client
s = $@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
i=BulletsShot%2==0 ? {Current} : {Secondary}; return BulletsShot%2==0 ? {Current} : {Secondary};
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].{dir}Vector);
"; ";
} }
else else
@ -139,8 +135,7 @@ namespace RageCoop.Client
s = $@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
i=BulletsShot%2==0 ? {Current} : {Secondary}; return BulletsShot%2==0 ? {Current} : {Secondary};
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].{((MuzzleDir)(dir-3)).ToString()}Vector*-1);
"; ";
} }
} }
@ -151,7 +146,7 @@ namespace RageCoop.Client
s = $@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return new MuzzleInfo(v.Bones[{Current}].Position, v.Bones[{Current}].{dir}Vector); return {Current};
"; ";
} }
else else
@ -159,12 +154,12 @@ namespace RageCoop.Client
s = $@" s = $@"
// {ToMark.DisplayName} // {ToMark.DisplayName}
case {ToMark.Model.Hash}: case {ToMark.Model.Hash}:
return new MuzzleInfo(v.Bones[{Current}].Position, v.Bones[{Current}].{((MuzzleDir)(dir-3)).ToString()}Vector*-1); return {Current};
"; ";
} }
} }
Thread thread = new Thread(() => Clipboard.SetText(s)); Thread thread = new Thread(() => Clipboard.SetText(s));
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA thread.SetApartmentState(ApartmentState.STA);
thread.Start(); thread.Start();
thread.Join(); thread.Join();
GTA.UI.Notification.Show("Copied to clipboard, please paste it on the GitHub issue page!"); GTA.UI.Notification.Show("Copied to clipboard, please paste it on the GitHub issue page!");

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura ExcludeAssemblies="RageCoop.Core"/>
</Weavers>

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -1,16 +1,16 @@
using System; using GTA;
using System.IO; using GTA.Math;
using System.Linq; using GTA.Native;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Diagnostics;
using RageCoop.Client.Menus; using RageCoop.Client.Menus;
using RageCoop.Core; using RageCoop.Core;
using GTA; using System;
using GTA.Native; using System.Collections.Generic;
using GTA.Math; using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -19,9 +19,8 @@ namespace RageCoop.Client
/// </summary> /// </summary>
internal class Main : Script internal class Main : Script
{ {
private bool _gameLoaded = false; private bool _gameLoaded = false;
internal static readonly string CurrentVersion = "V0_5_0"; internal static Version Version = typeof(Main).Assembly.GetName().Version;
internal static int LocalPlayerID = 0; internal static int LocalPlayerID = 0;
@ -37,14 +36,27 @@ namespace RageCoop.Client
internal static Logger Logger = null; internal static Logger Logger = null;
internal static ulong Ticked = 0; internal static ulong Ticked = 0;
internal static Vector3 PlayerPosition;
internal static Scripting.Resources Resources = null; internal static Scripting.Resources Resources = null;
private static List<Func<bool>> QueuedActions = new List<Func<bool>>(); private static readonly List<Func<bool>> QueuedActions = new List<Func<bool>>();
public static Worker Worker;
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
/// </summary> /// </summary>
public Main() public Main()
{
Worker = new Worker("RageCoop.Client.Main.Worker", Logger);
try
{ {
Settings = Util.ReadSettings(); Settings = Util.ReadSettings();
}
catch
{
GTA.UI.Notification.Show("Malformed configuration, overwriting with default values...");
Settings = new Settings();
Util.SaveSettings();
}
Directory.CreateDirectory(Settings.DataDirectory); Directory.CreateDirectory(Settings.DataDirectory);
Logger = new Logger() Logger = new Logger()
{ {
@ -84,16 +96,20 @@ namespace RageCoop.Client
KeyDown += OnKeyDown; KeyDown += OnKeyDown;
KeyDown += (s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); }; KeyDown += (s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); };
KeyUp += (s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); }; KeyUp += (s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); };
Aborted += (object sender, EventArgs e) => CleanUp(); Aborted += (object sender, EventArgs e) => Disconnected("Abort");
Util.NativeMemory(); Util.NativeMemory();
Counter.Restart(); Counter.Restart();
} }
#if DEBUG public static Ped P;
#endif public static float FPS;
private bool _lastDead;
private void OnTick(object sender, EventArgs e) private void OnTick(object sender, EventArgs e)
{ {
P = Game.Player.Character;
PlayerPosition = P.ReadPosition();
FPS = Game.FPS;
if (Game.IsLoading) if (Game.IsLoading)
{ {
return; return;
@ -109,6 +125,7 @@ namespace RageCoop.Client
CoopMenu.MenuPool.Process(); CoopMenu.MenuPool.Process();
#endif #endif
DoQueuedActions(); DoQueuedActions();
if (!Networking.IsOnServer) if (!Networking.IsOnServer)
{ {
@ -124,22 +141,17 @@ namespace RageCoop.Client
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Main.Logger.Error(ex); Main.Logger.Error(ex);
#endif
} }
#if DEBUG
if (Networking.ShowNetworkInfo) if (Networking.ShowNetworkInfo)
{ {
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {Networking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {Networking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesDownPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesDownPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesUpPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {Lidgren.Network.NetUtility.ToHumanReadable(Statistics.BytesUpPerSecond)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
} }
#endif
MainChat.Tick(); MainChat.Tick();
PlayerList.Tick(); PlayerList.Tick();
@ -149,8 +161,6 @@ namespace RageCoop.Client
Function.Call(Hash.IGNORE_NEXT_RESTART, true); Function.Call(Hash.IGNORE_NEXT_RESTART, true);
Function.Call(Hash.FORCE_GAME_STATE_PLAYING); Function.Call(Hash.FORCE_GAME_STATE_PLAYING);
Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller"); Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
var P = Game.Player.Character;
if (P.IsDead) if (P.IsDead)
{ {
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false); Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
@ -160,20 +170,23 @@ namespace RageCoop.Client
P.Health = 1; P.Health = 1;
Game.Player.WantedLevel = 0; Game.Player.WantedLevel = 0;
Main.Logger.Debug("Player died."); Main.Logger.Debug("Player died.");
Scripting.API.Events.InvokePlayerDied(KillMessage());
} }
GTA.UI.Screen.StopEffects(); GTA.UI.Screen.StopEffects();
} }
else else
{ {
Function.Call(Hash.DISPLAY_HUD, true); Function.Call(Hash.DISPLAY_HUD, true);
} }
}
else if (P.IsDead && !_lastDead)
{
Scripting.API.Events.InvokePlayerDied(KillMessage());
} }
_lastDead = P.IsDead;
Ticked++; Ticked++;
} }
private void OnKeyDown(object sender, KeyEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e)
{ {
if (MainChat.Focused) if (MainChat.Focused)
@ -183,6 +196,20 @@ namespace RageCoop.Client
} }
if (Networking.IsOnServer) if (Networking.IsOnServer)
{ {
if (Voice.WasInitialized())
{
if (Game.IsControlPressed(GTA.Control.PushToTalk))
{
Voice.StartRecording();
return;
}
else if (Voice.IsRecording())
{
Voice.StopRecording();
return;
}
}
if (Game.IsControlPressed(GTA.Control.FrontendPause)) if (Game.IsControlPressed(GTA.Control.FrontendPause))
{ {
Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0); Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
@ -212,6 +239,14 @@ namespace RageCoop.Client
CoopMenu.LastMenu.Visible = true; CoopMenu.LastMenu.Visible = true;
} }
} }
else if (Game.IsControlJustPressed(GTA.Control.MpTextChatAll))
{
if (Networking.IsOnServer)
{
MainChat.Focused = true;
}
}
else if (MainChat.Focused) { return; }
else if (Game.IsControlJustPressed(GTA.Control.MultiplayerInfo)) else if (Game.IsControlJustPressed(GTA.Control.MultiplayerInfo))
{ {
if (Networking.IsOnServer) if (Networking.IsOnServer)
@ -220,13 +255,6 @@ namespace RageCoop.Client
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp; PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp;
} }
} }
else if (Game.IsControlJustPressed(GTA.Control.MpTextChatAll))
{
if (Networking.IsOnServer)
{
MainChat.Focused = true;
}
}
else if (e.KeyCode == Settings.PassengerKey) else if (e.KeyCode == Settings.PassengerKey)
{ {
var P = Game.Player.Character; var P = Game.Player.Character;
@ -239,94 +267,74 @@ namespace RageCoop.Client
} }
else else
{ {
var V = World.GetClosestVehicle(P.Position, 50); var V = World.GetClosestVehicle(P.ReadPosition(), 50);
if (V != null) if (V != null)
{ {
var seat = P.GetNearestSeat(V); var seat = P.GetNearestSeat(V);
P.Task.EnterVehicle(V, seat); var p = V.GetPedOnSeat(seat);
} if (p != null && !p.IsDead)
}
}
}
}
public static void CleanUp()
{ {
for (int i = -1; i < V.PassengerCapacity; i++)
{
seat = (VehicleSeat)i;
p = V.GetPedOnSeat(seat);
if (p == null || p.IsDead)
{
break;
}
}
}
P.Task.EnterVehicle(V, seat, -1, 5, EnterVehicleFlags.None);
}
}
}
}
}
internal static void Connected()
{
Memory.ApplyPatches();
if (Settings.Voice && !Voice.WasInitialized())
{
Voice.Init();
}
QueueAction(() =>
{
WorldThread.Traffic(!Settings.DisableTraffic);
Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, true);
CoopMenu.ConnectedMenuSetting();
MainChat.Init();
GTA.UI.Notification.Show("~g~Connected!");
});
Logger.Info(">> Connected <<");
}
public static void Disconnected(string reason)
{
Logger.Info($">> Disconnected << reason: {reason}");
QueueAction(() =>
{
if (MainChat.Focused)
{
MainChat.Focused = false;
}
PlayerList.Cleanup();
MainChat.Clear(); MainChat.Clear();
EntityPool.Cleanup(); EntityPool.Cleanup();
PlayerList.Cleanup(); WorldThread.Traffic(true);
Main.LocalPlayerID=default; Function.Call(Hash.SET_ENABLE_VEHICLE_SLIPSTREAMING, false);
CoopMenu.DisconnectedMenuSetting();
if (reason != "Abort")
GTA.UI.Notification.Show("~r~Disconnected: " + reason);
LocalPlayerID = default;
});
Memory.RestorePatches();
DownloadManager.Cleanup();
Voice.ClearAll();
Resources.Unload();
} }
internal static readonly Dictionary<ulong, byte> CheckNativeHash = new Dictionary<ulong, byte>()
{
{ 0xD49F9B0955C367DE, 1 }, // Entities
{ 0xEF29A16337FACADB, 1 }, //
{ 0xB4AC7D0CF06BFE8F, 1 }, //
{ 0x9B62392B474F44A0, 1 }, //
{ 0x7DD959874C1FD534, 1 }, //
{ 0xAF35D0D2583051B0, 2 }, // Vehicles
{ 0x63C6CCA8E68AE8C8, 2 }, //
{ 0x509D5878EB39E842, 3 }, // Props
{ 0x9A294B2138ABB884, 3 }, //
{ 0x46818D79B1F7499A, 4 }, // Blips
{ 0x5CDE92C702A8FCE7, 4 }, //
{ 0xBE339365C863BD36, 4 }, //
{ 0x5A039BB0BCA604B6, 4 }, //
{ 0x0134F0835AB6BFCB, 5 } // Checkpoints
};
internal static Dictionary<int, byte> ServerItems = new Dictionary<int, byte>();
internal static void CleanUpWorld()
{
if (ServerItems.Count == 0)
{
return;
}
lock (ServerItems)
{
foreach (KeyValuePair<int, byte> item in ServerItems)
{
try
{
switch (item.Value)
{
case 1:
World.GetAllEntities().FirstOrDefault(x => x.Handle == item.Key)?.Delete();
break;
case 2:
World.GetAllVehicles().FirstOrDefault(x => x.Handle == item.Key)?.Delete();
break;
case 3:
World.GetAllProps().FirstOrDefault(x => x.Handle == item.Key)?.Delete();
break;
case 4:
Blip blip = new Blip(item.Key);
if (blip.Exists())
{
blip.Delete();
}
break;
case 5:
Checkpoint checkpoint = new Checkpoint(item.Key);
if (checkpoint.Exists())
{
checkpoint.Delete();
}
break;
}
}
catch
{
GTA.UI.Notification.Show("~r~~h~CleanUpWorld() Error");
Main.Logger.Error($"CleanUpWorld(): ~r~Item {item.Value} cannot be deleted!");
}
}
ServerItems.Clear();
}
}
private static void DoQueuedActions() private static void DoQueuedActions()
{ {
lock (QueuedActions) lock (QueuedActions)
@ -342,7 +350,9 @@ namespace RageCoop.Client
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Logger.Error(ex); Logger.Error(ex);
#endif
QueuedActions.Remove(action); QueuedActions.Remove(action);
} }
} }
@ -352,7 +362,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Queue an action to be executed on next tick, allowing you to call scripting API from another thread. /// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
/// </summary> /// </summary>
/// <param name="a"> The action to be executed, must return a bool indicating whether the action cane be removed after execution.</param> /// <param name="a"> An action to be executed with a return value indicating whether the action can be removed after execution.</param>
internal static void QueueAction(Func<bool> a) internal static void QueueAction(Func<bool> a)
{ {
lock (QueuedActions) lock (QueuedActions)
@ -375,6 +385,24 @@ namespace RageCoop.Client
lock (QueuedActions) { QueuedActions.Clear(); } lock (QueuedActions) { QueuedActions.Clear(); }
} }
public static void Delay(Action a, int time)
{
Task.Run(() =>
{
Thread.Sleep(time);
QueueAction(a);
});
}
private string KillMessage()
{
if (P.Killer != null)
{
var killer = EntityPool.GetPedByHandle(P.Killer.Handle);
if (killer != null && killer.ID == killer.Owner.ID)
return $"~h~{PlayerList.GetPlayer(LocalPlayerID).Username}~h~ was killed by ~h~{killer.Owner.Username}~h~ ({P.CauseOfDeath})";
}
return $"~h~{PlayerList.GetPlayer(LocalPlayerID).Username}~h~ died";
}
} }
} }

View File

@ -1,10 +1,9 @@
using GTA; using GTA;
using GTA.Native;
using System.Drawing;
using LemonUI; using LemonUI;
using LemonUI.Menus; using LemonUI.Menus;
using LemonUI.Scaleform; using LemonUI.Scaleform;
using System.Drawing;
namespace RageCoop.Client.Menus namespace RageCoop.Client.Menus
{ {
@ -38,7 +37,8 @@ namespace RageCoop.Client.Menus
private static readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" + private static readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" +
"https://github.com/RAGECOOP~n~" + "https://github.com/RAGECOOP~n~" +
"~y~VERSION~s~~n~" + "~y~VERSION~s~~n~" +
Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") }; Main.Version)
{ LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") };
#endregion #endregion
@ -50,7 +50,7 @@ namespace RageCoop.Client.Menus
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
_usernameItem.Activated += UsernameActivated; _usernameItem.Activated += UsernameActivated;
_passwordItem.Activated += _passwordActivated; _passwordItem.Activated += _passwordActivated;
@ -67,14 +67,17 @@ namespace RageCoop.Client.Menus
Menu.AddSubMenu(SettingsMenu.Menu); Menu.AddSubMenu(SettingsMenu.Menu);
Menu.AddSubMenu(DevToolMenu.Menu); Menu.AddSubMenu(DevToolMenu.Menu);
#if DEBUG
Menu.AddSubMenu(DebugMenu.Menu); Menu.AddSubMenu(DebugMenu.Menu);
#endif
MenuPool.Add(Menu); MenuPool.Add(Menu);
MenuPool.Add(SettingsMenu.Menu); MenuPool.Add(SettingsMenu.Menu);
MenuPool.Add(DevToolMenu.Menu); MenuPool.Add(DevToolMenu.Menu);
#if DEBUG
MenuPool.Add(DebugMenu.Menu); MenuPool.Add(DebugMenu.Menu);
MenuPool.Add(DebugMenu.DiagnosticMenu); MenuPool.Add(DebugMenu.DiagnosticMenu);
#endif
MenuPool.Add(ServersMenu.Menu); MenuPool.Add(ServersMenu.Menu);
MenuPool.Add(PopUp); MenuPool.Add(PopUp);
@ -90,10 +93,21 @@ namespace RageCoop.Client.Menus
PopUp.Error = error; PopUp.Error = error;
PopUp.ShowBackground = showbackground; PopUp.ShowBackground = showbackground;
PopUp.Visible = true; PopUp.Visible = true;
Script.Yield();
while (true) while (true)
{ {
Game.DisableAllControlsThisFrame(); Game.DisableAllControlsThisFrame();
MenuPool.Process(); MenuPool.Process();
var scaleform = new Scaleform("instructional_buttons");
scaleform.CallFunction("CLEAR_ALL");
scaleform.CallFunction("TOGGLE_MOUSE_BUTTONS", 0);
scaleform.CallFunction("CREATE_CONTAINER");
scaleform.CallFunction("SET_DATA_SLOT", 0, Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendAccept, 0), "Continue");
scaleform.CallFunction("SET_DATA_SLOT", 1, Function.Call<string>((Hash)0x0499D7B09FC9B407, 2, (int)Control.FrontendCancel, 0), "Cancel");
scaleform.CallFunction("DRAW_INSTRUCTIONAL_BUTTONS", -1);
scaleform.Render2D();
if (Game.IsControlJustPressed(Control.FrontendAccept)) if (Game.IsControlJustPressed(Control.FrontendAccept))
{ {
PopUp.Visible = false; PopUp.Visible = false;
@ -105,6 +119,8 @@ namespace RageCoop.Client.Menus
return false; return false;
} }
Script.Yield(); Script.Yield();
Game.DisableAllControlsThisFrame();
} }
} }
public static void UsernameActivated(object a, System.EventArgs b) public static void UsernameActivated(object a, System.EventArgs b)
@ -122,13 +138,10 @@ namespace RageCoop.Client.Menus
private static void _passwordActivated(object sender, System.EventArgs e) private static void _passwordActivated(object sender, System.EventArgs e)
{ {
string newPass = Game.GetUserInput(WindowTitle.EnterMessage20, "", 20); string newPass = Game.GetUserInput(WindowTitle.EnterMessage20, "", 20);
if (!string.IsNullOrWhiteSpace(newPass))
{
Main.Settings.Password = newPass; Main.Settings.Password = newPass;
Util.SaveSettings(); Util.SaveSettings();
_passwordItem.AltTitle = new string('*', newPass.Length); _passwordItem.AltTitle = new string('*', newPass.Length);
} }
}
public static void ServerIpActivated(object a, System.EventArgs b) public static void ServerIpActivated(object a, System.EventArgs b)
{ {
string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60); string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);

View File

@ -1,18 +1,15 @@
using System; #if DEBUG
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LemonUI;
using LemonUI.Menus;
using GTA; using GTA;
using LemonUI.Menus;
using System;
using System.Drawing; using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static class DebugMenu internal static class DebugMenu
{ {
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Debug", "Debug settings") { public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Debug", "Debug settings")
{
UseMouse = false, UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
@ -21,23 +18,16 @@ namespace RageCoop.Client
UseMouse = false, UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
private static NativeItem d1=new NativeItem("PositionPrediction"); public static NativeItem SimulatedLatencyItem = new NativeItem("Simulated network latency", "Simulated network latency in ms (one way)", "0");
public static NativeCheckboxItem ShowOwnerItem = new NativeCheckboxItem("Show entity owner", "Show the owner name of the entity you're aiming at", false);
private static readonly NativeCheckboxItem ShowNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
static DebugMenu() static DebugMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
d1.Activated+=(sender,e) =>
{
try{ SyncParameters.PositioinPredictionDefault =float.Parse(Game.GetUserInput(WindowTitle.EnterMessage20, SyncParameters.PositioinPredictionDefault.ToString(), 20));}
catch { }
Update();
};
Menu.Add(d1);
Menu.AddSubMenu(DiagnosticMenu);
Menu.Opening+=(sender, e) =>Update();
DiagnosticMenu.Opening += (sender, e) => DiagnosticMenu.Opening += (sender, e) =>
{ {
DiagnosticMenu.Clear(); DiagnosticMenu.Clear();
@ -47,15 +37,24 @@ namespace RageCoop.Client
DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString())); DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
} }
}; };
SimulatedLatencyItem.Activated += (s, e) =>
Update();
}
private static void Update()
{ {
d1.AltTitle = SyncParameters.PositioinPredictionDefault.ToString(); try
} {
SimulatedLatencyItem.AltTitle = ((Networking.SimulatedLatency = int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle)) * 0.002f) * 500).ToString();
}
catch (Exception ex) { Main.Logger.Error(ex); }
};
ShowNetworkInfoItem.CheckboxChanged += (s, e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; };
ShowOwnerItem.CheckboxChanged += (s, e) => { Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; Util.SaveSettings(); };
Menu.Add(SimulatedLatencyItem);
Menu.Add(ShowNetworkInfoItem);
Menu.Add(ShowOwnerItem);
Menu.AddSubMenu(DiagnosticMenu);
}
} }
} }
#endif

View File

@ -1,10 +1,6 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LemonUI.Menus; using LemonUI.Menus;
using GTA; using System;
using System.Drawing; using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
@ -16,9 +12,9 @@ namespace RageCoop.Client
UseMouse = false, UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
private static NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable"); private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable");
private static NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary","Enable if this vehicle have two muzzles"); private static readonly NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles");
public static NativeItem boneIndexItem = new NativeItem("Current bone index"); public static NativeItem boneIndexItem = new NativeItem("Current bone index");
public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index"); public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index");
public static NativeItem clipboardItem = new NativeItem("Copy to clipboard"); public static NativeItem clipboardItem = new NativeItem("Copy to clipboard");
@ -26,7 +22,7 @@ namespace RageCoop.Client
static DevToolMenu() static DevToolMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
enableItem.Activated += enableItem_Activated; enableItem.Activated += enableItem_Activated;
enableItem.Checked = false; enableItem.Checked = false;

View File

@ -1,36 +1,15 @@
using System; using GTA.UI;
using System.Net;
using System.Drawing;
using System.Collections.Generic;
using Newtonsoft.Json;
using LemonUI.Menus; using LemonUI.Menus;
using Newtonsoft.Json;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using System.Threading; using System.Threading;
namespace RageCoop.Client.Menus namespace RageCoop.Client.Menus
{ {
internal class ServerListClass
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("port")]
public string Port { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
[JsonProperty("players")]
public int Players { get; set; }
[JsonProperty("maxPlayers")]
public int MaxPlayers { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
}
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
@ -51,7 +30,7 @@ namespace RageCoop.Client.Menus
static ServersMenu() static ServersMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) => Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
{ {
@ -76,9 +55,9 @@ namespace RageCoop.Client.Menus
private static void GetAllServers() private static void GetAllServers()
{ {
List<ServerListClass> serverList = null; List<ServerInfo> serverList = null;
var realUrl = Main.Settings.MasterServer; var realUrl = Main.Settings.MasterServer;
serverList = JsonConvert.DeserializeObject<List<ServerListClass>>(DownloadString(realUrl)); serverList = JsonConvert.DeserializeObject<List<ServerInfo>>(DownloadString(realUrl));
// Need to be processed in main thread // Need to be processed in main thread
Main.QueueAction(() => Main.QueueAction(() =>
@ -94,17 +73,25 @@ namespace RageCoop.Client.Menus
return; return;
} }
CleanUpList(); CleanUpList();
foreach (ServerListClass server in serverList) foreach (ServerInfo server in serverList)
{ {
string address = $"{server.Address}:{server.Port}"; string address = $"{server.address}:{server.port}";
NativeItem tmpItem = new NativeItem($"[{server.Country}] {server.Name}", $"~b~{address}~s~~n~~g~Version {server.Version}.x~s~") { AltTitle = $"[{server.Players}/{server.MaxPlayers}]" }; NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" };
tmpItem.Activated += (object sender, EventArgs e) => tmpItem.Activated += (object sender, EventArgs e) =>
{ {
try try
{ {
Menu.Visible = false; Menu.Visible = false;
if (server.useZT)
Networking.ToggleConnection(address); {
address = $"{server.ztAddress}:{server.port}";
Notification.Show($"~y~Joining ZeroTier network... {server.ztID}");
if (ZeroTierHelper.Join(server.ztID) == null)
{
throw new Exception("Failed to obtain ZeroTier network IP");
}
}
Networking.ToggleConnection(address, null, null, PublicKey.FromServerInfo(server));
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
CoopMenu.ServerIpItem.AltTitle = address; CoopMenu.ServerIpItem.AltTitle = address;
@ -115,7 +102,11 @@ namespace RageCoop.Client.Menus
} }
catch (Exception ex) catch (Exception ex)
{ {
GTA.UI.Notification.Show($"~r~{ex.Message}"); Notification.Show($"~r~{ex.Message}");
if (server.useZT)
{
Notification.Show($"Make sure ZeroTier is correctly installed, download it from https://www.zerotier.com/");
}
} }
}; };
Menu.Add(tmpItem); Menu.Add(tmpItem);

View File

@ -1,14 +1,11 @@
using System.Drawing; using GTA;
using System;
using System.Windows.Forms;
using GTA;
using LemonUI.Menus; using LemonUI.Menus;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RageCoop.Client.Menus namespace RageCoop.Client.Menus
{ {
/// <summary>
/// Don't use it!
/// </summary>
internal static class SettingsMenu internal static class SettingsMenu
{ {
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings") public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings")
@ -19,54 +16,95 @@ namespace RageCoop.Client.Menus
private static readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic); private static readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic);
private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu); private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableTraffic); private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableAlternatePause);
private static readonly NativeCheckboxItem _showBlip = new NativeCheckboxItem("Show player blip", "Show other player's blip on map", Main.Settings.ShowPlayerBlip);
private static readonly NativeCheckboxItem _showNametag = new NativeCheckboxItem("Show player nametag", "Show other player's nametag on your screen", Main.Settings.ShowPlayerNameTag);
private static readonly NativeCheckboxItem _disableVoice = new NativeCheckboxItem("Enable voice", "Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends", Main.Settings.Voice);
private static readonly NativeCheckboxItem _showNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo); private static readonly NativeItem _menuKey = new NativeItem("Menu Key", "The key to open menu", Main.Settings.MenuKey.ToString());
private static readonly NativeItem _passengerKey = new NativeItem("Passenger Key", "The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString());
private static readonly NativeItem _vehicleSoftLimit = new NativeItem("Vehicle limit (soft)", "The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).", Main.Settings.WorldVehicleSoftLimit.ToString());
private static readonly NativeItem _pedSoftLimit = new NativeItem("Ped limit (soft)", "The game won't spawn more NPCs if the limit is exceeded. \n-1 for unlimited (not recommended).", Main.Settings.WorldPedSoftLimit.ToString());
private static NativeItem _menuKey = new NativeItem("Menu Key","The key to open menu", Main.Settings.MenuKey.ToString());
private static NativeItem _passengerKey = new NativeItem("Passenger Key", "The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString());
private static NativeItem _vehicleSoftLimit = new NativeItem("Vehicle limit (soft)", "The game won't spawn more NPC traffic if the limit is exceeded. \n-1 for unlimited (not recommended).",Main.Settings.WorldVehicleSoftLimit.ToString());
/// <summary>
/// Don't use it!
/// </summary>
static SettingsMenu() static SettingsMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
_disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged; _disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged;
_disablePauseAlt.CheckboxChanged+=_disablePauseAlt_CheckboxChanged; _disablePauseAlt.CheckboxChanged += DisablePauseAltCheckboxChanged;
_disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged;
_flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged; _flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
_showNetworkInfoItem.CheckboxChanged += ShowNetworkInfoCheckboxChanged;
_menuKey.Activated += ChaneMenuKey; _menuKey.Activated += ChaneMenuKey;
_passengerKey.Activated += ChangePassengerKey; _passengerKey.Activated += ChangePassengerKey;
_vehicleSoftLimit.Activated+=vehicleSoftLimit_Activated; _vehicleSoftLimit.Activated += VehicleSoftLimitActivated;
_pedSoftLimit.Activated += PedSoftLimitActivated;
_showBlip.Activated += (s, e) =>
{
Main.Settings.ShowPlayerBlip = _showBlip.Checked;
Util.SaveSettings();
};
_showNametag.Activated += (s, e) =>
{
Main.Settings.ShowPlayerNameTag = _showNametag.Checked;
Util.SaveSettings();
};
Menu.Add(_disableTrafficItem); Menu.Add(_disableTrafficItem);
Menu.Add(_disablePauseAlt); Menu.Add(_disablePauseAlt);
Menu.Add(_flipMenuItem); Menu.Add(_flipMenuItem);
Menu.Add(_showNetworkInfoItem); Menu.Add(_disableVoice);
Menu.Add(_menuKey); Menu.Add(_menuKey);
Menu.Add(_passengerKey); Menu.Add(_passengerKey);
Menu.Add(_vehicleSoftLimit); Menu.Add(_vehicleSoftLimit);
Menu.Add(_pedSoftLimit);
Menu.Add(_showBlip);
Menu.Add(_showNametag);
} }
private static void DisableVoiceCheckboxChanged(object sender, EventArgs e)
{
if (_disableVoice.Checked)
{
if (Networking.IsOnServer && !Voice.WasInitialized())
{
Voice.Init();
}
}
else
{
Voice.ClearAll();
}
Main.Settings.Voice = _disableVoice.Checked;
Util.SaveSettings();
}
private static void _disablePauseAlt_CheckboxChanged(object sender, EventArgs e) private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
{ {
Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked; Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }
private static void vehicleSoftLimit_Activated(object sender, EventArgs e) private static void VehicleSoftLimitActivated(object sender, EventArgs e)
{ {
try try
{ {
Main.Settings.WorldVehicleSoftLimit = int.Parse( Main.Settings.WorldVehicleSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.WorldVehicleSoftLimit.ToString(), 20)); Main.Settings.WorldVehicleSoftLimit.ToString(), 20));
_menuKey.AltTitle=Main.Settings.WorldVehicleSoftLimit.ToString(); _vehicleSoftLimit.AltTitle = Main.Settings.WorldVehicleSoftLimit.ToString();
Util.SaveSettings();
}
catch { }
}
private static void PedSoftLimitActivated(object sender, EventArgs e)
{
try
{
Main.Settings.WorldPedSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.WorldPedSoftLimit.ToString(), 20));
_pedSoftLimit.AltTitle = Main.Settings.WorldPedSoftLimit.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -101,6 +139,7 @@ namespace RageCoop.Client.Menus
public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b) public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{ {
WorldThread.Traffic(!_disableTrafficItem.Checked);
Main.Settings.DisableTraffic = _disableTrafficItem.Checked; Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }
@ -113,11 +152,5 @@ namespace RageCoop.Client.Menus
Main.Settings.FlipMenu = _flipMenuItem.Checked; Main.Settings.FlipMenu = _flipMenuItem.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }
public static void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
{
Networking.ShowNetworkInfo = _showNetworkInfoItem.Checked;
}
} }
} }

View File

@ -1,10 +1,9 @@
using System; using GTA;
using GTA.Native;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using RageCoop.Core;
using GTA;
using GTA.Native;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -17,7 +16,7 @@ namespace RageCoop.Client
private bool CurrentFocused { get; set; } private bool CurrentFocused { get; set; }
public bool Focused public bool Focused
{ {
get { return CurrentFocused; } get => CurrentFocused;
set set
{ {
if (value && Hidden) if (value && Hidden)
@ -36,7 +35,7 @@ namespace RageCoop.Client
private bool CurrentHidden { get; set; } private bool CurrentHidden { get; set; }
private bool Hidden private bool Hidden
{ {
get { return CurrentHidden; } get => CurrentHidden;
set set
{ {
if (value) if (value)

View File

@ -1,22 +1,23 @@
using System.IO; using RageCoop.Core;
using System.Linq;
using System.Collections.Generic;
using RageCoop.Core;
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static class DownloadManager internal static class DownloadManager
{ {
public static event EventHandler<string> DownloadCompleted;
static DownloadManager() static DownloadManager()
{ {
Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) => Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) =>
{ {
var fr = new Packets.FileTransferRequest(); var fr = new Packets.FileTransferRequest();
fr.Unpack(data); fr.Deserialize(data);
if (fr.Name.EndsWith(".zip")) if (fr.Name.EndsWith(".res"))
{ {
_zips.Add(fr.Name); _resources.Add(fr.Name);
} }
return new Packets.FileTransferResponse() return new Packets.FileTransferResponse()
{ {
@ -27,7 +28,7 @@ namespace RageCoop.Client
Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) => Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) =>
{ {
Packets.FileTransferComplete packet = new Packets.FileTransferComplete(); Packets.FileTransferComplete packet = new Packets.FileTransferComplete();
packet.Unpack(data); packet.Deserialize(data);
Main.Logger.Debug($"Finalizing download:{packet.ID}"); Main.Logger.Debug($"Finalizing download:{packet.ID}");
Complete(packet.ID); Complete(packet.ID);
@ -43,44 +44,42 @@ namespace RageCoop.Client
{ {
try try
{ {
Main.Resources.Load(ResourceFolder,_zips.ToArray()); Main.Resources.Load(ResourceFolder, _resources.ToArray());
return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.Loaded }; return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.Loaded };
} }
catch (Exception ex) catch (Exception ex)
{ {
Main.Logger.Error("Error occurred when loading server resource:"); Main.Logger.Error("Error occurred when loading server resource:");
Main.Logger.Error(ex); Main.Logger.Error(ex);
return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.LoadFailed }; return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.LoadFailed };
} }
}); });
} }
public static string ResourceFolder { public static string ResourceFolder => Path.GetFullPath(Path.Combine(Main.Settings.DataDirectory, "Resources", Main.Settings.LastServerAddress.Replace(":", ".")));
get {
return Path.Combine(Main.Settings.DataDirectory,"Resources", Main.Settings.LastServerAddress.Replace(":", "."));
}
}
private static readonly Dictionary<int, DownloadFile> InProgressDownloads = new Dictionary<int, DownloadFile>(); private static readonly Dictionary<int, DownloadFile> InProgressDownloads = new Dictionary<int, DownloadFile>();
private static readonly List<string> _zips = new List<string>(); private static readonly HashSet<string> _resources = new HashSet<string>();
public static bool AddFile(int id, string name, long length) public static bool AddFile(int id, string name, long length)
{ {
Main.Logger.Debug($"Downloading file to {ResourceFolder}\\{name} , id:{id}"); var path = $"{ResourceFolder}\\{name}";
if (!Directory.Exists(ResourceFolder)) Main.Logger.Debug($"Downloading file to {path} , id:{id}");
if (!Directory.Exists(Directory.GetParent(path).FullName))
{ {
Directory.CreateDirectory(ResourceFolder); Directory.CreateDirectory(Directory.GetParent(path).FullName);
} }
if (FileAlreadyExists(ResourceFolder, name, length)) if (FileAlreadyExists(ResourceFolder, name, length))
{ {
Main.Logger.Debug($"File already exists! canceling download:{name}"); Main.Logger.Debug($"File already exists! canceling download:{name}");
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, name));
return false; return false;
} }
/*
if (!name.EndsWith(".zip")) if (!name.EndsWith(".zip"))
{ {
Main.Logger.Error($"File download blocked! [{name}]"); Main.Logger.Error($"File download blocked! [{name}]");
return false; return false;
} }
*/
lock (InProgressDownloads) lock (InProgressDownloads)
{ {
InProgressDownloads.Add(id, new DownloadFile() InProgressDownloads.Add(id, new DownloadFile()
@ -88,7 +87,7 @@ namespace RageCoop.Client
FileID = id, FileID = id,
FileName = name, FileName = name,
FileLength = length, FileLength = length,
Stream = new FileStream($"{ResourceFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite) Stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)
}); });
} }
return true; return true;
@ -123,8 +122,7 @@ namespace RageCoop.Client
{ {
lock (InProgressDownloads) lock (InProgressDownloads)
{ {
DownloadFile file; if (InProgressDownloads.TryGetValue(id, out DownloadFile file))
if (InProgressDownloads.TryGetValue(id, out file))
{ {
file.Stream.Write(chunk, 0, chunk.Length); file.Stream.Write(chunk, 0, chunk.Length);
@ -134,22 +132,17 @@ namespace RageCoop.Client
Main.Logger.Trace($"Received unhandled file chunk:{id}"); Main.Logger.Trace($"Received unhandled file chunk:{id}");
} }
} }
} }
public static void Complete(int id) public static void Complete(int id)
{ {
DownloadFile f;
if (InProgressDownloads.TryGetValue(id, out f)) if (InProgressDownloads.TryGetValue(id, out DownloadFile f))
{ {
InProgressDownloads.Remove(id); InProgressDownloads.Remove(id);
f.Dispose(); f.Dispose();
if (f.FileName.EndsWith(".zip"))
{
_zips.Add(f.FileName);
}
Main.Logger.Info($"Download finished:{f.FileName}"); Main.Logger.Info($"Download finished:{f.FileName}");
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, f.FileName));
} }
else else
{ {
@ -167,20 +160,12 @@ namespace RageCoop.Client
} }
InProgressDownloads.Clear(); InProgressDownloads.Clear();
} }
if (Directory.Exists(ResourceFolder)) _resources.Clear();
{
foreach (var zip in Directory.GetDirectories(ResourceFolder, "*.zip"))
{
File.Delete(zip);
}
}
_zips.Clear();
} }
} }
internal class DownloadFile: System.IDisposable internal class DownloadFile : IDisposable
{ {
public int FileID { get; set; } = 0; public int FileID { get; set; } = 0;
public string FileName { get; set; } = string.Empty; public string FileName { get; set; } = string.Empty;

View File

@ -0,0 +1,83 @@
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Timers;
namespace RageCoop.Client
{
internal static partial class HolePunch
{
static HolePunch()
{
// Periodically send hole punch message as needed
var timer = new Timer(1000);
timer.Elapsed += DoPunch;
timer.Enabled = true;
}
private static void DoPunch(object sender, ElapsedEventArgs e)
{
try
{
if (!Networking.IsOnServer) { return; }
foreach (var p in PlayerList.Players.Values.ToArray())
{
if (p.InternalEndPoint != null && p.ExternalEndPoint != null && (p.Connection == null || p.Connection.Status == NetConnectionStatus.Disconnected))
{
Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.ID}");
var msg = Networking.Peer.CreateMessage();
new Packets.HolePunch
{
Puncher = Main.LocalPlayerID,
Status = p.HolePunchStatus
}.Pack(msg);
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint });
}
}
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
}
public static void Add(Packets.HolePunchInit p)
{
if (PlayerList.Players.TryGetValue(p.TargetID, out var player))
{
Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
player.ConnectWhenPunched = p.Connect;
}
else
{
Main.Logger.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
}
}
public static void Punched(Packets.HolePunch p, IPEndPoint from)
{
Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
if (PlayerList.Players.TryGetValue(p.Puncher, out var puncher))
{
Main.Logger.Debug("Puncher identified as: " + puncher.Username);
puncher.HolePunchStatus = (byte)(p.Status + 1);
if (p.Status >= 3)
{
Main.Logger.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
if (puncher.ConnectWhenPunched && (puncher.Connection == null || puncher.Connection.Status == NetConnectionStatus.Disconnected))
{
Main.Logger.Debug("Connecting to peer: " + from);
var msg = Networking.Peer.CreateMessage();
new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
puncher.Connection = Networking.Peer.Connect(from, msg);
Networking.Peer.FlushSendQueue();
}
}
}
}
}
}

View File

@ -1,57 +1,68 @@
using System; using GTA.UI;
using Lidgren.Network; using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System.Threading.Tasks; using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography;
using System.Threading; using System.Threading;
using System.IO; using System.Threading.Tasks;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static partial class Networking internal static partial class Networking
{ {
public static NetClient Client; public static CoopPeer Peer;
public static float Latency = 0; public static float Latency => ServerConnection.AverageRoundtripTime / 2;
public static bool ShowNetworkInfo = false; public static bool ShowNetworkInfo = false;
public static Security Security; public static Security Security;
public static NetConnection ServerConnection;
private static readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new Dictionary<int, Action<PacketType, NetIncomingMessage>>();
internal static readonly Dictionary<PacketType, Func<NetIncomingMessage, Packet>> RequestHandlers = new Dictionary<PacketType, Func<NetIncomingMessage, Packet>>();
internal static float SimulatedLatency = 0;
public static bool IsConnecting { get; private set; }
public static IPEndPoint _targetServerEP;
static Networking() static Networking()
{ {
Security = new Security(Main.Logger); Security = new Security(Main.Logger);
Task.Run(() =>
{
while (true)
{
if (Client!=null)
{
ProcessMessage(Client.WaitMessage(200));
Client.FlushSendQueue();
}
else
{
Thread.Sleep(20);
}
}
});
} }
public static void ToggleConnection(string address,string username=null,string password=null) public static void ToggleConnection(string address, string username = null, string password = null, PublicKey publicKey = null)
{ {
if (IsOnServer) Menus.CoopMenu.Menu.Visible = false;
if (IsConnecting)
{ {
Client.Disconnect("Bye!"); _publicKeyReceived.Set();
IsConnecting = false;
Main.QueueAction(() => Notification.Show("Connection has been canceled"));
Peer?.Shutdown("Bye");
}
else if (IsOnServer)
{
Peer?.Shutdown("Bye");
} }
else else
{ {
Peer?.Dispose();
IsConnecting = true;
password = password ?? Main.Settings.Password; password = password ?? Main.Settings.Password;
username = username ?? Main.Settings.Username; username = username ?? Main.Settings.Username;
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP // 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0") NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
{ {
AutoFlushSendQueue = true AutoFlushSendQueue = false,
AcceptIncomingConnections = true,
MaximumConnections = 32,
PingInterval = 5
}; };
#if DEBUG
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated); config.SimulatedMinimumLatency = SimulatedLatency;
config.SimulatedRandomLatency = 0;
#endif
config.EnableMessageType(NetIncomingMessageType.UnconnectedData); config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
string[] ip = new string[2]; string[] ip = new string[2];
@ -67,87 +78,144 @@ namespace RageCoop.Client
throw new Exception("Malformed URL"); throw new Exception("Malformed URL");
} }
PlayerList.Cleanup();
EntityPool.AddPlayer(); EntityPool.AddPlayer();
if (publicKey == null && !string.IsNullOrEmpty(password) && !Menus.CoopMenu.ShowPopUp("", "WARNING", "Server's IP can be spoofed when using direct connection, do you wish to continue?", "", true))
{
IsConnecting = false;
return;
}
Task.Run(() => Task.Run(() =>
{ {
try try
{ {
_targetServerEP = CoreUtils.StringToEndPoint(address);
// Ensure static constructor invocation
DownloadManager.Cleanup(); DownloadManager.Cleanup();
Client = new NetClient(config); Peer = new CoopPeer(config);
Client.Start(); Peer.OnMessageReceived += (s, m) =>
Main.QueueAction(() => { GTA.UI.Notification.Show($"~y~Trying to connect..."); }); {
try { ProcessMessage(m); }
catch (Exception ex)
{
#if DEBUG
Main.Logger.Error(ex);
#endif
}
};
Main.QueueAction(() => { Notification.Show($"~y~Trying to connect..."); });
Menus.CoopMenu._serverConnectItem.Enabled = false; Menus.CoopMenu._serverConnectItem.Enabled = false;
Security.Regen(); Security.Regen();
if(!GetServerPublicKey(address)) if (publicKey == null)
{
if (!GetServerPublicKey(ip[0], int.Parse(ip[1])))
{ {
Menus.CoopMenu._serverConnectItem.Enabled = true; Menus.CoopMenu._serverConnectItem.Enabled = true;
throw new TimeoutException("Failed to retrive server's public key"); throw new TimeoutException("Failed to retrive server's public key");
} }
}
else
{
Security.SetServerPublicKey(publicKey.Modulus, publicKey.Exponent);
}
// Send HandshakePacket // Send handshake packet
NetOutgoingMessage outgoingMessage = Client.CreateMessage(); NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
var handshake = new Packets.Handshake() var handshake = new Packets.Handshake()
{ {
PedID = Main.LocalPlayerID, PedID = Main.LocalPlayerID,
Username = username, Username = username,
ModVersion = Main.CurrentVersion, ModVersion = Main.Version.ToString(),
PassHashEncrypted=Security.Encrypt(password.GetHash()) PasswordEncrypted = Security.Encrypt(password.GetBytes()),
InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port)
}; };
Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted, out handshake.AesIVCrypted); Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted, out handshake.AesIVCrypted);
handshake.Pack(outgoingMessage); handshake.Pack(outgoingMessage);
Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage); ServerConnection = Peer.Connect(ip[0], short.Parse(ip[1]), outgoingMessage);
} }
catch (Exception ex) catch (Exception ex)
{ {
Main.Logger.Error("Cannot connect to server: ", ex); Main.Logger.Error("Cannot connect to server: ", ex);
Main.QueueAction(() => GTA.UI.Notification.Show("Cannot connect to server: "+ex.Message)); Main.QueueAction(() => Notification.Show("Cannot connect to server: " + ex.Message));
} }
IsConnecting = false;
}); });
} }
} }
public static bool IsOnServer public static bool IsOnServer { get => ServerConnection?.Status == NetConnectionStatus.Connected; }
{
get { return Client?.ConnectionStatus == NetConnectionStatus.Connected; }
}
#region -- GET --
#region -- PLAYER -- #region -- PLAYER --
private static void PlayerConnect(Packets.PlayerConnect packet) private static void PlayerConnect(Packets.PlayerConnect packet)
{ {
var p = new PlayerData var p = new Player
{ {
PedID = packet.PedID, ID = packet.PedID,
Username = packet.Username, Username = packet.Username,
}; };
GTA.UI.Notification.Show($"{p.Username} connected.");
PlayerList.SetPlayer(packet.PedID, packet.Username); PlayerList.SetPlayer(packet.PedID, packet.Username);
Main.Logger.Debug($"player connected:{p.Username}"); Main.Logger.Debug($"player connected:{p.Username}");
Main.QueueAction(() =>
GTA.UI.Notification.Show($"~h~{p.Username}~h~ connected."));
} }
private static void PlayerDisconnect(Packets.PlayerDisconnect packet) private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{ {
var name=PlayerList.GetPlayer(packet.PedID).Username; var player = PlayerList.GetPlayer(packet.PedID);
GTA.UI.Notification.Show($"{name} left."); if (player == null) { return; }
PlayerList.RemovePlayer(packet.PedID); PlayerList.RemovePlayer(packet.PedID);
Main.QueueAction(() =>
{
EntityPool.RemoveAllFromPlayer(packet.PedID); EntityPool.RemoveAllFromPlayer(packet.PedID);
GTA.UI.Notification.Show($"~h~{player.Username}~h~ left.");
});
} }
#endregion // -- PLAYER -- #endregion // -- PLAYER --
#region -- GET --
private static bool GetServerPublicKey(string address,int timeout=10000) private static bool GetServerPublicKey(string host, int port, int timeout = 10000)
{ {
var msg=Client.CreateMessage(); Security.ServerRSA = null;
var msg = Peer.CreateMessage();
new Packets.PublicKeyRequest().Pack(msg); new Packets.PublicKeyRequest().Pack(msg);
var adds =address.Split(':'); Peer.SendUnconnectedMessage(msg, host, port);
Client.SendUnconnectedMessage(msg,adds[0],int.Parse(adds[1])); return _publicKeyReceived.WaitOne(timeout) && Security.ServerRSA != null;
return PublicKeyReceived.WaitOne(timeout);
} }
public static void GetResponse<T>(Packet request, Action<T> callback, ConnectionChannel channel = ConnectionChannel.RequestResponse) where T : Packet, new()
{
var received = new AutoResetEvent(false);
var id = NewRequestID();
PendingResponses.Add(id, (type, p) =>
{
var result = new T();
result.Deserialize(p);
callback(result);
});
var msg = Peer.CreateMessage();
msg.Write((byte)PacketType.Request);
msg.Write(id);
request.Pack(msg);
Peer.SendMessage(msg, ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel);
}
#endregion #endregion
private static int NewRequestID()
{
int ID = 0;
while ((ID == 0) || PendingResponses.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
RandomNumberGenerator.Create().GetBytes(rngBytes);
// Convert the bytes into an integer
ID = BitConverter.ToInt32(rngBytes, 0);
}
return ID;
}
} }
} }

View File

@ -1,101 +1,112 @@
using System; using GTA;
using System.Collections.Generic;
using Lidgren.Network; using Lidgren.Network;
using RageCoop.Core;
using GTA;
using RageCoop.Client.Menus; using RageCoop.Client.Menus;
using RageCoop.Core;
using System;
using System.Linq;
using System.Threading; using System.Threading;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static partial class Networking internal static partial class Networking
{ {
private static Func<byte, BitReader, object> _resolveHandle = (t, reader) =>
/// <summary>
/// Reduce GC pressure by reusing frequently used packets
/// </summary>
private static class ReceivedPackets
{
public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
public static Packets.ProjectileSync ProjectilePacket = new Packets.ProjectileSync();
}
/// <summary>
/// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/>
/// </summary>
private static readonly Func<byte, NetIncomingMessage, object> _resolveHandle = (t, reader) =>
{ {
switch (t) switch (t)
{ {
case 50: case 50:
return EntityPool.ServerProps[reader.ReadInt()].MainProp?.Handle; return EntityPool.ServerProps[reader.ReadInt32()].MainProp?.Handle;
case 51: case 51:
return EntityPool.GetPedByID(reader.ReadInt())?.MainPed?.Handle; return EntityPool.GetPedByID(reader.ReadInt32())?.MainPed?.Handle;
case 52: case 52:
return EntityPool.GetVehicleByID(reader.ReadInt())?.MainVehicle?.Handle; return EntityPool.GetVehicleByID(reader.ReadInt32())?.MainVehicle?.Handle;
case 60: case 60:
return EntityPool.ServerBlips[reader.ReadInt()].Handle; return EntityPool.ServerBlips[reader.ReadInt32()].Handle;
default: default:
throw new ArgumentException("Cannot resolve server side argument: " + t); throw new ArgumentException("Cannot resolve server side argument: " + t);
} }
}; };
private static AutoResetEvent PublicKeyReceived=new AutoResetEvent(false); private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false);
private static bool _recycle;
private static Dictionary<int, Action<PacketType, byte[]>> PendingResponses = new Dictionary<int, Action<PacketType, byte[]>>();
internal static Dictionary<PacketType, Func< byte[], Packet>> RequestHandlers = new Dictionary<PacketType, Func< byte[], Packet>>();
public static void ProcessMessage(NetIncomingMessage message) public static void ProcessMessage(NetIncomingMessage message)
{ {
if (message == null) { return; } if (message == null) { return; }
_recycle = true;
switch (message.MessageType) switch (message.MessageType)
{ {
case NetIncomingMessageType.StatusChanged: case NetIncomingMessageType.StatusChanged:
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte(); NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
string reason = message.ReadString(); string reason = message.ReadString();
switch (status) switch (status)
{ {
case NetConnectionStatus.InitiatedConnect: case NetConnectionStatus.InitiatedConnect:
#if !NON_INTERACTIVE if (message.SenderConnection == ServerConnection)
{
CoopMenu.InitiateConnectionMenuSetting(); CoopMenu.InitiateConnectionMenuSetting();
#endif }
break; break;
case NetConnectionStatus.Connected: case NetConnectionStatus.Connected:
Main.QueueAction(() => { if (message.SenderConnection == ServerConnection)
CoopMenu.ConnectedMenuSetting(); {
Main.MainChat.Init(); var response = message.SenderConnection.RemoteHailMessage;
PlayerList.Cleanup(); if ((PacketType)response.ReadByte() != PacketType.HandshakeSuccess)
GTA.UI.Notification.Show("~g~Connected!"); {
}); throw new Exception("Invalid handshake response!");
}
var p = new Packets.HandshakeSuccess();
p.Deserialize(response);
foreach (var player in p.Players)
{
PlayerList.SetPlayer(player.ID, player.Username);
}
Main.Connected();
}
else
{
// Self-initiated connection
if (message.SenderConnection.RemoteHailMessage == null) { return; }
Main.Logger.Info(">> Connected <<"); var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>();
if (PlayerList.Players.TryGetValue(p.ID, out var player))
{
player.Connection = message.SenderConnection;
Main.Logger.Debug($"Direct connection to {player.Username} established");
}
else
{
Main.Logger.Info($"Unidentified peer connection from {message.SenderEndPoint} was rejected.");
message.SenderConnection.Disconnect("eat poop");
}
}
break; break;
case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnected:
DownloadManager.Cleanup(); if (message.SenderConnection == ServerConnection)
// Reset all values
Latency = 0;
Main.QueueAction(() => Main.CleanUpWorld());
if (Main.MainChat.Focused)
{ {
Main.MainChat.Focused = false; Main.Disconnected(reason);
} }
Main.QueueAction(() => Main.CleanUp());
#if !NON_INTERACTIVE
CoopMenu.DisconnectedMenuSetting();
#endif
Main.QueueAction(() =>
GTA.UI.Notification.Show("~r~Disconnected: " + reason));
Main.Resources.Unload();
Main.Logger.Info($">> Disconnected << reason: {reason}");
break; break;
} }
break; break;
case NetIncomingMessageType.Data: case NetIncomingMessageType.Data:
{ {
if (message.LengthBytes == 0) { break; } if (message.LengthBytes == 0) { break; }
var packetType = PacketType.Unknown; var packetType = PacketType.Unknown;
try try
{ {
// Get packet type // Get packet type
packetType = (PacketType)message.ReadByte(); packetType = (PacketType)message.ReadByte();
switch (packetType) switch (packetType)
@ -105,7 +116,7 @@ namespace RageCoop.Client
int id = message.ReadInt32(); int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback)) if (PendingResponses.TryGetValue(id, out var callback))
{ {
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32())); callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id); PendingResponses.Remove(id);
} }
break; break;
@ -114,184 +125,177 @@ namespace RageCoop.Client
{ {
int id = message.ReadInt32(); int id = message.ReadInt32();
var realType = (PacketType)message.ReadByte(); var realType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
Main.Logger.Debug($"{id},{realType},{len}");
if (RequestHandlers.TryGetValue(realType, out var handler)) if (RequestHandlers.TryGetValue(realType, out var handler))
{ {
var response = Client.CreateMessage(); var response = Peer.CreateMessage();
response.Write((byte)PacketType.Response); response.Write((byte)PacketType.Response);
response.Write(id); response.Write(id);
handler(message.ReadBytes(len)).Pack(response); handler(message).Pack(response);
Client.SendMessage(response, NetDeliveryMethod.ReliableOrdered); Peer.SendMessage(response, ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel);
Peer.FlushSendQueue();
}
else
{
Main.Logger.Debug("Did not find a request handler of type: " + realType);
} }
break; break;
} }
default: default:
{ {
byte[] data = message.ReadBytes(message.ReadInt32());
HandlePacket(packetType, data); HandlePacket(packetType, message, message.SenderConnection, ref _recycle);
break; break;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Main.QueueAction(() => { #if DEBUG
GTA.UI.Notification.Show("~r~~h~Packet Error"); Main.QueueAction(() =>
{
GTA.UI.Notification.Show($"~r~~h~Packet Error {ex.Message}");
return true; return true;
}); });
Main.Logger.Error($"[{packetType}] {ex.Message}"); Main.Logger.Error($"[{packetType}] {ex.Message}");
Main.Logger.Error(ex); Main.Logger.Error(ex);
Client.Disconnect($"Packet Error [{packetType}]"); Peer.Shutdown($"Packet Error [{packetType}]");
#endif
_recycle = false;
} }
break; break;
} }
case NetIncomingMessageType.ConnectionLatencyUpdated:
Latency = message.ReadFloat();
break;
case NetIncomingMessageType.UnconnectedData: case NetIncomingMessageType.UnconnectedData:
{ {
var packetType = (PacketType)message.ReadByte(); var packetType = (PacketType)message.ReadByte();
int len = message.ReadInt32(); switch (packetType)
byte[] data = message.ReadBytes(len);
if (packetType==PacketType.PublicKeyResponse)
{ {
var packet=new Packets.PublicKeyResponse();
packet.Unpack(data);
Security.SetServerPublicKey(packet.Modulus,packet.Exponent);
PublicKeyReceived.Set();
}
case PacketType.HolePunch:
{
HolePunch.Punched(message.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
break;
}
case PacketType.PublicKeyResponse:
{
if (message.SenderEndPoint.ToString() != _targetServerEP.ToString() || !IsConnecting) { break; }
var packet = message.GetPacket<Packets.PublicKeyResponse>();
Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
_publicKeyReceived.Set();
break;
}
}
break; break;
} }
case NetIncomingMessageType.DebugMessage: case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.ErrorMessage: case NetIncomingMessageType.ErrorMessage:
case NetIncomingMessageType.WarningMessage: case NetIncomingMessageType.WarningMessage:
case NetIncomingMessageType.VerboseDebugMessage: case NetIncomingMessageType.VerboseDebugMessage:
Main.Logger.Trace(message.ReadString());
break; break;
default: default:
break; break;
} }
if (_recycle)
Client.Recycle(message); {
} Peer.Recycle(message);
private static void HandlePacket(PacketType packetType, byte[] data) }
}
private static void HandlePacket(PacketType packetType, NetIncomingMessage msg, NetConnection senderConnection, ref bool recycle)
{ {
switch (packetType) switch (packetType)
{ {
case PacketType.HolePunchInit:
HolePunch.Add(msg.GetPacket<Packets.HolePunchInit>());
break;
case PacketType.PlayerConnect: case PacketType.PlayerConnect:
{ PlayerConnect(msg.GetPacket<Packets.PlayerConnect>());
Packets.PlayerConnect packet = new Packets.PlayerConnect();
packet.Unpack(data);
Main.QueueAction(() => PlayerConnect(packet));
}
break; break;
case PacketType.PlayerDisconnect: case PacketType.PlayerDisconnect:
{ PlayerDisconnect(msg.GetPacket<Packets.PlayerDisconnect>());
Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect();
packet.Unpack(data);
Main.QueueAction(() => PlayerDisconnect(packet));
}
break; break;
case PacketType.PlayerInfoUpdate: case PacketType.PlayerInfoUpdate:
{ PlayerList.UpdatePlayer(msg.GetPacket<Packets.PlayerInfoUpdate>());
var packet = new Packets.PlayerInfoUpdate();
packet.Unpack(data);
PlayerList.UpdatePlayer(packet);
break; break;
}
#region ENTITY SYNC
case PacketType.VehicleSync: case PacketType.VehicleSync:
{ ReceivedPackets.VehicelPacket.Deserialize(msg);
VehicleSync(ReceivedPackets.VehicelPacket);
Packets.VehicleSync packet = new Packets.VehicleSync();
packet.Unpack(data);
VehicleSync(packet);
}
break; break;
case PacketType.PedSync: case PacketType.PedSync:
{ ReceivedPackets.PedPacket.Deserialize(msg);
PedSync(ReceivedPackets.PedPacket);
Packets.PedSync packet = new Packets.PedSync();
packet.Unpack(data);
PedSync(packet);
}
break;
case PacketType.VehicleStateSync:
{
Packets.VehicleStateSync packet = new Packets.VehicleStateSync();
packet.Unpack(data);
VehicleStateSync(packet);
}
break;
case PacketType.PedStateSync:
{
Packets.PedStateSync packet = new Packets.PedStateSync();
packet.Unpack(data);
PedStateSync(packet);
}
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
{ ReceivedPackets.ProjectilePacket.Deserialize(msg);
Packets.ProjectileSync packet = new Packets.ProjectileSync(); ProjectileSync(ReceivedPackets.ProjectilePacket);
packet.Unpack(data);
ProjectileSync(packet);
break; break;
}
#endregion
case PacketType.ChatMessage: case PacketType.ChatMessage:
{ {
Packets.ChatMessage packet = new Packets.ChatMessage(); Packets.ChatMessage packet = new Packets.ChatMessage((b) => Security.Decrypt(b));
packet.Unpack(data); packet.Deserialize(msg);
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; }); Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
} }
break; break;
case PacketType.Voice:
{
if (Main.Settings.Voice)
{
Packets.Voice packet = new Packets.Voice();
packet.Deserialize(msg);
SyncedPed player = EntityPool.GetPedByID(packet.ID);
player.IsSpeaking = true;
player.LastSpeakingTime = Main.Ticked;
Voice.AddVoiceData(packet.Buffer, packet.Recorded);
}
}
break;
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
packet.Unpack(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
} }
break; break;
case PacketType.CustomEventQueued: case PacketType.CustomEventQueued:
{ {
recycle = false;
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
Main.QueueAction(() => Main.QueueAction(() =>
{ {
packet.Unpack(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
Peer.Recycle(msg);
}); });
} }
break; break;
case PacketType.FileTransferChunk: case PacketType.FileTransferChunk:
{ {
Packets.FileTransferChunk packet = new Packets.FileTransferChunk(); Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
packet.Unpack(data); packet.Deserialize(msg);
DownloadManager.Write(packet.ID, packet.FileChunk); DownloadManager.Write(packet.ID, packet.FileChunk);
} }
break; break;
default: default:
if (packetType.IsSyncEvent()) if (packetType.IsSyncEvent())
{ {
// Dispatch to main thread recycle = false;
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; }); // Dispatch to script thread
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, msg); return true; });
} }
break; break;
} }
@ -301,65 +305,72 @@ namespace RageCoop.Client
{ {
SyncedPed c = EntityPool.GetPedByID(packet.ID); SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c == null) if (c == null)
{
if (EntityPool.PedsByID.Count(x => x.Value.OwnerID == packet.OwnerID) < Main.Settings.WorldPedSoftLimit / PlayerList.Players.Count ||
EntityPool.VehiclesByID.Any(x => x.Value.Position.DistanceTo(packet.Position) < 2) || packet.ID == packet.OwnerID)
{ {
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}"); // Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(c = new SyncedPed(packet.ID)); EntityPool.ThreadSafe.Add(c = new SyncedPed(packet.ID));
} }
PedDataFlags flags = packet.Flag; else return;
}
c.ID = packet.ID; c.ID = packet.ID;
//c.OwnerID=packet.OwnerID; c.OwnerID = packet.OwnerID;
c.Health = packet.Health; c.Health = packet.Health;
c.Position = packet.Position;
c.Rotation = packet.Rotation; c.Rotation = packet.Rotation;
c.Velocity = packet.Velocity; c.Velocity = packet.Velocity;
c.Speed = packet.Speed; c.Speed = packet.Speed;
c.CurrentWeaponHash = packet.CurrentWeaponHash; c.Flags = packet.Flags;
c.IsAiming = flags.HasPedFlag(PedDataFlags.IsAiming);
c.IsReloading = flags.HasPedFlag(PedDataFlags.IsReloading);
c.IsJumping = flags.HasPedFlag(PedDataFlags.IsJumping);
c.IsRagdoll = flags.HasPedFlag(PedDataFlags.IsRagdoll);
c.IsOnFire = flags.HasPedFlag(PedDataFlags.IsOnFire);
c.IsInParachuteFreeFall = flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
c.IsParachuteOpen = flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
c.IsOnLadder = flags.HasPedFlag(PedDataFlags.IsOnLadder);
c.IsVaulting = flags.HasPedFlag(PedDataFlags.IsVaulting);
c.IsInCover = flags.HasPedFlag(PedDataFlags.IsInCover);
c.IsInStealthMode = flags.HasPedFlag(PedDataFlags.IsInStealthMode);
c.Heading = packet.Heading; c.Heading = packet.Heading;
c.Position = packet.Position;
c.LastSyncedStopWatch.Restart();
if (c.IsRagdoll)
{
c.HeadPosition = packet.HeadPosition;
c.RightFootPosition = packet.RightFootPosition;
c.LeftFootPosition = packet.LeftFootPosition;
}
else if (c.Speed >= 4)
{
c.VehicleID = packet.VehicleID;
c.Seat = packet.Seat;
}
c.LastSynced = Main.Ticked; c.LastSynced = Main.Ticked;
if (c.IsAiming) if (c.IsAiming)
{ {
c.AimCoords = packet.AimCoords; c.AimCoords = packet.AimCoords;
} }
if (c.IsRagdoll) if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync))
{ {
c.RotationVelocity=packet.RotationVelocity; c.CurrentWeaponHash = packet.CurrentWeaponHash;
}
}
private static void PedStateSync(Packets.PedStateSync packet)
{
SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null) { return; }
c.ID=packet.ID;
c.OwnerID=packet.OwnerID;
c.Clothes = packet.Clothes; c.Clothes = packet.Clothes;
c.WeaponComponents = packet.WeaponComponents; c.WeaponComponents = packet.WeaponComponents;
c.WeaponTint = packet.WeaponTint; c.WeaponTint = packet.WeaponTint;
c.ModelHash=packet.ModelHash; c.Model = packet.ModelHash;
c.LastStateSynced = Main.Ticked;
c.BlipColor = packet.BlipColor; c.BlipColor = packet.BlipColor;
c.BlipSprite = packet.BlipSprite; c.BlipSprite = packet.BlipSprite;
c.BlipScale = packet.BlipScale; c.BlipScale = packet.BlipScale;
c.LastFullSynced = Main.Ticked;
}
} }
private static void VehicleSync(Packets.VehicleSync packet) private static void VehicleSync(Packets.VehicleSync packet)
{ {
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID); SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v == null) if (v == null)
{ {
if (EntityPool.VehiclesByID.Count(x => x.Value.OwnerID == packet.OwnerID) < Main.Settings.WorldVehicleSoftLimit / PlayerList.Players.Count ||
EntityPool.PedsByID.Any(x => x.Value.VehicleID == packet.ID || x.Value.Position.DistanceTo(packet.Position) < 2))
{
// Main.Logger.Debug($"Creating vehicle for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(v = new SyncedVehicle(packet.ID)); EntityPool.ThreadSafe.Add(v = new SyncedVehicle(packet.ID));
} }
else return;
}
if (v.IsLocal) { return; } if (v.IsLocal) { return; }
v.ID = packet.ID; v.ID = packet.ID;
v.OwnerID = packet.OwnerID;
v.Flags = packet.Flags;
v.Position = packet.Position; v.Position = packet.Position;
v.Quaternion = packet.Quaternion; v.Quaternion = packet.Quaternion;
v.SteeringAngle = packet.SteeringAngle; v.SteeringAngle = packet.SteeringAngle;
@ -369,61 +380,41 @@ namespace RageCoop.Client
v.RotationVelocity = packet.RotationVelocity; v.RotationVelocity = packet.RotationVelocity;
v.DeluxoWingRatio = packet.DeluxoWingRatio; v.DeluxoWingRatio = packet.DeluxoWingRatio;
v.LastSynced = Main.Ticked; v.LastSynced = Main.Ticked;
} v.LastSyncedStopWatch.Restart();
private static void VehicleStateSync(Packets.VehicleStateSync packet) if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{ {
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null||v.IsLocal) { return; }
v.ID= packet.ID;
v.OwnerID= packet.OwnerID;
v.DamageModel = packet.DamageModel; v.DamageModel = packet.DamageModel;
v.EngineHealth = packet.EngineHealth; v.EngineHealth = packet.EngineHealth;
v.OwnerID=packet.OwnerID;
v.Mods = packet.Mods; v.Mods = packet.Mods;
v.ModelHash=packet.ModelHash; v.Model = packet.ModelHash;
v.Colors = packet.Colors; v.Colors = packet.Colors;
v.LandingGear = packet.LandingGear; v.LandingGear = packet.LandingGear;
v.RoofState = (VehicleRoofState)packet.RoofState; v.RoofState = (VehicleRoofState)packet.RoofState;
v.EngineRunning = packet.Flag.HasVehFlag(VehicleDataFlags.IsEngineRunning);
v.LightsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreLightsOn);
v.BrakeLightsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn);
v.HighBeamsOn = packet.Flag.HasVehFlag(VehicleDataFlags.AreHighBeamsOn);
v.SireneActive = packet.Flag.HasVehFlag(VehicleDataFlags.IsSirenActive);
v.IsDead = packet.Flag.HasVehFlag(VehicleDataFlags.IsDead);
v.HornActive = packet.Flag.HasVehFlag(VehicleDataFlags.IsHornActive);
v.Transformed = packet.Flag.HasVehFlag(VehicleDataFlags.IsTransformed);
v.Passengers=new Dictionary<VehicleSeat, SyncedPed>();
v.LockStatus = packet.LockStatus; v.LockStatus = packet.LockStatus;
v.RadioStation = packet.RadioStation; v.RadioStation = packet.RadioStation;
v.LicensePlate = packet.LicensePlate; v.LicensePlate = packet.LicensePlate;
v.Livery = packet.Livery; v.Livery = packet.Livery;
v.Flags=packet.Flag; v.LastFullSynced = Main.Ticked;
foreach (KeyValuePair<int, int> pair in packet.Passengers)
{
if (EntityPool.PedExists(pair.Value))
{
v.Passengers.Add((VehicleSeat)pair.Key, EntityPool.GetPedByID(pair.Value));
} }
} }
v.LastStateSynced= Main.Ticked;
}
private static void ProjectileSync(Packets.ProjectileSync packet) private static void ProjectileSync(Packets.ProjectileSync packet)
{ {
var p = EntityPool.GetProjectileByID(packet.ID); var p = EntityPool.GetProjectileByID(packet.ID);
if (p == null) if (p == null)
{ {
if (packet.Exploded) { return; } if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; }
// Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}"); // Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
EntityPool.ThreadSafe.Add(p = new SyncedProjectile(packet.ID)); EntityPool.ThreadSafe.Add(p = new SyncedProjectile(packet.ID));
} }
p.Flags = packet.Flags;
p.Position = packet.Position; p.Position = packet.Position;
p.Rotation = packet.Rotation; p.Rotation = packet.Rotation;
p.Velocity = packet.Velocity; p.Velocity = packet.Velocity;
p.Hash=(WeaponHash)packet.WeaponHash; p.WeaponHash = (WeaponHash)packet.WeaponHash;
p.ShooterID=packet.ShooterID; p.Shooter = packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ?
p.Exploded=packet.Exploded; (SyncedEntity)EntityPool.GetVehicleByID(packet.ShooterID) : EntityPool.GetPedByID(packet.ShooterID);
p.LastSynced = Main.Ticked; p.LastSynced = Main.Ticked;
p.LastSyncedStopWatch.Restart();
} }
} }
} }

View File

@ -1,158 +1,179 @@
using Lidgren.Network; using GTA;
using RageCoop.Core;
using GTA;
using GTA.Native;
using GTA.Math; using GTA.Math;
using GTA.Native;
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static partial class Networking internal static partial class Networking
{ {
#region -- SEND --
/// <summary> /// <summary>
/// Pack the packet then send to server. /// Reduce GC pressure by reusing frequently used packets
/// </summary> /// </summary>
/// <param name="p"></param> private static class SendPackets
/// <param name="channel"></param>
/// <param name="method"></param>
public static void Send(Packet p, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{ {
NetOutgoingMessage outgoingMessage = Client.CreateMessage(); public static Packets.PedSync PedPacket = new Packets.PedSync();
p.Pack(outgoingMessage); public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
Client.SendMessage(outgoingMessage, method, (int)channel); public static Packets.ProjectileSync ProjectilePacket = new Packets.ProjectileSync();
}
public static int SyncInterval = 30;
public static List<NetConnection> Targets = new List<NetConnection>();
public static void SendSync(Packet p, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
Peer.SendTo(p, Targets, channel, method);
} }
public static void SendPed(SyncedPed c) public static void SendPed(SyncedPed sp, bool full)
{ {
Ped p = c.MainPed; if (sp.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
var packet=new Packets.PedSync()
{ {
ID =c.ID, return;
Health = p.Health,
Position = p.Position,
Rotation = p.Rotation,
Velocity = p.Velocity,
Speed = p.GetPedSpeed(),
CurrentWeaponHash = (uint)p.Weapons.Current.Hash,
Flag = p.GetPedFlags(),
Heading=p.Heading,
};
if (packet.Flag.HasPedFlag(PedDataFlags.IsAiming))
{
packet.AimCoords = p.GetAimCoord();
} }
if (packet.Flag.HasPedFlag(PedDataFlags.IsRagdoll)) Ped ped = sp.MainPed;
var p = SendPackets.PedPacket;
p.ID = sp.ID;
p.OwnerID = sp.OwnerID;
p.Health = ped.Health;
p.Rotation = ped.ReadRotation();
p.Velocity = ped.ReadVelocity();
p.Speed = ped.GetPedSpeed();
p.Flags = ped.GetPedFlags();
p.Heading = ped.Heading;
if (p.Flags.HasPedFlag(PedDataFlags.IsAiming))
{ {
packet.RotationVelocity=p.RotationVelocity; p.AimCoords = ped.GetAimCoord();
} }
Send(packet, ConnectionChannel.PedSync); if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll))
}
public static void SendPedState(SyncedPed c)
{ {
Ped p = c.MainPed; p.HeadPosition = ped.Bones[Bone.SkelHead].Position;
p.RightFootPosition = ped.Bones[Bone.SkelRightFoot].Position;
p.LeftFootPosition = ped.Bones[Bone.SkelLeftFoot].Position;
}
else
{
// Seat sync
if (p.Speed >= 4)
{
var veh = ped.CurrentVehicle?.GetSyncEntity() ?? ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity();
p.VehicleID = veh?.ID ?? 0;
if (p.VehicleID == 0) { Main.Logger.Error("Invalid vehicle"); }
if (p.Speed == 5)
{
p.Seat = ped.GetSeatTryingToEnter();
}
else
{
p.Seat = ped.SeatIndex;
}
if (!veh.IsLocal && p.Speed == 4 && p.Seat == VehicleSeat.Driver)
{
veh.OwnerID = Main.LocalPlayerID;
SyncEvents.TriggerChangeOwner(veh.ID, Main.LocalPlayerID);
}
}
p.Position = ped.ReadPosition();
}
sp.LastSentStopWatch.Restart();
if (full)
{
var w = ped.VehicleWeapon;
p.CurrentWeaponHash = (w != VehicleWeaponHash.Invalid) ? (uint)w : (uint)ped.Weapons.Current.Hash;
p.Flags |= PedDataFlags.IsFullSync;
p.Clothes = ped.GetPedClothes();
p.ModelHash = ped.Model.Hash;
p.WeaponComponents = ped.Weapons.Current.GetWeaponComponents();
p.WeaponTint = (byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
var packet = new Packets.PedStateSync()
{
ID = c.ID,
OwnerID=c.OwnerID,
Clothes=p.GetPedClothes(),
ModelHash=p.Model.Hash,
WeaponComponents=p.Weapons.Current.GetWeaponComponents(),
WeaponTint=(byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, p, p.Weapons.Current.Hash),
};
Blip b; Blip b;
if (c.IsPlayer) if (sp.IsPlayer)
{ {
packet.BlipColor=Scripting.API.Config.BlipColor; p.BlipColor = Scripting.API.Config.BlipColor;
packet.BlipSprite=Scripting.API.Config.BlipSprite; p.BlipSprite = Scripting.API.Config.BlipSprite;
packet.BlipScale=Scripting.API.Config.BlipScale; p.BlipScale = Scripting.API.Config.BlipScale;
} }
else if ((b = p.AttachedBlip) !=null) else if ((b = ped.AttachedBlip) != null)
{ {
packet.BlipColor=b.Color; p.BlipColor = b.Color;
packet.BlipSprite=b.Sprite; p.BlipSprite = b.Sprite;
if (packet.BlipSprite==BlipSprite.PoliceOfficer || packet.BlipSprite==BlipSprite.PoliceOfficer2) if (p.BlipSprite == BlipSprite.PoliceOfficer || p.BlipSprite == BlipSprite.PoliceOfficer2)
{ {
packet.BlipScale=0.5f; p.BlipScale = 0.5f;
} }
} }
Send(packet, ConnectionChannel.PedSync); else
}
public static void SendVehicle(SyncedVehicle v)
{ {
p.BlipColor = (BlipColor)255;
}
}
SendSync(p, ConnectionChannel.PedSync);
}
public static void SendVehicle(SyncedVehicle v, bool full)
{
if (v.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{
return;
}
Vehicle veh = v.MainVehicle; Vehicle veh = v.MainVehicle;
var packet = new Packets.VehicleSync() var packet = SendPackets.VehicelPacket;
packet.ID = v.ID;
packet.OwnerID = v.OwnerID;
packet.Flags = v.GetVehicleFlags();
packet.SteeringAngle = veh.SteeringAngle;
packet.Position = veh.ReadPosition();
packet.Velocity = veh.Velocity;
packet.Quaternion = veh.ReadQuaternion();
packet.RotationVelocity = veh.RotationVelocity;
packet.ThrottlePower = veh.ThrottlePower;
packet.BrakePower = veh.BrakePower;
v.LastSentStopWatch.Restart();
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) { packet.DeluxoWingRatio = v.MainVehicle.GetDeluxoWingRatio(); }
if (full)
{ {
ID =v.ID,
SteeringAngle = veh.SteeringAngle,
Position = veh.PredictPosition(),
Quaternion=veh.Quaternion,
// Rotation = veh.Rotation,
Velocity = veh.Velocity,
RotationVelocity=veh.RotationVelocity,
ThrottlePower = veh.ThrottlePower,
BrakePower = veh.BrakePower,
};
if (v.MainVehicle.Model.Hash==1483171323) { packet.DeluxoWingRatio=v.MainVehicle.GetDeluxoWingRatio(); }
Send(packet,ConnectionChannel.VehicleSync);
}
public static void SendVehicleState(SyncedVehicle v)
{
Vehicle veh = v.MainVehicle;
byte primaryColor = 0; byte primaryColor = 0;
byte secondaryColor = 0; byte secondaryColor = 0;
unsafe unsafe
{ {
Function.Call<byte>(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor); Function.Call<byte>(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
} }
var packet = new Packets.VehicleStateSync() packet.Flags |= VehicleDataFlags.IsFullSync;
{ packet.Colors = new byte[] { primaryColor, secondaryColor };
ID =v.ID, packet.DamageModel = veh.GetVehicleDamageModel();
OwnerID = v.OwnerID, packet.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0;
Flag = veh.GetVehicleFlags(), packet.RoofState = (byte)veh.RoofState;
Colors=new byte[] { primaryColor, secondaryColor }, packet.Mods = veh.Mods.GetVehicleMods();
DamageModel=veh.GetVehicleDamageModel(), packet.ModelHash = veh.Model.Hash;
LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0, packet.EngineHealth = veh.EngineHealth;
RoofState=(byte)veh.RoofState, packet.LockStatus = veh.LockStatus;
Mods = veh.Mods.GetVehicleMods(), packet.LicensePlate = Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
ModelHash=veh.Model.Hash, packet.Livery = Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
EngineHealth=veh.EngineHealth,
Passengers=veh.GetPassengers(),
LockStatus=veh.LockStatus,
LicensePlate=Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh),
Livery=Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh)
};
if (v.MainVehicle == Game.Player.LastVehicle) if (v.MainVehicle == Game.Player.LastVehicle)
{ {
packet.RadioStation = Util.GetPlayerRadioIndex(); packet.RadioStation = Util.GetPlayerRadioIndex();
} }
Send(packet, ConnectionChannel.VehicleSync); if (packet.EngineHealth > v.LastEngineHealth)
{
packet.Flags |= VehicleDataFlags.Repaired;
}
v.LastEngineHealth = packet.EngineHealth;
}
SendSync(packet, ConnectionChannel.VehicleSync);
} }
public static void SendProjectile(SyncedProjectile sp) public static void SendProjectile(SyncedProjectile sp)
{ {
var p = sp.MainProjectile; sp.ExtractData(ref SendPackets.ProjectilePacket);
var packet = new Packets.ProjectileSync() if (sp.MainProjectile.IsDead) { EntityPool.RemoveProjectile(sp.ID, "Dead"); }
{ SendSync(SendPackets.ProjectilePacket, ConnectionChannel.ProjectileSync);
ID =sp.ID,
ShooterID=sp.ShooterID,
Position=p.Position,
Rotation=p.Rotation,
Velocity=p.Velocity,
WeaponHash=(uint)p.WeaponHash,
Exploded=p.IsDead
};
if (p.IsDead) { EntityPool.RemoveProjectile(sp.ID,"Dead"); }
Send(packet, ConnectionChannel.ProjectileSync);
} }
#region SYNC EVENTS #region SYNC EVENTS
public static void SendBulletShot(Vector3 start,Vector3 end,uint weapon,int ownerID) public static void SendBullet(Vector3 start, Vector3 end, uint weapon, int ownerID)
{ {
Send(new Packets.BulletShot() SendSync(new Packets.BulletShot()
{ {
StartPosition = start, StartPosition = start,
EndPosition = end, EndPosition = end,
@ -160,19 +181,27 @@ namespace RageCoop.Client
WeaponHash = weapon, WeaponHash = weapon,
}, ConnectionChannel.SyncEvents); }, ConnectionChannel.SyncEvents);
} }
public static void SendVehicleBullet(uint hash, SyncedPed owner, EntityBone b)
{
SendSync(new Packets.VehicleBulletShot
{
StartPosition = b.Position,
EndPosition = b.Position + b.ForwardVector,
OwnerID = owner.ID,
Bone = (ushort)b.Index,
WeaponHash = hash
});
}
#endregion #endregion
public static void SendChatMessage(string message) public static void SendChatMessage(string message)
{ {
NetOutgoingMessage outgoingMessage = Client.CreateMessage(); Peer.SendTo(new Packets.ChatMessage(new Func<string, byte[]>((s) => Security.Encrypt(s.GetBytes())))
{ Username = Main.Settings.Username, Message = message }, ServerConnection, ConnectionChannel.Chat, NetDeliveryMethod.ReliableOrdered);
new Packets.ChatMessage() { Username = Main.Settings.Username, Message = message }.Pack(outgoingMessage); Peer.FlushSendQueue();
}
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Chat); public static void SendVoiceMessage(byte[] buffer, int recorded)
Client.FlushSendQueue(); {
SendSync(new Packets.Voice() { ID = Main.LocalPlayerID, Buffer = buffer, Recorded = recorded }, ConnectionChannel.Voice, NetDeliveryMethod.ReliableOrdered);
#if DEBUG
#endif
} }
#endregion
} }
} }

View File

@ -1,8 +1,4 @@
using System; using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace RageCoop.Client namespace RageCoop.Client
@ -17,11 +13,11 @@ namespace RageCoop.Client
{ {
while (true) while (true)
{ {
var bu=Networking.Client.Statistics.SentBytes; var bu = Networking.Peer.Statistics.SentBytes;
var bd = Networking.Client.Statistics.ReceivedBytes; var bd = Networking.Peer.Statistics.ReceivedBytes;
Thread.Sleep(1000); Thread.Sleep(1000);
BytesUpPerSecond=Networking.Client.Statistics.SentBytes-bu; BytesUpPerSecond = Networking.Peer.Statistics.SentBytes - bu;
BytesDownPerSecond=Networking.Client.Statistics.ReceivedBytes-bd; BytesDownPerSecond = Networking.Peer.Statistics.ReceivedBytes - bd;
} }
}); });
} }

View File

@ -1,9 +1,11 @@
using System.Collections.Generic; using GTA;
using System.Linq;
using GTA;
using GTA.Math; using GTA.Math;
using RageCoop.Core;
using GTA.Native; using GTA.Native;
using Lidgren.Network;
using RageCoop.Core;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -16,7 +18,7 @@ namespace RageCoop.Client
public static ulong Pressed { get; set; } public static ulong Pressed { get; set; }
public static bool LeftAlign = true; public static bool LeftAlign = true;
public static Dictionary<int,PlayerData> Players=new Dictionary<int, PlayerData> { }; public static Dictionary<int, Player> Players = new Dictionary<int, Player> { };
public static void Tick() public static void Tick()
{ {
if (!Networking.IsOnServer) if (!Networking.IsOnServer)
@ -26,7 +28,7 @@ namespace RageCoop.Client
if ((Util.GetTickCount64() - _lastUpdate) >= 1000) if ((Util.GetTickCount64() - _lastUpdate) >= 1000)
{ {
Update( Main.Settings.Username); Update();
} }
if ((Util.GetTickCount64() - Pressed) < 5000 && !Main.MainChat.Focused if ((Util.GetTickCount64() - Pressed) < 5000 && !Main.MainChat.Focused
@ -42,54 +44,73 @@ namespace RageCoop.Client
} }
} }
private static void Update( string localUsername) private static void Update()
{ {
_lastUpdate = Util.GetTickCount64(); _lastUpdate = Util.GetTickCount64();
_mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0); _mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0);
_mainScaleform.CallFunction("SET_DATA_SLOT", 0, $"{Networking.Latency * 1000:N0}ms", localUsername, 116, 0, 0, "", "", 2, "", "", ' ');
int i = 1; int i = 0;
foreach (var player in Players) foreach (var player in Players.Values)
{ {
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Value.Latency * 1000:N0}ms", player.Value.Username, 116, 0, i - 1, "", "", 2, "", "", ' '); _mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.Username + (player.IsHost ? " (Host)" : ""), 116, 0, i - 1, "", "", 2, "", "", ' ');
} }
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count+1} players"); _mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count} players");
_mainScaleform.CallFunction("DISPLAY_VIEW"); _mainScaleform.CallFunction("DISPLAY_VIEW");
} }
public static void SetPlayer(int id, string username, float latency = 0) public static void SetPlayer(int id, string username, float latency = 0)
{ {
Main.Logger.Debug($"{id},{username},{latency}");
PlayerData p; if (Players.TryGetValue(id, out Player p))
if (Players.TryGetValue(id,out p))
{ {
p.Username = username; p.Username = username;
p.PedID=id; p.ID = id;
p.Latency=latency; p._latencyToServer = latency;
} }
else else
{ {
p = new PlayerData { PedID=id, Username=username,Latency=latency }; p = new Player { ID = id, Username = username, _latencyToServer = latency };
Players.Add(id, p); Players.Add(id, p);
} }
} }
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet) public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
{ {
var p = GetPlayer(packet.PedID); var p = GetPlayer(packet.PedID);
if(p?.Character != null) if (p != null)
{ {
p.Latency= packet.Latency; p._latencyToServer = packet.Latency;
p.Position = packet.Position;
p.IsHost = packet.IsHost;
Main.QueueAction(() =>
{
if (p.FakeBlip?.Exists() != true)
{
p.FakeBlip = World.CreateBlip(p.Position);
}
if (EntityPool.PedExists(p.ID))
{
p.FakeBlip.DisplayType = BlipDisplayType.NoDisplay;
}
else
{
p.FakeBlip.Color = Scripting.API.Config.BlipColor;
p.FakeBlip.Scale = Scripting.API.Config.BlipScale;
p.FakeBlip.Sprite = Scripting.API.Config.BlipSprite;
p.FakeBlip.DisplayType = BlipDisplayType.Default;
p.FakeBlip.Position = p.Position;
}
});
} }
} }
public static PlayerData GetPlayer(int id) public static Player GetPlayer(int id)
{ {
PlayerData p; Players.TryGetValue(id, out Player p);
Players.TryGetValue(id, out p);
return p; return p;
} }
public static PlayerData GetPlayer(SyncedPed p) public static Player GetPlayer(SyncedPed p)
{ {
var player = GetPlayer(p.ID); var player = GetPlayer(p.ID);
if (player != null) if (player != null)
@ -100,35 +121,48 @@ namespace RageCoop.Client
} }
public static void RemovePlayer(int id) public static void RemovePlayer(int id)
{ {
if (Players.ContainsKey(id)) if (Players.TryGetValue(id, out var player))
{ {
Players.Remove(id); Players.Remove(id);
Main.QueueAction(() => player.FakeBlip?.Delete());
} }
} }
public static void Cleanup() public static void Cleanup()
{ {
Players=new Dictionary<int, PlayerData>{ }; foreach (var p in Players.Values.ToArray())
}
}
internal class PlayerData
{ {
p.FakeBlip?.Delete();
}
Players = new Dictionary<int, Player> { };
}
}
public class Player
{
public byte HolePunchStatus { get; internal set; } = 1;
public bool IsHost { get; internal set; }
public string Username { get; internal set; } public string Username { get; internal set; }
/// <summary> /// <summary>
/// Universal character ID. /// Universal ped ID.
/// </summary> /// </summary>
public int PedID public int ID
{ {
get; internal set; get; internal set;
} }
public SyncedPed Character { get; set; } public IPEndPoint InternalEndPoint { get; internal set; }
public IPEndPoint ExternalEndPoint { get; internal set; }
internal bool ConnectWhenPunched { get; set; }
public Blip FakeBlip { get; internal set; }
public Vector3 Position { get; internal set; }
public SyncedPed Character { get; internal set; }
/// <summary> /// <summary>
/// Player Latency in second. /// Player round-trip time in seconds, will be the rtt to server if not using P2P connection.
/// </summary> /// </summary>
public float Latency { get; set; } public float Ping => Main.LocalPlayerID == ID ? Networking.Latency * 2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer * 2);
public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime / 2 : Networking.Latency + _latencyToServer;
internal float _latencyToServer = 0;
public bool DisplayNameTag { get; set; } = true; public bool DisplayNameTag { get; set; } = true;
public NetConnection Connection { get; internal set; }
public bool HasDirectConnection => Connection?.Status == NetConnectionStatus.Connected;
} }
} }

View File

@ -0,0 +1,22 @@

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Resources;
// General Information
[assembly: AssemblyTitle("RageCoop.Client")]
[assembly: AssemblyDescription("RageCoop.Client")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("RAGECOOP")]
[assembly: AssemblyProduct("RageCoop.Client")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version information
[assembly: AssemblyVersion("1.5.4.7")]
[assembly: AssemblyFileVersion("1.5.4.7")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -0,0 +1,46 @@
<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
string output = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo.cs"));
Regex pattern = new Regex("AssemblyVersion\\(\"(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<revision>\\d+)\\.(?<build>\\d+)\"\\)");
MatchCollection matches = pattern.Matches(output);
if( matches.Count == 1 )
{
major = Convert.ToInt32(matches[0].Groups["major"].Value);
minor = Convert.ToInt32(matches[0].Groups["minor"].Value);
build = Convert.ToInt32(matches[0].Groups["build"].Value) + 1;
revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
if( this.Host.ResolveParameterValue("-","-","BuildConfiguration") == "Release" )
revision++;
}
#>
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Resources;
// General Information
[assembly: AssemblyTitle("RageCoop.Client")]
[assembly: AssemblyDescription("RageCoop.Client")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("RAGECOOP")]
[assembly: AssemblyProduct("RageCoop.Client")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version informationr(
[assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]
<#+
int major = 1;
int minor = 0;
int revision = 0;
int build = 0;
#>

View File

@ -1,55 +1,138 @@
<Project Sdk="Microsoft.NET.Sdk"> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<UseWindowsForms>True</UseWindowsForms> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <ProjectGuid>{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}</ProjectGuid>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath> <OutputType>Library</OutputType>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly> <AppDesignerFolder>Properties</AppDesignerFolder>
<GenerateDocumentationFile>True</GenerateDocumentationFile> <RootNamespace>RageCoop.Client</RootNamespace>
<DocumentationFile></DocumentationFile> <AssemblyName>RageCoop.Client</AssemblyName>
<DebugType>portable</DebugType> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<AssemblyVersion>0.5.0</AssemblyVersion> <FileAlignment>512</FileAlignment>
<FileVersion>0.5.0</FileVersion> <Deterministic>true</Deterministic>
<Version>0.5.0</Version> <TargetFrameworkProfile />
<GeneratePackageOnBuild>False</GeneratePackageOnBuild> <NuGetPackageImportStamp>
<Authors>RAGECOOP</Authors> </NuGetPackageImportStamp>
<Description>An API reference for developing client-side resource for RAGECOOP</Description>
<PackageProjectUrl>https://ragecoop.online/</PackageProjectUrl>
<RepositoryUrl>https://github.com/RAGECOOP/RAGECOOP-V</RepositoryUrl>
<ApplicationIcon>icon.ico</ApplicationIcon>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<DefineConstants>SHVDN3</DefineConstants>
<TargetFrameworks>net48</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'"> <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<OutDir>..\bin\Debug\Client</OutDir> <OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutDir>..\bin\Release\Client</OutDir> <OutPutPath>..\bin\Release\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="icon.ico" /> <Compile Include="Debug.cs" />
</ItemGroup> <Compile Include="DevTools\DevTool.cs" />
<Compile Include="Main.cs" />
<ItemGroup> <Compile Include="Menus\CoopMenu.cs" />
<PackageReference Include="SharpZipLib" Version="1.3.3" /> <Compile Include="Menus\Sub\DebugMenu.cs" />
<Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" />
<Compile Include="Networking\Networking.cs" />
<Compile Include="Networking\Receive.cs" />
<Compile Include="Networking\Send.cs" />
<Compile Include="Networking\Statistics.cs" />
<Compile Include="PlayerList.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>AssemblyInfo.tt</DependentUpon>
</Compile>
<Compile Include="Scripting\API.cs" />
<Compile Include="Scripting\BaseScript.cs" />
<Compile Include="Scripting\ClientScript.cs" />
<Compile Include="Scripting\Resources.cs" />
<Compile Include="Security.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Members.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.Animations.cs" />
<Compile Include="Sync\Entities\SyncedEntity.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
<Compile Include="Sync\Entities\SyncedProjectile.cs" />
<Compile Include="Sync\Entities\SyncedProp.cs" />
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.cs" />
<Compile Include="Sync\Entities\Vehicle\SyncedVehicle.Members.cs" />
<Compile Include="Sync\EntityPool.cs" />
<Compile Include="Sync\SyncEvents.cs" />
<Compile Include="Sync\Voice.cs" />
<Compile Include="Util\AddOnDataProvider.cs" />
<Compile Include="Util\Memory.cs" />
<Compile Include="Util\NativeCaller.cs" />
<Compile Include="Util\PedConfigFlags.cs" />
<Compile Include="Util\PedExtensions.cs" />
<Compile Include="Util\TaskType.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="Util\VehicleExtensions.cs" />
<Compile Include="Util\WeaponUtil.cs" />
<Compile Include="WorldThread.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj" /> <ProjectReference Include="..\RageCoop.Core\RageCoop.Core.csproj">
<Project>{cc2e8102-e568-4524-aa1f-f8e0f1cfe58a}</Project>
<Name>RageCoop.Core</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="LemonUI.SHVDN3"> <Reference Include="Costura, Version=5.7.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.5.7.0\lib\netstandard1.0\Costura.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=1.3.3.11, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="LemonUI.SHVDN3, Version=1.10.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath> <HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference> </Reference>
<Reference Include="Lidgren.Network"> <Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Lidgren.Network.dll</HintPath> <HintPath>..\libs\Lidgren.Network.dll</HintPath>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json"> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.ObjectPool, Version=6.0.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.ObjectPool.6.0.8\lib\net461\Microsoft.Extensions.ObjectPool.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Registry.4.7.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="NAudio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.2.1.0\lib\net472\NAudio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Asio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Asio.2.1.0\lib\netstandard2.0\NAudio.Asio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Core.2.1.0\lib\netstandard2.0\NAudio.Core.dll</HintPath>
</Reference>
<Reference Include="NAudio.Midi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Midi.2.1.0\lib\netstandard2.0\NAudio.Midi.dll</HintPath>
</Reference>
<Reference Include="NAudio.Wasapi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Wasapi.2.1.0\lib\netstandard2.0\NAudio.Wasapi.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinForms, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinForms.2.1.0\lib\net472\NAudio.WinForms.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinMM, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinMM.2.1.0\lib\netstandard2.0\NAudio.WinMM.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath> <HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="ScriptHookVDotNet"> <Reference Include="ScriptHookVDotNet">
@ -58,6 +141,170 @@
<Reference Include="ScriptHookVDotNet3"> <Reference Include="ScriptHookVDotNet3">
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath> <HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" />
<Reference Include="System.AppContext, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Console.4.3.0\lib\net46\System.Console.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Diagnostics.Tracing, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.Tracing.4.3.0\lib\net462\System.Diagnostics.Tracing.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Globalization.Calendars, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Linq.Expressions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Sockets, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Reflection, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.Extensions, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.Extensions.4.3.0\lib\net462\System.Runtime.Extensions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.AccessControl, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.AccessControl.4.7.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.4.7.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Text.RegularExpressions, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.RegularExpressions.4.3.0\lib\net463\System.Text.RegularExpressions.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml.ReaderWriter, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Serialization" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="FodyWeavers.xml" />
<Content Include="Properties\AssemblyInfo.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>AssemblyInfo.cs</LastGenOutput>
</Content>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.6.6.3\build\Fody.targets" Condition="Exists('..\packages\Fody.6.6.3\build\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.6.6.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.6.3\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.props'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets'))" />
</Target>
<Import Project="..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.5.7.0\build\Costura.Fody.targets')" />
<PropertyGroup Condition=" '$(DevEnvDir)' != '*Undefined*'">
<PostBuildEvent>"$(DevEnvDir)TextTransform.exe" -a !!BuildConfiguration!$(Configuration) "$(ProjectDir)Properties\AssemblyInfo.tt"</PostBuildEvent>
</PropertyGroup>
</Project> </Project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Client\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>

View File

@ -1,10 +1,10 @@
#undef DEBUG #undef DEBUG
using System.Collections.Generic;
using System;
using System.Linq;
using RageCoop.Core;
using System.Windows.Forms;
using GTA; using GTA;
using Newtonsoft.Json;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace RageCoop.Client.Scripting namespace RageCoop.Client.Scripting
{ {
@ -25,7 +25,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// Provides vital functionality to interact with RAGECOOP /// Provides vital functionality to interact with RAGECOOP
/// </summary> /// </summary>
public static class API public class API
{ {
#region INTERNAL #region INTERNAL
internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new Dictionary<int, List<Action<CustomEventReceivedArgs>>>(); internal static Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new Dictionary<int, List<Action<CustomEventReceivedArgs>>>();
@ -40,7 +40,7 @@ namespace RageCoop.Client.Scripting
/// </summary> /// </summary>
public static string Username public static string Username
{ {
get { return Main.Settings.Username; } get => Main.Settings.Username;
set set
{ {
if (Networking.IsOnServer || string.IsNullOrEmpty(value)) if (Networking.IsOnServer || string.IsNullOrEmpty(value))
@ -91,7 +91,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// The local player is dead /// The local player is dead
/// </summary> /// </summary>
public static event EmptyEvent OnPlayerDied; public static event EventHandler<string> OnPlayerDied;
/// <summary> /// <summary>
/// A local vehicle is spawned /// A local vehicle is spawned
@ -133,7 +133,7 @@ namespace RageCoop.Client.Scripting
internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); } internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); } internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); } internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal static void InvokePlayerDied() { OnPlayerDied?.Invoke(); } internal static void InvokePlayerDied(string m) { OnPlayerDied?.Invoke(null, m); }
internal static void InvokeTick() { OnTick?.Invoke(); } internal static void InvokeTick() { OnTick?.Invoke(); }
internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); } internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); }
@ -146,8 +146,7 @@ namespace RageCoop.Client.Scripting
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType()); // Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
List<Action<CustomEventReceivedArgs>> handlers; if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
{ {
handlers.ForEach((x) => { x.Invoke(args); }); handlers.ForEach((x) => { x.Invoke(args); });
} }
@ -161,58 +160,84 @@ namespace RageCoop.Client.Scripting
/// Get the local player's ID /// Get the local player's ID
/// </summary> /// </summary>
/// <returns>PlayerID</returns> /// <returns>PlayerID</returns>
public static int LocalPlayerID public static int LocalPlayerID => Main.LocalPlayerID;
{
get { return Main.LocalPlayerID; } /// <summary>
} /// Check if player is connected to a server
/// </summary>
public static bool IsOnServer => Networking.IsOnServer;
/// <summary>
/// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server
/// </summary>
public static System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
/// <summary> /// <summary>
/// Check if a RAGECOOP menu is visible /// Check if a RAGECOOP menu is visible
/// </summary> /// </summary>
public static bool IsMenuVisible public static bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
{
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP chat is visible /// Check if the RAGECOOP chat is visible
/// </summary> /// </summary>
public static bool IsChatFocused public static bool IsChatFocused => Main.MainChat.Focused;
{
get { return Main.MainChat.Focused; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP list of players is visible /// Check if the RAGECOOP list of players is visible
/// </summary> /// </summary>
public static bool IsPlayerListVisible public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
{
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
}
/// <summary> /// <summary>
/// Get the version of RAGECOOP /// Get the version of RAGECOOP
/// </summary> /// </summary>
public static string CurrentVersion public static Version CurrentVersion => Main.Version;
{
get { return Main.CurrentVersion; }
}
/// <summary> /// <summary>
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using. /// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Logger Logger public static Logger Logger => Main.Logger;
{ /// <summary>
get /// Get all players indexed by their ID
{ /// </summary>
return Main.Logger; public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
}
}
#endregion #endregion
#region FUNCTIONS #region FUNCTIONS
/// <summary>
/// Connect to a server
/// </summary>
/// <param name="address">Address of the server, e.g. 127.0.0.1:4499</param>
/// <exception cref="InvalidOperationException">When a connection is active or being established</exception>
public static void Connect(string address)
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
throw new InvalidOperationException("Cannot connect to server when another connection is active");
}
Networking.ToggleConnection(address);
}
/// <summary>
/// Disconnect from current server or cancel the connection attempt.
/// </summary>
public static void Disconnect()
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
Networking.ToggleConnection(null);
}
}
/// <summary>
/// List all servers from master server address
/// </summary>
/// <returns></returns>
public static List<ServerInfo> ListServers()
{
return JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Main.Settings.MasterServer));
}
/// <summary> /// <summary>
/// Send a local chat message to this player /// Send a local chat message to this player
/// </summary> /// </summary>
@ -223,6 +248,15 @@ namespace RageCoop.Client.Scripting
Main.MainChat.AddMessage(from, message); Main.MainChat.AddMessage(from, message);
} }
/// <summary>
/// Send a chat message or command to server/other players
/// </summary>
/// <param name="message"></param>
public static void SendChatMessage(string message)
{
Networking.SendChatMessage(message);
}
/// <summary> /// <summary>
/// Queue an action to be executed on next tick. /// Queue an action to be executed on next tick.
/// </summary> /// </summary>
@ -231,12 +265,14 @@ namespace RageCoop.Client.Scripting
{ {
Main.QueueAction(a); Main.QueueAction(a);
} }
/// <summary> /// <summary>
/// Disconnect from the server /// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
/// </summary> /// </summary>
public static void Disconnect() /// <param name="a"> An <see cref="Func{T, TResult}"/> to be executed with a return value indicating whether it can be removed after execution.</param>
public static void QueueAction(Func<bool> a)
{ {
Networking.ToggleConnection(null); Main.QueueAction(a);
} }
/// <summary> /// <summary>
@ -246,12 +282,12 @@ namespace RageCoop.Client.Scripting
/// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param> /// <param name="args">The objects conataing your data, see <see cref="CustomEventReceivedArgs"/> for a list of supported types</param>
public static void SendCustomEvent(int eventHash, params object[] args) public static void SendCustomEvent(int eventHash, params object[] args)
{ {
var p = new Packets.CustomEvent()
Networking.Peer.SendTo(new Packets.CustomEvent()
{ {
Args = args, Args = args,
Hash = eventHash Hash = eventHash
}; }, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
Networking.Send(p, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
} }
/// <summary> /// <summary>
@ -270,6 +306,34 @@ namespace RageCoop.Client.Scripting
handlers.Add(handler); handlers.Add(handler);
} }
} }
/// <summary>
///
/// </summary>
/// <returns></returns>
public static void RequestSharedFile(string name, Action<string> callback)
{
EventHandler<string> handler = (s, e) =>
{
if (e.EndsWith(name))
{
callback(e);
}
};
DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{
Name = name,
},
(p) =>
{
if (p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: " + name);
}
});
}
#endregion #endregion
} }
} }

View File

@ -1,13 +1,11 @@
using System; using GTA;
using System.Collections.Generic;
using GTA.Native;
using GTA.Math; using GTA.Math;
using GTA; using GTA.Native;
using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System.Linq; using System;
using System.Threading.Tasks; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace RageCoop.Client.Scripting namespace RageCoop.Client.Scripting
{ {
@ -18,6 +16,7 @@ namespace RageCoop.Client.Scripting
{ {
API.Events.OnPedDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); }; API.Events.OnPedDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); };
API.Events.OnVehicleDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); }; API.Events.OnVehicleDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); };
API.Events.OnPlayerDied += (s, m) => { API.SendCustomEvent(CustomEvents.OnPlayerDied, m); };
API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn); API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn);
API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag); API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag);
@ -32,6 +31,7 @@ namespace RageCoop.Client.Scripting
API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip); API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip);
API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost = (bool)e.Args[0]; }); API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost = (bool)e.Args[0]; });
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync); API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync);
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show((string)e.Args[0]); });
Task.Run(() => Task.Run(() =>
{ {
while (true) while (true)
@ -46,7 +46,7 @@ namespace RageCoop.Client.Scripting
int weather1 = default(int); int weather1 = default(int);
int weather2 = default(int); int weather2 = default(int);
float percent2 = default(float); float percent2 = default(float);
Function.Call(Hash._GET_WEATHER_TYPE_TRANSITION, &weather1, &weather2, &percent2); Function.Call(Hash.GET_CURR_WEATHER_STATE, &weather1, &weather2, &percent2);
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2); API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2);
} }
}); });
@ -60,7 +60,7 @@ namespace RageCoop.Client.Scripting
private void WeatherTimeSync(CustomEventReceivedArgs e) private void WeatherTimeSync(CustomEventReceivedArgs e)
{ {
World.CurrentTimeOfDay = new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]); World.CurrentTimeOfDay = new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]);
Function.Call(Hash._SET_WEATHER_TYPE_TRANSITION, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]); Function.Call(Hash.SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
} }
private void SetDisplayNameTag(CustomEventReceivedArgs e) private void SetDisplayNameTag(CustomEventReceivedArgs e)
@ -127,8 +127,7 @@ namespace RageCoop.Client.Scripting
var pos = (Vector3)obj.Args[4]; var pos = (Vector3)obj.Args[4];
int rot = (int)obj.Args[5]; int rot = (int)obj.Args[5];
var name = (string)obj.Args[6]; var name = (string)obj.Args[6];
Blip blip; if (!EntityPool.ServerBlips.TryGetValue(id, out Blip blip))
if (!EntityPool.ServerBlips.TryGetValue(id, out blip))
{ {
EntityPool.ServerBlips.Add(id, blip = World.CreateBlip(pos)); EntityPool.ServerBlips.Add(id, blip = World.CreateBlip(pos));
} }
@ -183,7 +182,7 @@ namespace RageCoop.Client.Scripting
} }
} }
prop.LastSynced = Main.Ticked + 1; prop.LastSynced = Main.Ticked + 1;
prop.ModelHash= (Model)e.Args[1]; prop.Model = (Model)e.Args[1];
prop.Position = (Vector3)e.Args[2]; prop.Position = (Vector3)e.Args[2];
prop.Rotation = (Vector3)e.Args[3]; prop.Rotation = (Vector3)e.Args[3];
prop.Update(); prop.Update();

View File

@ -27,5 +27,10 @@ namespace RageCoop.Client.Scripting
/// </summary> /// </summary>
public ClientResource CurrentResource { get; internal set; } public ClientResource CurrentResource { get; internal set; }
/// <summary>
/// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/>
/// </summary>
public Core.Logger Logger => CurrentResource.Logger;
} }
} }

View File

@ -1,11 +1,11 @@
using System.IO; using ICSharpCode.SharpZipLib.Zip;
using RageCoop.Core.Scripting;
using RageCoop.Core; using RageCoop.Core;
using ICSharpCode.SharpZipLib.Zip; using RageCoop.Core.Scripting;
using System; using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace RageCoop.Client.Scripting namespace RageCoop.Client.Scripting
{ {
@ -30,11 +30,19 @@ namespace RageCoop.Client.Scripting
/// Get the <see cref="ResourceFile"/> where this script is loaded from. /// Get the <see cref="ResourceFile"/> where this script is loaded from.
/// </summary> /// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>(); public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// A <see cref="Core.Logger"/> instance that can be used to debug your resource.
/// </summary>
public Logger Logger { get; internal set; }
} }
internal class Resources internal class Resources
{ {
public Resources(){ private readonly List<ClientResource> LoadedResources = new List<ClientResource>();
BaseScriptType = "RageCoop.Client.Scripting.ClientScript"; private const string BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
private Logger Logger { get; set; }
public Resources()
{
Logger = Main.Logger; Logger = Main.Logger;
} }
private void StartAll() private void StartAll()
@ -47,6 +55,7 @@ namespace RageCoop.Client.Scripting
{ {
try try
{ {
s.CurrentResource = d;
s.OnStart(); s.OnStart();
} }
catch (Exception ex) catch (Exception ex)
@ -81,6 +90,7 @@ namespace RageCoop.Client.Scripting
} }
public void Load(string path, string[] zips) public void Load(string path, string[] zips)
{ {
LoadedResources.Clear();
foreach (var zip in zips) foreach (var zip in zips)
{ {
var zipPath = Path.Combine(path, zip); var zipPath = Path.Combine(path, zip);
@ -98,21 +108,13 @@ namespace RageCoop.Client.Scripting
} }
LoadedResources.Clear(); LoadedResources.Clear();
} }
private List<string> ToIgnore = new List<string>
{
"RageCoop.Client.dll",
"RageCoop.Core.dll",
"RageCoop.Server.dll",
"ScriptHookVDotNet3.dll"
};
private List<ClientResource> LoadedResources = new List<ClientResource>();
private string BaseScriptType;
public Logger Logger { get; set; }
private void LoadResource(ZipFile file, string dataFolderRoot) private void LoadResource(ZipFile file, string dataFolderRoot)
{ {
List<Action> toLoad = new List<Action>(10);
var r = new ClientResource() var r = new ClientResource()
{ {
Logger = Main.Logger,
Scripts = new List<ClientScript>(), Scripts = new List<ClientScript>(),
Name = Path.GetFileNameWithoutExtension(file.Name), Name = Path.GetFileNameWithoutExtension(file.Name),
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name)) DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
@ -130,53 +132,30 @@ namespace RageCoop.Client.Scripting
if (!entry.IsDirectory) if (!entry.IsDirectory)
{ {
rFile.GetStream = () => { return file.GetInputStream(entry); }; rFile.GetStream = () => { return file.GetInputStream(entry); };
if (entry.Name.EndsWith(".dll")) if (entry.Name.EndsWith(".dll") && !entry.Name.Contains("/"))
{ {
// Don't load API assembly
if (Path.GetFileName(entry.Name).CanBeIgnored()) { continue; }
var tmp = Path.GetTempFileName(); var tmp = Path.GetTempFileName();
var f = File.OpenWrite(tmp); var f = File.OpenWrite(tmp);
rFile.GetStream().CopyTo(f); rFile.GetStream().CopyTo(f);
f.Close(); f.Close();
LoadScriptsFromAssembly(rFile, tmp, r, false); if (!IsManagedAssembly(tmp))
{
continue;
}
var asm = Assembly.LoadFrom(tmp);
toLoad.Add(() => LoadScriptsFromAssembly(rFile, asm, entry.Name, r));
} }
} }
} }
foreach (var a in toLoad)
{
a();
}
LoadedResources.Add(r); LoadedResources.Add(r);
file.Close(); file.Close();
} }
private bool LoadScriptsFromAssembly(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true)
{
lock (LoadedResources)
{
if (!IsManagedAssembly(path)) { return false; }
if (ToIgnore.Contains(file.Name)) { try { File.Delete(path); } catch { }; return false; }
Logger?.Debug($"Loading assembly {file.Name} ...");
Assembly assembly;
try
{
if (shadowCopy)
{
var temp = Path.GetTempFileName();
File.Copy(path, temp, true);
assembly = Assembly.LoadFrom(temp);
}
else
{
assembly = Assembly.LoadFrom(path);
}
}
catch (Exception ex)
{
Logger?.Error("Unable to load "+file.Name);
Logger?.Error(ex);
return false;
}
return LoadScriptsFromAssembly(file, assembly, path, resource);
}
}
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly, string filename, ClientResource toload) private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly, string filename, ClientResource toload)
{ {
int count = 0; int count = 0;

View File

@ -1,18 +1,13 @@
using System; using RageCoop.Core;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.IO; using System.IO;
using RageCoop.Core; using System.Security.Cryptography;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class Security internal class Security
{ {
public RSA ServerRSA { get; set; } public RSA ServerRSA { get; set; }
public Aes ClientAes { get; set; } = Aes.Create(); public Aes ClientAes { get; set; } = Aes.Create();
private Logger Logger; private readonly Logger Logger;
public Security(Logger logger) public Security(Logger logger)
{ {
Logger = logger; Logger = logger;
@ -29,6 +24,10 @@ namespace RageCoop.Client
{ {
return new CryptoStream(new MemoryStream(data), ClientAes.CreateEncryptor(), CryptoStreamMode.Read).ReadToEnd(); return new CryptoStream(new MemoryStream(data), ClientAes.CreateEncryptor(), CryptoStreamMode.Read).ReadToEnd();
} }
public byte[] Decrypt(byte[] data)
{
return new CryptoStream(new MemoryStream(data), ClientAes.CreateDecryptor(), CryptoStreamMode.Read).ReadToEnd();
}
public void SetServerPublicKey(byte[] modulus, byte[] exponent) public void SetServerPublicKey(byte[] modulus, byte[] exponent)
{ {
var para = new RSAParameters(); var para = new RSAParameters();

View File

@ -22,17 +22,21 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
/// </summary> /// </summary>
public string MasterServer { get; set; } = "https://masterserver.ragecoop.online/"; public string MasterServer { get; set; } = "https://masterserver.ragecoop.com/";
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
/// </summary> /// </summary>
public bool FlipMenu { get; set; } = false; public bool FlipMenu { get; set; } = false;
/// <summary>
/// Don't use it!
/// </summary>
public bool Voice { get; set; } = false;
/// <summary> /// <summary>
/// LogLevel for RageCoop. /// LogLevel for RageCoop.
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error /// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary> /// </summary>
public int LogLevel = 2; public int LogLevel = 00;
/// <summary> /// <summary>
/// The key to open menu /// The key to open menu
@ -47,7 +51,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Disable world NPC traffic, mission entities won't be affected /// Disable world NPC traffic, mission entities won't be affected
/// </summary> /// </summary>
public bool DisableTraffic { get; set; } = true; public bool DisableTraffic { get; set; } = false;
/// <summary> /// <summary>
/// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed. /// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed.
@ -57,15 +61,31 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended). /// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary> /// </summary>
public int WorldVehicleSoftLimit { get; set; } = 35; public int WorldVehicleSoftLimit { get; set; } = 20;
/// <summary> /// <summary>
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended). /// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary> /// </summary>
public int WorldPedSoftLimit { get; set; } = 50; public int WorldPedSoftLimit { get; set; } = 30;
/// <summary> /// <summary>
/// The directory where log and resources downloaded from server will be placed. /// The directory where log and resources downloaded from server will be placed.
/// </summary> /// </summary>
public string DataDirectory { get; set; } = "Scripts\\RageCoop\\Data"; public string DataDirectory { get; set; } = "Scripts\\RageCoop\\Data";
/// <summary>
/// Show the owner name of the entity you're aiming at
/// </summary>
public bool ShowEntityOwnerName { get; set; } = false;
/// <summary>
/// Show other player's nametag on your screen
/// </summary>
public bool ShowPlayerNameTag { get; set; } = true;
/// <summary>
/// Show other player's blip on map
/// </summary>
public bool ShowPlayerBlip { get; set; } = true;
} }
} }

View File

@ -0,0 +1,161 @@
using GTA;
using GTA.Native;
namespace RageCoop.Client
{
public partial class SyncedPed
{
private void DisplaySpeaking(bool speaking)
{
if (!MainPed.IsHuman)
return;
if (speaking)
{
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mic_chatter", "mp_facial");
return;
}
switch (MainPed.Gender)
{
case Gender.Male:
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@gen_male@variations@normal");
break;
case Gender.Female:
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@gen_female@variations@normal");
break;
default:
Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@mime@variations@normal");
break;
}
}
private void DisplayInCover()
{
var ourAnim = GetCoverAnim();
var animDict = GetCoverIdleAnimDict();
if (ourAnim != null && animDict != null)
{
var flag = AnimationFlags.Loop;
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed, animDict, ourAnim, 3))
{
MainPed.Task.ClearAll();
Function.Call(Hash.TASK_PLAY_ANIM, MainPed, LoadAnim(animDict), ourAnim, 8f, 10f, -1, flag, -8f, 1, 1, 1);
}
}
}
internal string GetCoverAnim()
{
if (IsInCover)
{
if (IsBlindFiring)
{
if (IsInCover)
return IsInCoverFacingLeft ? "blindfire_low_l_aim_med" : "blindfire_low_r_aim_med";
return IsInCoverFacingLeft ? "blindfire_hi_l_aim_med" : "blindfire_hi_r_aim_med";
}
return IsInCoverFacingLeft ? "idle_l_corner" : "idle_r_corner";
}
return null;
}
internal string GetCoverIdleAnimDict()
{
if (!IsInCover) return "";
var altitude = IsInLowCover ? "low" : "high";
var hands = GetWeaponHandsHeld(CurrentWeaponHash);
if (IsBlindFiring)
{
if (hands == 1) return "cover@weapon@1h";
if (hands == 2 || hands == 5) return "cover@weapon@2h";
}
if (hands == 1) return "cover@idles@1h@" + altitude + "@_a";
if (hands == 2 || hands == 5) return "cover@idles@2h@" + altitude + "@_a";
if (hands == 3 || hands == 4 || hands == 0) return "cover@idles@unarmed@" + altitude + "@_a";
return "";
}
internal int GetWeaponHandsHeld(uint weapon)
{
switch (weapon)
{
case unchecked((uint)WeaponHash.Unarmed):
return 0;
case unchecked((uint)WeaponHash.RPG):
case unchecked((uint)WeaponHash.HomingLauncher):
case unchecked((uint)WeaponHash.Firework):
return 5;
case unchecked((uint)WeaponHash.Minigun):
return 5;
case unchecked((uint)WeaponHash.GolfClub):
case unchecked((uint)WeaponHash.PoolCue):
case unchecked((uint)WeaponHash.Bat):
return 4;
case unchecked((uint)WeaponHash.Knife):
case unchecked((uint)WeaponHash.Nightstick):
case unchecked((uint)WeaponHash.Hammer):
case unchecked((uint)WeaponHash.Crowbar):
case unchecked((uint)WeaponHash.Wrench):
case unchecked((uint)WeaponHash.BattleAxe):
case unchecked((uint)WeaponHash.Dagger):
case unchecked((uint)WeaponHash.Hatchet):
case unchecked((uint)WeaponHash.KnuckleDuster):
case unchecked((uint)-581044007):
case unchecked((uint)-102323637):
case unchecked((uint)-538741184):
return 3;
case unchecked((uint)-1357824103):
case unchecked((uint)-1074790547):
case unchecked(2132975508):
case unchecked((uint)-2084633992):
case unchecked((uint)-952879014):
case unchecked(100416529):
case unchecked((uint)WeaponHash.Gusenberg):
case unchecked((uint)WeaponHash.MG):
case unchecked((uint)WeaponHash.CombatMG):
case unchecked((uint)WeaponHash.CombatPDW):
case unchecked((uint)WeaponHash.AssaultSMG):
case unchecked((uint)WeaponHash.SMG):
case unchecked((uint)WeaponHash.HeavySniper):
case unchecked((uint)WeaponHash.PumpShotgun):
case unchecked((uint)WeaponHash.HeavyShotgun):
case unchecked((uint)WeaponHash.Musket):
case unchecked((uint)WeaponHash.AssaultShotgun):
case unchecked((uint)WeaponHash.BullpupShotgun):
case unchecked((uint)WeaponHash.SawnOffShotgun):
case unchecked((uint)WeaponHash.SweeperShotgun):
case unchecked((uint)WeaponHash.CompactRifle):
return 2;
}
return 1;
}
private string LoadAnim(string anim)
{
ulong startTime = Util.GetTickCount64();
while (!Function.Call<bool>(Hash.HAS_ANIM_DICT_LOADED, anim))
{
Script.Yield();
Function.Call(Hash.REQUEST_ANIM_DICT, anim);
if (Util.GetTickCount64() - startTime >= 1000)
{
break;
}
}
return anim;
}
}
}

View File

@ -0,0 +1,81 @@
using GTA;
using GTA.Math;
using RageCoop.Core;
using System.Collections.Generic;
namespace RageCoop.Client
{
/// <summary>
/// ?
/// </summary>
public partial class SyncedPed : SyncedEntity
{
internal Blip PedBlip = null;
internal BlipColor BlipColor = (BlipColor)255;
internal BlipSprite BlipSprite = 0;
internal float BlipScale = 1;
internal int VehicleID
{
get => CurrentVehicle?.ID ?? 0;
set
{
if (CurrentVehicle == null || value != CurrentVehicle?.ID)
{
CurrentVehicle = EntityPool.GetVehicleByID(value);
}
}
}
internal SyncedVehicle CurrentVehicle { get; private set; }
internal VehicleSeat Seat;
public bool IsPlayer { get => OwnerID == ID && ID != 0; }
public Ped MainPed { get; internal set; }
internal int Health { get; set; }
internal Vector3 HeadPosition { get; set; }
internal Vector3 RightFootPosition { get; set; }
internal Vector3 LeftFootPosition { get; set; }
internal byte WeaponTint { get; set; }
private bool _lastRagdoll = false;
private ulong _lastRagdollTime = 0;
private bool _lastInCover = false;
private byte[] _lastClothes = null;
internal byte[] Clothes { get; set; }
internal float Heading { get; set; }
internal ulong LastSpeakingTime { get; set; } = 0;
internal bool IsSpeaking { get; set; } = false;
public byte Speed { get; set; }
private bool _lastIsJumping = false;
internal PedDataFlags Flags;
internal bool IsAiming => Flags.HasPedFlag(PedDataFlags.IsAiming);
internal bool _lastDriveBy;
internal bool IsReloading => Flags.HasPedFlag(PedDataFlags.IsReloading);
internal bool IsJumping => Flags.HasPedFlag(PedDataFlags.IsJumping);
internal bool IsRagdoll => Flags.HasPedFlag(PedDataFlags.IsRagdoll);
internal bool IsOnFire => Flags.HasPedFlag(PedDataFlags.IsOnFire);
internal bool IsInParachuteFreeFall => Flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
internal bool IsParachuteOpen => Flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
internal bool IsOnLadder => Flags.HasPedFlag(PedDataFlags.IsOnLadder);
internal bool IsVaulting => Flags.HasPedFlag(PedDataFlags.IsVaulting);
internal bool IsInCover => Flags.HasPedFlag(PedDataFlags.IsInCover);
internal bool IsInLowCover => Flags.HasPedFlag(PedDataFlags.IsInLowCover);
internal bool IsInCoverFacingLeft => Flags.HasPedFlag(PedDataFlags.IsInCoverFacingLeft);
internal bool IsBlindFiring => Flags.HasPedFlag(PedDataFlags.IsBlindFiring);
internal bool IsInStealthMode => Flags.HasPedFlag(PedDataFlags.IsInStealthMode);
internal Prop ParachuteProp { get; set; } = null;
internal uint CurrentWeaponHash { get; set; }
private Dictionary<uint, bool> _lastWeaponComponents = null;
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
private Entity _weaponObj;
internal Vector3 AimCoords { get; set; }
private readonly string[] _currentAnimation = new string[2] { "", "" };
private bool LastMoving;
}
}

View File

@ -1,22 +1,20 @@
using System; using GTA;
using System.Linq;
using System.Drawing;
using System.Collections.Generic;
using RageCoop.Core;
using GTA;
using GTA.Native;
using GTA.Math; using GTA.Math;
using GTA.Native;
using LemonUI.Elements; using LemonUI.Elements;
using System.Security.Cryptography; using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace RageCoop.Client namespace RageCoop.Client
{ {
/// <summary> /// <summary>
/// ? /// ?
/// </summary> /// </summary>
public class SyncedPed:SyncedEntity public partial class SyncedPed : SyncedEntity
{ {
#region CONSTRUCTORS
/// <summary> /// <summary>
/// Create a local entity (outgoing sync) /// Create a local entity (outgoing sync)
@ -30,7 +28,7 @@ namespace RageCoop.Client
MainPed = p; MainPed = p;
OwnerID = Main.LocalPlayerID; OwnerID = Main.LocalPlayerID;
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false); //Function.Call(Hash.SET_PED_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
// MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true); // MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
@ -44,50 +42,12 @@ namespace RageCoop.Client
ID = id; ID = id;
LastSynced = Main.Ticked; LastSynced = Main.Ticked;
} }
#endregion
#region PLAYER -- ONLY
internal Blip PedBlip = null;
internal BlipColor BlipColor = (BlipColor)255;
internal BlipSprite BlipSprite = (BlipSprite)0;
internal float BlipScale=1;
internal PlayerData Player;
#endregion
/// <summary>
/// Indicates whether this ped is a player
/// </summary>
public bool IsPlayer { get { return (OwnerID==ID)&&(ID!=0); } }
/// <summary>
/// real entity
/// </summary>
public Ped MainPed { get; internal set; }
internal int Health { get; set; }
internal bool IsInStealthMode { get; set; }
internal byte WeaponTint { get; set; }
internal bool _lastEnteringVehicle=false;
internal bool _lastSittingInVehicle=false;
private bool _lastRagdoll=false;
private ulong _lastRagdollTime=0;
private bool _lastInCover = false;
private byte[] _lastClothes = null;
internal byte[] Clothes { get; set; }
internal float Heading { get; set; }
internal Vector3 RotationVelocity { get; set; }
internal Vector3 AimCoords { get; set; }
private WeaponAsset WeaponAsset { get; set; }
internal override void Update() internal override void Update()
{ {
if (Owner == null) { OwnerID = OwnerID; return; }
if (IsPlayer) if (IsPlayer)
{ {
if (Player==null)
{
Player = PlayerList.GetPlayer(this);
return;
}
RenderNameTag(); RenderNameTag();
} }
@ -95,75 +55,66 @@ namespace RageCoop.Client
if (!IsReady) { return; } if (!IsReady) { return; }
// Skip update if no new sync message has arrived. // Skip update if no new sync message has arrived.
if (!NeedUpdate) if (!NeedUpdate) { return; }
if (MainPed == null || !MainPed.Exists())
{
if (!CreateCharacter())
{ {
return; return;
} }
bool characterExist = (MainPed != null) && MainPed.Exists();
if (!characterExist)
{
CreateCharacter();
return;
} }
// Need to update state
if (LastFullSynced >= LastUpdated)
{
if (MainPed != null && (Model != MainPed.Model.Hash))
{
if (!CreateCharacter())
{
return;
}
}
if (((byte)BlipColor==255) && (PedBlip!=null)) if (!Main.Settings.ShowPlayerBlip && (byte)BlipColor != 255) BlipColor = (BlipColor)255;
if ((byte)BlipColor == 255 && PedBlip != null)
{ {
PedBlip.Delete(); PedBlip.Delete();
PedBlip = null; PedBlip = null;
} }
else if (((byte)BlipColor != 255) && PedBlip==null) else if ((byte)BlipColor != 255 && PedBlip == null)
{ {
PedBlip = MainPed.AddBlip(); PedBlip = MainPed.AddBlip();
if (IsPlayer)
{
Main.Logger.Debug("blip:"+Player.Username);
PedBlip.Name=Player.Username;
}
PedBlip.Color = BlipColor; PedBlip.Color = BlipColor;
PedBlip.Sprite = BlipSprite; PedBlip.Sprite = BlipSprite;
PedBlip.Scale = BlipScale; PedBlip.Scale = BlipScale;
} }
if (PedBlip != null)
// Need to update state
if (LastStateSynced>=LastUpdated)
{ {
if (PedBlip.Color != BlipColor)
if (MainPed!=null&& (ModelHash != MainPed.Model.Hash))
{ {
CreateCharacter(); PedBlip.Color = BlipColor;
return; }
if (PedBlip.Sprite != BlipSprite)
{
PedBlip.Sprite = BlipSprite;
}
if (IsPlayer)
{
PedBlip.Name = Owner.Username;
}
} }
if (!Clothes.SequenceEqual(_lastClothes)) if (!Clothes.SequenceEqual(_lastClothes))
{ {
SetClothes(); SetClothes();
} }
var b = MainPed.AttachedBlip;
if (b==null || b.Color!=BlipColor || b.Sprite!=BlipSprite)
{
PedBlip?.Delete();
PedBlip=MainPed.AddBlip();
PedBlip.Color=BlipColor;
PedBlip.Sprite =BlipSprite;
if (IsPlayer)
{
Main.Logger.Debug("blip:"+Player.Username);
b.Name=Player.Username;
}
}
CheckCurrentWeapon(); CheckCurrentWeapon();
} }
if (MainPed.IsDead) if (MainPed.IsDead)
{ {
if (Health > 0) if (Health > 0)
@ -190,50 +141,68 @@ namespace RageCoop.Client
} }
} }
if (MainPed.IsInVehicle()||MainPed.IsGettingIntoVehicle) if (!IsPlayer && Health <= 0 && !MainPed.IsDead)
{
MainPed.Kill();
return;
}
if (Speed >= 4)
{ {
DisplayInVehicle(); DisplayInVehicle();
} }
else else
{ {
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); return; }
DisplayOnFoot(); DisplayOnFoot();
} }
LastUpdated=Main.Ticked;
return; if (IsSpeaking)
{
if (Main.Ticked - LastSpeakingTime < 10)
{
DisplaySpeaking(true);
}
else
{
DisplaySpeaking(false);
IsSpeaking = false;
LastSpeakingTime = 0;
}
} }
LastUpdated = Main.Ticked;
}
private void RenderNameTag() private void RenderNameTag()
{ {
if (!Player.DisplayNameTag || (MainPed==null) || !MainPed.IsVisible || !MainPed.IsInRange(Game.Player.Character.Position, 20f)) if (!Owner.DisplayNameTag || !Main.Settings.ShowPlayerNameTag || MainPed == null || !MainPed.IsVisible || !MainPed.IsInRange(Main.PlayerPosition, 40f))
{ {
return; return;
} }
string renderText = IsOutOfSync ? "~r~AFK" : Player.Username; Vector3 targetPos = MainPed.Bones[Bone.IKHead].Position;
Vector3 targetPos = MainPed.Bones[Bone.IKHead].Position + new Vector3(0, 0, 0.35f); Point toDraw = default;
if (Util.WorldToScreen(targetPos, ref toDraw))
Function.Call(Hash.SET_DRAW_ORIGIN, targetPos.X, targetPos.Y, targetPos.Z, 0); {
toDraw.Y -= 100;
float dist = (GameplayCamera.Position - MainPed.Position).Length(); new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon)
var sizeOffset = Math.Max(1f - (dist / 30f), 0.3f);
new ScaledText(new PointF(0, 0), renderText, 0.4f * sizeOffset, GTA.UI.Font.ChaletLondon)
{ {
Outline = true, Outline = true,
Alignment = GTA.UI.Alignment.Center Alignment = GTA.UI.Alignment.Center,
Color = Owner.HasDirectConnection ? Color.FromArgb(179, 229, 252) : Color.White,
}.Draw(); }.Draw();
}
Function.Call(Hash.CLEAR_DRAW_ORIGIN);
} }
private void CreateCharacter() private bool CreateCharacter()
{ {
if (MainPed != null) if (MainPed != null)
{ {
if (MainPed.Exists()) if (MainPed.Exists())
{ {
Main.Logger.Debug($"Removing ped {ID}. Reason:CreateCharacter"); // Main.Logger.Debug($"Removing ped {ID}. Reason:CreateCharacter");
MainPed.Kill(); MainPed.Kill();
MainPed.MarkAsNoLongerNeeded(); MainPed.MarkAsNoLongerNeeded();
MainPed.Delete(); MainPed.Delete();
@ -242,26 +211,23 @@ namespace RageCoop.Client
MainPed = null; MainPed = null;
} }
if (PedBlip != null && PedBlip.Exists()) if (PedBlip != null)
{ {
PedBlip.Delete(); PedBlip.Delete();
PedBlip = null; PedBlip = null;
} }
if (!Model.IsLoaded)
Model characterModel = ModelHash.ModelRequest();
if (characterModel == null)
{ {
return; Model.Request();
return false;
} }
MainPed = World.CreatePed(characterModel, Position); if ((MainPed = Util.CreatePed(Model, Position)) == null)
characterModel.MarkAsNoLongerNeeded();
if (MainPed == null)
{ {
return; return false;
} }
Model.MarkAsNoLongerNeeded();
MainPed.BlockPermanentEvents = true; MainPed.BlockPermanentEvents = true;
MainPed.CanWrithe = false; MainPed.CanWrithe = false;
@ -276,29 +242,34 @@ namespace RageCoop.Client
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true); Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false); Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true); Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false); Function.Call(Hash.SET_PED_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false); Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableExplosionReactions, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableExplosionReactions, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_AvoidTearGas, false); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_AvoidTearGas, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_IgnoreBeingOnFire, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableEvasiveDives, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisablePanicInVehicle, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_BlockNonTemporaryEvents, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableShockingEvents, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableShockingEvents, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
SetClothes(); SetClothes();
if (IsPlayer) if (IsPlayer) { MainPed.IsInvincible = true; }
{
MainPed.IsInvincible=true;
}
if (IsInvincible) { MainPed.IsInvincible = true; } if (IsInvincible) { MainPed.IsInvincible = true; }
lock (EntityPool.PedsLock)
{
// Add to EntityPool so this Character can be accessed by handle. // Add to EntityPool so this Character can be accessed by handle.
EntityPool.Add(this); EntityPool.Add(this);
} }
return true;
}
private void SetClothes() private void SetClothes()
{ {
for (byte i = 0; i < 12; i++) for (byte i = 0; i < 12; i++)
@ -307,37 +278,14 @@ namespace RageCoop.Client
} }
_lastClothes = Clothes; _lastClothes = Clothes;
} }
#region ONFOOT
#region -- VARIABLES --
/// <summary>
/// The latest character rotation (may not have been applied yet)
/// </summary>
public byte Speed { get; set; }
private bool _lastIsJumping = false;
internal bool IsJumping { get; set; }
internal bool IsOnLadder { get; set; }
internal bool IsVaulting { get; set; }
internal bool IsInParachuteFreeFall { get; set; }
internal bool IsParachuteOpen { get; set; }
internal Prop ParachuteProp { get; set; } = null;
internal bool IsRagdoll { get; set; }
internal bool IsOnFire { get; set; }
internal bool IsAiming { get; set; }
internal bool IsReloading { get; set; }
internal bool IsInCover { get; set; }
internal uint CurrentWeaponHash { get; set; }
private Dictionary<uint, bool> _lastWeaponComponents = null;
internal Dictionary<uint, bool> WeaponComponents { get; set; } = null;
private int _lastWeaponObj = 0;
#endregion
private string[] _currentAnimation = new string[2] { "", "" };
private void DisplayOnFoot() private void DisplayOnFoot()
{ {
if (IsInParachuteFreeFall) if (IsInParachuteFreeFall)
{ {
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.Position, Position + Velocity, 0.5f); MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
MainPed.Quaternion = Rotation.ToQuaternion(); MainPed.Quaternion = Rotation.ToQuaternion();
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@base", "free_idle", 3)) if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@base", "free_idle", 3))
@ -351,10 +299,11 @@ namespace RageCoop.Client
{ {
if (ParachuteProp == null) if (ParachuteProp == null)
{ {
Model model = 1740193300.ModelRequest(); Model model = 1740193300;
model.Request(1000);
if (model != null) if (model != null)
{ {
ParachuteProp = World.CreateProp(model, MainPed.Position, MainPed.Rotation, false, false); ParachuteProp = World.CreateProp(model, MainPed.ReadPosition(), MainPed.ReadRotation(), false, false);
model.MarkAsNoLongerNeeded(); model.MarkAsNoLongerNeeded();
ParachuteProp.IsPositionFrozen = true; ParachuteProp.IsPositionFrozen = true;
ParachuteProp.IsCollisionEnabled = false; ParachuteProp.IsCollisionEnabled = false;
@ -365,7 +314,7 @@ namespace RageCoop.Client
MainPed.Task.ClearSecondary(); MainPed.Task.ClearSecondary();
} }
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.Position, Position + Velocity, 0.5f); MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
MainPed.Quaternion = Rotation.ToQuaternion(); MainPed.Quaternion = Rotation.ToQuaternion();
if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@parachute@first_person", "chute_idle_right", 3)) if (!Function.Call<bool>(Hash.IS_ENTITY_PLAYING_ANIM, MainPed.Handle, "skydive@parachute@first_person", "chute_idle_right", 3))
@ -433,7 +382,7 @@ namespace RageCoop.Client
SmoothTransition(); SmoothTransition();
return; return;
} }
if (!IsOnLadder && MainPed.IsTaskActive(TaskType.CTaskGoToAndClimbLadder)) else if (MainPed.IsTaskActive(TaskType.CTaskGoToAndClimbLadder))
{ {
MainPed.Task.ClearAllImmediately(); MainPed.Task.ClearAllImmediately();
_currentAnimation[1] = ""; _currentAnimation[1] = "";
@ -457,12 +406,11 @@ namespace RageCoop.Client
if (IsOnFire && !MainPed.IsOnFire) if (IsOnFire && !MainPed.IsOnFire)
{ {
Function.Call(Hash.START_ENTITY_FIRE, MainPed);
MainPed.SetOnFire(true);
} }
else if (!IsOnFire && MainPed.IsOnFire) else if (!IsOnFire && MainPed.IsOnFire)
{ {
MainPed.SetOnFire(false); Function.Call(Hash.STOP_ENTITY_FIRE, MainPed);
} }
if (IsJumping) if (IsJumping)
@ -478,29 +426,20 @@ namespace RageCoop.Client
} }
_lastIsJumping = false; _lastIsJumping = false;
if (IsRagdoll || Health==0) if (IsRagdoll || (IsPlayer && Health == 0))
{ {
if (!MainPed.IsRagdoll) if (!MainPed.IsRagdoll)
{ {
MainPed.Ragdoll(); MainPed.Ragdoll();
} }
SmoothTransition(); SmoothTransition();
if (!_lastRagdoll) if (!_lastRagdoll)
{ {
_lastRagdoll = true; _lastRagdoll = true;
_lastRagdollTime = Main.Ticked; _lastRagdollTime = Main.Ticked;
} }
/*
if((Main.Ticked-_lastRagdollTime>30)&&((Position.DistanceTo(MainPed.Position)>2)||MainPed.Velocity.Length()<3f))
{
MainPed.ApplyForce((Position-MainPed.Position)*0.2f, (RotationVelocity-MainPed.RotationVelocity)*0.1f);
}*/
return; return;
} }
else
{
if (MainPed.IsRagdoll) if (MainPed.IsRagdoll)
{ {
if (Speed == 0) if (Speed == 0)
@ -511,14 +450,9 @@ namespace RageCoop.Client
{ {
MainPed.Task.ClearAllImmediately(); MainPed.Task.ClearAllImmediately();
} }
_lastRagdoll = false;
return; return;
} }
else
{
_lastRagdoll = false;
}
}
if (IsReloading) if (IsReloading)
{ {
@ -542,8 +476,6 @@ namespace RageCoop.Client
} }
else if (IsInCover) else if (IsInCover)
{ {
if (!_lastInCover) if (!_lastInCover)
{ {
Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle); Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle);
@ -559,7 +491,6 @@ namespace RageCoop.Client
{ {
SmoothTransition(); SmoothTransition();
} }
return;
} }
else if (_lastInCover) else if (_lastInCover)
{ {
@ -580,34 +511,28 @@ namespace RageCoop.Client
} }
} }
#region WEAPON
private void CheckCurrentWeapon() private void CheckCurrentWeapon()
{ {
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents)) if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents) || (Speed <= 3 && _weaponObj?.IsVisible != true))
{ {
if (WeaponAsset!=null) { WeaponAsset.MarkAsNoLongerNeeded(); } new WeaponAsset(CurrentWeaponHash).Request();
WeaponAsset=new WeaponAsset(CurrentWeaponHash);
if (!WeaponAsset.IsLoaded) { WeaponAsset.Request(); }
MainPed.Weapons.RemoveAll();
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
MainPed.Weapons.RemoveAll();
_weaponObj = Entity.FromHandle(Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0));
if (_weaponObj == null) { return; }
if (CurrentWeaponHash != (uint)WeaponHash.Unarmed) if (CurrentWeaponHash != (uint)WeaponHash.Unarmed)
{ {
if (WeaponComponents != null && WeaponComponents.Count != 0) if (WeaponComponents != null && WeaponComponents.Count != 0)
{ {
foreach (KeyValuePair<uint, bool> comp in WeaponComponents) foreach (KeyValuePair<uint, bool> comp in WeaponComponents)
{ {
if (comp.Value) if (comp.Value)
{ {
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _lastWeaponObj, comp.Key); Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _weaponObj, comp.Key);
} }
} }
} }
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle); Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _weaponObj, MainPed.Handle);
} }
_lastWeaponComponents = WeaponComponents; _lastWeaponComponents = WeaponComponents;
} }
@ -632,13 +557,13 @@ namespace RageCoop.Client
} }
SmoothTransition(); SmoothTransition();
} }
#endregion
private bool LastMoving;
private void WalkTo() private void WalkTo()
{ {
Vector3 predictPosition = Position + (Position - MainPed.Position) + Velocity * 0.5f; MainPed.Task.ClearAll();
float range = predictPosition.DistanceToSquared(MainPed.Position); Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
Vector3 predictPosition = Predict(Position) + Velocity;
float range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
switch (Speed) switch (Speed)
{ {
@ -679,6 +604,8 @@ namespace RageCoop.Client
MainPed.Task.StandStill(2000); MainPed.Task.StandStill(2000);
LastMoving = false; LastMoving = false;
} }
if (MainPed.IsTaskActive(TaskType.CTaskDiveToGround)) MainPed.Task.ClearAll();
break; break;
} }
SmoothTransition(); SmoothTransition();
@ -687,52 +614,128 @@ namespace RageCoop.Client
private void SmoothTransition() private void SmoothTransition()
{ {
var localRagdoll = MainPed.IsRagdoll; var localRagdoll = MainPed.IsRagdoll;
var dist = Position.DistanceTo(MainPed.Position); var predicted = Predict(Position);
if (dist>3) var dist = predicted.DistanceTo(MainPed.ReadPosition());
if (IsOff(dist))
{ {
MainPed.PositionNoOffset=Position; MainPed.PositionNoOffset = predicted;
return; return;
} }
var f = dist*(Position+SyncParameters.PositioinPredictionDefault*Velocity-MainPed.Position)+(Velocity-MainPed.Velocity)*0.2f;
if (!localRagdoll) { f*=5; }
if (!(localRagdoll || MainPed.IsDead)) if (!(localRagdoll || MainPed.IsDead))
{ {
MainPed.Rotation=Rotation; if (!IsAiming && !MainPed.IsGettingUp)
if (MainPed.Speed<0.05) { f*=10; MainPed.Heading=Heading; } {
var cur = MainPed.Heading;
var diff = Heading - cur;
if (diff > 180) { diff -= 360; }
else if (diff < -180) { diff += 360; }
MainPed.Heading = cur + diff / 2;
}
MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
} }
else if (Main.Ticked - _lastRagdollTime < 10) else if (Main.Ticked - _lastRagdollTime < 10)
{ {
return; return;
} }
MainPed.ApplyForce(f); else if (IsRagdoll)
}
private string LoadAnim(string anim)
{ {
ulong startTime = Util.GetTickCount64(); var helper = new GTA.NaturalMotion.ApplyImpulseHelper(MainPed);
var head = MainPed.Bones[Bone.SkelHead];
var rightFoot = MainPed.Bones[Bone.SkelRightFoot];
var leftFoot = MainPed.Bones[Bone.SkelLeftFoot];
Vector3 amount;
// 20:head, 3:left foot, 6:right foot, 17:right hand,
while (!Function.Call<bool>(Hash.HAS_ANIM_DICT_LOADED, anim)) amount = 20 * (Predict(HeadPosition) - head.Position);
{ if (amount.Length() > 50) { amount = amount.Normalized * 50; }
Script.Yield(); helper.EqualizeAmount = 1;
Function.Call(Hash.REQUEST_ANIM_DICT, anim); helper.PartIndex = 20;
if (Util.GetTickCount64() - startTime >= 1000) helper.Impulse = amount;
{ helper.Start();
break; helper.Stop();
}
}
return anim; amount = 20 * (Predict(RightFootPosition) - rightFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex = 6;
helper.Impulse = amount;
helper.Start();
helper.Stop();
amount = 20 * (Predict(LeftFootPosition) - leftFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex = 3;
helper.Impulse = amount;
helper.Start();
helper.Stop();
}
else
{
// localRagdoll
var force = Velocity - MainPed.Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
if (force.Length() > 20) { force = force.Normalized * 20; }
MainPed.ApplyForce(force);
}
} }
#endregion
private void DisplayInVehicle() private void DisplayInVehicle()
{ {
if (CurrentVehicle?.MainVehicle == null) { return; }
switch (Speed)
{
case 4:
if (MainPed.CurrentVehicle != CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
{
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat);
}
if (MainPed.IsOnTurretSeat()) if (MainPed.IsOnTurretSeat())
{ {
Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100); // Function.Call(Hash.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z); Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
} }
if (MainPed.VehicleWeapon == VehicleWeaponHash.Invalid)
{
// World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue);
if (IsAiming)
{
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z);
if (!_lastDriveBy)
{
_lastDriveBy = true;
Function.Call(Hash.TASK_DRIVE_BY, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z, 1, 100, 1, FiringPattern.SingleShot);
}
}
else if (_lastDriveBy || MainPed.IsTaskActive(TaskType.CTaskAimGunVehicleDriveBy))
{
MainPed.Task.ClearAll();
_lastDriveBy = false;
}
}
else if (MainPed.VehicleWeapon != (VehicleWeaponHash)CurrentWeaponHash)
{
MainPed.VehicleWeapon = (VehicleWeaponHash)CurrentWeaponHash;
}
break;
case 5:
if (MainPed.VehicleTryingToEnter != CurrentVehicle.MainVehicle || MainPed.GetSeatTryingToEnter() != Seat)
{
MainPed.Task.EnterVehicle(CurrentVehicle.MainVehicle, Seat, -1, 5, EnterVehicleFlags.JackAnyone);
}
break;
case 6:
if (!MainPed.IsTaskActive(TaskType.CTaskExitVehicle))
{
MainPed.Task.LeaveVehicle(CurrentVehicle.Velocity.Length() > 5f ? LeaveVehicleFlags.BailOut : LeaveVehicleFlags.None);
}
break;
}
/* /*
Function.Call(Hash.TASK_SWEEP_AIM_ENTITY,P, "random@paparazzi@pap_anims", "sweep_low", "sweep_med", "sweep_high", -1,V, 1.57f, 0.25f);
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, P,true, 0); Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, P,true, 0);
return Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, P); return Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, P);
*/ */

View File

@ -1,10 +1,6 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Math; using GTA.Math;
using System.Diagnostics;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -19,38 +15,50 @@ namespace RageCoop.Client
/// </summary> /// </summary>
public bool IsLocal public bool IsLocal
{ {
get get => OwnerID == Main.LocalPlayerID;
{
return OwnerID==Main.LocalPlayerID;
}
} }
/// <summary> /// <summary>
/// Network ID for this entity /// Network ID for this entity
/// </summary> /// </summary>
public int ID { get; internal set; } public int ID { get; internal set; }
private int _ownerID;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public int OwnerID { get; internal set; } public int OwnerID
{
get => _ownerID;
internal set
{
if (value == _ownerID && Owner != null) { return; }
_ownerID = value;
Owner = PlayerList.GetPlayer(value);
if (this is SyncedPed && Owner != null)
{
Owner.Character = ((SyncedPed)this);
}
}
}
internal virtual Player Owner { get; private set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public bool IsOutOfSync public bool IsOutOfSync
{ {
get get => Main.Ticked - LastSynced > 200 && ID != 0;
{
return Main.Ticked-LastSynced>200 && ID!=0;
}
} }
internal bool IsReady internal bool IsReady
{ {
get {return (LastSynced>0||LastStateSynced==0);} get => LastSynced > 0 || LastFullSynced == 0;
} }
internal bool IsInvincible { get; set; } = false; internal bool IsInvincible { get; set; } = false;
internal bool NeedUpdate internal bool NeedUpdate
{ {
get { return LastSynced>LastUpdated; } get => LastSynced >= LastUpdated;
} }
#region LAST STATE #region LAST STATE
/// <summary> /// <summary>
@ -60,27 +68,49 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Last time a new sync message arrived. /// Last time a new sync message arrived.
/// </summary> /// </summary>
public ulong LastStateSynced { get; internal set; } = 0; public ulong LastFullSynced { get; internal set; } = 0;
/// <summary> /// <summary>
/// Last time the local entity has been updated, /// Last time the local entity has been updated,
/// </summary> /// </summary>
public ulong LastUpdated { get; set; } = 0; public ulong LastUpdated { get; set; } = 0;
internal Stopwatch LastSentStopWatch { get; set; } = Stopwatch.StartNew();
#endregion #endregion
public bool SendNextFrame { get; set; } = false;
public bool SendFullNextFrame { get; set; } = false;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
internal protected bool _lastFrozen=false; protected internal bool _lastFrozen = false;
internal int ModelHash { get; set; } internal Model Model { get; set; }
internal Vector3 Position { get; set; } internal Vector3 Position { get; set; }
internal Vector3 Rotation { get; set; } internal Vector3 Rotation { get; set; }
internal Quaternion Quaternion { get; set; } internal Quaternion Quaternion { get; set; }
internal Vector3 Velocity { get; set; } internal Vector3 Velocity { get; set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
internal abstract void Update(); internal abstract void Update();
internal void PauseUpdate(ulong frames) internal void PauseUpdate(ulong frames)
{ {
LastUpdated = Main.Ticked + frames; LastUpdated = Main.Ticked + frames;
} }
protected Vector3 Predict(Vector3 input)
{
return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input;
}
private float _accumulatedOff = 0;
protected bool IsOff(float thisOff, float tolerance = 3, float limit = 30)
{
_accumulatedOff += thisOff - tolerance;
if (_accumulatedOff < 0) { _accumulatedOff = 0; }
else if (_accumulatedOff >= limit)
{
_accumulatedOff = 0;
return true;
}
return false;
}
} }
} }

View File

@ -1,66 +1,94 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Math; using GTA.Math;
using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class SyncedProjectile : SyncedEntity internal class SyncedProjectile : SyncedEntity
{ {
public ProjectileDataFlags Flags { private get; set; } = ProjectileDataFlags.None;
public readonly Vector3 Origin;
private bool _firstSend = false;
public bool IsValid { get; private set; } = true;
public new bool IsLocal { get; private set; } = false;
public Projectile MainProjectile { get; set; }
public SyncedEntity Shooter { get; set; }
public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded);
internal override Player Owner => Shooter.Owner;
/// <summary>
/// Invalid property for projectile.
/// </summary>
private new int OwnerID { set { } }
public WeaponHash WeaponHash { get; set; }
private WeaponAsset Asset { get; set; }
public void ExtractData(ref Packets.ProjectileSync p)
{
p.Position = MainProjectile.Position;
p.Velocity = MainProjectile.Velocity;
p.Rotation = MainProjectile.Rotation;
p.ID = ID;
p.ShooterID = Shooter.ID;
p.WeaponHash = (uint)MainProjectile.WeaponHash;
p.Flags = ProjectileDataFlags.None;
if (MainProjectile.IsDead)
{
p.Flags |= ProjectileDataFlags.Exploded;
}
if (MainProjectile.AttachedEntity != null)
{
p.Flags |= ProjectileDataFlags.IsAttached;
}
if (Shooter is SyncedVehicle)
{
p.Flags |= ProjectileDataFlags.IsShotByVehicle;
}
if (_firstSend)
{
p.Flags |= ProjectileDataFlags.IsAttached;
_firstSend = false;
}
}
public SyncedProjectile(Projectile p) public SyncedProjectile(Projectile p)
{ {
var owner = p.OwnerEntity;
if (owner == null) { IsValid = false; return; }
ID = EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
MainProjectile = p; MainProjectile = p;
Origin = p.Position; Origin = p.Position;
var shooter = EntityPool.GetPedByHandle((p.Owner?.Handle).GetValueOrDefault()); if (EntityPool.PedsByHandle.TryGetValue(owner.Handle, out var shooter))
if (shooter==null)
{ {
// Owner will be the vehicle if projectile is shot with a vehicle if (shooter.MainPed != null
var shooterVeh = EntityPool.GetVehicleByHandle((p.Owner?.Handle).GetValueOrDefault()); && (p.AttachedEntity == shooter.MainPed.Weapons.CurrentWeaponObject
if (shooterVeh!=null && shooterVeh.MainVehicle.Driver!=null) || p.AttachedEntity == shooter.MainPed))
{
shooter=shooterVeh.MainVehicle.Driver?.GetSyncEntity();
}
else
{
Main.Logger.Warning($"Could not find owner for projectile:{Hash}");
}
}
if(shooter != null)
{
if (shooter.MainPed!=null && (p.AttachedEntity==shooter.MainPed.Weapons.CurrentWeaponObject || p.AttachedEntity== shooter.MainPed))
{ {
// Reloading // Reloading
IsValid = false; IsValid = false;
return;
} }
ShooterID=shooter.ID; Shooter = shooter;
IsLocal = shooter.IsLocal; IsLocal = shooter.IsLocal;
} }
else if (EntityPool.VehiclesByHandle.TryGetValue(owner.Handle, out var shooterVeh))
{
Shooter = shooterVeh;
IsLocal = shooterVeh.IsLocal;
}
else
{
IsValid = false;
}
} }
public SyncedProjectile(int id) public SyncedProjectile(int id)
{ {
ID = id; ID = id;
IsLocal = false; IsLocal = false;
} }
public bool IsValid { get; private set; } = true;
public new bool IsLocal { get; private set; } = false;
public bool Exploded { get; set; } = false;
public Projectile MainProjectile { get; set; }
public int ShooterID { get; set; }
private SyncedPed Shooter { get;set; }
public Vector3 Origin { get; set; }
/// <summary>
/// Invalid property for projectile.
/// </summary>
private new int OwnerID{ set { } }
public WeaponHash Hash { get; set; }
private WeaponAsset Asset { get; set; }
internal override void Update() internal override void Update()
{ {
@ -72,26 +100,26 @@ namespace RageCoop.Client
CreateProjectile(); CreateProjectile();
return; return;
} }
MainProjectile.Velocity=Velocity+(Position+Networking.Latency*Velocity-MainProjectile.Position); MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
MainProjectile.Rotation = Rotation; MainProjectile.Rotation = Rotation;
LastUpdated = Main.Ticked; LastUpdated = Main.Ticked;
} }
private void CreateProjectile() private void CreateProjectile()
{ {
Asset=new WeaponAsset(Hash); Asset = new WeaponAsset(WeaponHash);
if (!Asset.IsLoaded) { Asset.Request(); } if (!Asset.IsLoaded) { Asset.Request(); return; }
World.ShootBullet(Position,Position+Velocity,(Shooter=EntityPool.GetPedByID(ShooterID))?.MainPed,Asset,0); if (Shooter == null) { return; }
Entity owner;
owner = (Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle;
Position = (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Shooter.Velocity + Position;
var end = Position + Velocity;
Function.Call(Hash.SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z, end.X, end.Y, end.Z, 0, 1, WeaponHash, owner?.Handle ?? 0, 1, 0, -1);
var ps = World.GetAllProjectiles(); var ps = World.GetAllProjectiles();
MainProjectile = ps[ps.Length - 1]; MainProjectile = ps[ps.Length - 1];
if (Hash==(WeaponHash)VehicleWeaponHash.Tank) MainProjectile.Position = Position;
{ MainProjectile.Rotation = Rotation;
var v = Shooter?.MainPed?.CurrentVehicle; MainProjectile.Velocity = Velocity;
if (v!=null)
{
World.CreateParticleEffectNonLooped(SyncEvents.CorePFXAsset, "muz_tank", v.GetMuzzleInfo().Position, v.Bones[35].ForwardVector.ToEulerRotation(v.Bones[35].UpVector), 1);
}
}
EntityPool.Add(this); EntityPool.Add(this);
} }
} }

View File

@ -1,10 +1,4 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA.Native;
using GTA;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -21,18 +15,21 @@ namespace RageCoop.Client
/// The real entity /// The real entity
/// </summary> /// </summary>
public Prop MainProp { get; set; } public Prop MainProp { get; set; }
internal new int OwnerID { get internal new int OwnerID
{
get
{ {
// alwayse owned by server // alwayse owned by server
return 0; return 0;
} } }
}
internal override void Update() internal override void Update()
{ {
if (!NeedUpdate) { return; } if (!NeedUpdate) { return; }
if (MainProp == null || !MainProp.Exists()) if (MainProp == null || !MainProp.Exists())
{ {
MainProp=World.CreateProp(ModelHash,Position,Rotation,false,false); MainProp = World.CreateProp(Model, Position, Rotation, false, false);
MainProp.IsInvincible = true; MainProp.IsInvincible = true;
} }
MainProp.Position = Position; MainProp.Position = Position;

View File

@ -1,426 +0,0 @@
using System;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native;
using GTA.Math;
using RageCoop.Core;
namespace RageCoop.Client
{
/// <summary>
/// A synchronized vehicle instance
/// </summary>
public class SyncedVehicle : SyncedEntity
{
#region -- CONSTRUCTORS --
/// <summary>
/// Create a local entity (outgoing sync)
/// </summary>
/// <param name="v"></param>
internal SyncedVehicle(Vehicle v)
{
ID=EntityPool.RequestNewID();
MainVehicle=v;
MainVehicle.CanPretendOccupants=false;
OwnerID=Main.LocalPlayerID;
}
/// <summary>
/// Create an empty VehicleEntity
/// </summary>
internal SyncedVehicle()
{
}
internal SyncedVehicle(int id)
{
ID=id;
LastSynced=Main.Ticked;
}
#endregion
/// <summary>
/// VehicleSeat,ID
/// </summary>
public Vehicle MainVehicle { get;internal set; }
#region LAST STATE
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
#endregion
#region -- CRITICAL STUFF --
internal Vector3 RotationVelocity { get; set; }
internal float SteeringAngle { get; set; }
internal float ThrottlePower { get; set; }
internal float BrakePower { get; set; }
internal float DeluxoWingRatio { get; set; } = -1;
internal bool IsFlipped
{
get
{
return (Quaternion*Vector3.RelativeTop).Z <(Quaternion*Vector3.RelativeBottom).Z;
}
}
#endregion
#region -- VEHICLE STATE --
internal VehicleDataFlags Flags { get; set; }
internal bool EngineRunning { get; set; }
private bool _lastTransformed = false;
internal bool Transformed { get; set; }
private bool _lastHornActive = false;
internal bool HornActive { get; set; }
internal bool LightsOn { get; set; }
internal bool BrakeLightsOn { get; set; } = false;
internal bool HighBeamsOn { get; set; }
internal byte LandingGear { get; set; }
internal VehicleRoofState RoofState { get; set; }
internal bool SireneActive { get; set; }
internal VehicleDamageModel DamageModel { get; set; }
internal byte[] Colors { get; set; }
internal Dictionary<int, int> Mods { get; set; }
internal bool IsDead { get; set; }
internal float EngineHealth { get; set; }
internal VehicleLockStatus LockStatus{get;set;}
/// <summary>
/// VehicleSeat,PedID
/// </summary>
internal Dictionary<VehicleSeat, SyncedPed> Passengers { get; set; }
internal byte RadioStation = 255;
internal string LicensePlate { get; set; }
internal int _lastLivery = -1;
internal int Livery { get; set; } = -1;
internal bool _checkSeat { get; set; } = true;
#endregion
internal override void Update()
{
#region -- INITIAL CHECK --
// Check if all data avalible
if(!IsReady) { return; }
// Skip update if no new sync message has arrived.
if (!NeedUpdate) { return; }
#endregion
#region -- CHECK EXISTENCE --
if ((MainVehicle == null) || (!MainVehicle.Exists()) || (MainVehicle.Model.Hash != ModelHash))
{
CreateVehicle();
return;
}
#endregion
#region -- SYNC CRITICAL --
if (SteeringAngle != MainVehicle.SteeringAngle)
{
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
}
MainVehicle.ThrottlePower=ThrottlePower;
MainVehicle.BrakePower=BrakePower;
if (MainVehicle.Position.DistanceTo(Position)<5)
{
MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPredictionDefault - MainVehicle.Position);
if (IsFlipped)
{
MainVehicle.Quaternion=Quaternion.Slerp(MainVehicle.Quaternion, Quaternion, 0.5f);
MainVehicle.RotationVelocity=RotationVelocity;
}
else
{
Vector3 cali = GetCalibrationRotation();
if (cali.Length()<50)
{
MainVehicle.RotationVelocity = RotationVelocity+cali*0.2f;
}
else
{
MainVehicle.Quaternion=Quaternion;
MainVehicle.RotationVelocity=RotationVelocity;
}
}
}
else
{
MainVehicle.Position=Position;
MainVehicle.Velocity=Velocity;
MainVehicle.Quaternion=Quaternion;
}
if (DeluxoWingRatio!=-1)
{
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
#endregion
if (LastStateSynced>LastUpdated)
{
#region -- SYNC STATE --
#region -- PASSENGER SYNC --
// check passengers (and driver).
if (_checkSeat)
{
var currentPassengers = MainVehicle.GetPassengers();
lock (Passengers)
{
for (int i = -1; i<MainVehicle.PassengerCapacity; i++)
{
VehicleSeat seat = (VehicleSeat)i;
if (Passengers.ContainsKey(seat))
{
SyncedPed c = Passengers[seat];
if (c?.ID==Main.LocalPlayerID && (RadioStation!=Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX)))
{
Util.SetPlayerRadioIndex(RadioStation);
}
if (c?.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)&&(!c.MainPed.IsTaskActive(TaskType.CTaskExitVehicleSeat)))
{
Passengers[seat].MainPed.SetIntoVehicle(MainVehicle, seat);
}
}
else if (!MainVehicle.IsSeatFree(seat))
{
var p = MainVehicle.Occupants.Where(x => x.SeatIndex==seat).FirstOrDefault();
if ((p!=null)&& !p.IsTaskActive(TaskType.CTaskLeaveAnyCar))
{
p.Task.WarpOutOfVehicle(MainVehicle);
}
}
}
}
}
#endregion
if (Colors != null && Colors != _lastVehicleColors)
{
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, Colors[0], Colors[1]);
_lastVehicleColors = Colors;
}
MainVehicle.EngineHealth=EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods))
{
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
foreach (KeyValuePair<int, int> mod in Mods)
{
MainVehicle.Mods[(VehicleModType)mod.Key].Index = mod.Value;
}
_lastVehicleMods = Mods;
}
if (IsDead)
{
if (MainVehicle.IsDead)
{
return;
}
else
{
MainVehicle.Explode();
}
}
else
{
if (MainVehicle.IsDead)
{
MainVehicle.Repair();
}
}
if (EngineRunning != MainVehicle.IsEngineRunning)
{
MainVehicle.IsEngineRunning = EngineRunning;
}
if (LightsOn != MainVehicle.AreLightsOn)
{
MainVehicle.AreLightsOn = LightsOn;
}
if (HighBeamsOn != MainVehicle.AreHighBeamsOn)
{
MainVehicle.AreHighBeamsOn = HighBeamsOn;
}
if (MainVehicle.IsSubmarineCar)
{
if (Transformed)
{
if (!_lastTransformed)
{
_lastTransformed = true;
Function.Call(Hash._TRANSFORM_VEHICLE_TO_SUBMARINE, MainVehicle.Handle, false);
}
}
else if (_lastTransformed)
{
_lastTransformed = false;
Function.Call(Hash._TRANSFORM_SUBMARINE_TO_VEHICLE, MainVehicle.Handle, false);
}
}
if (MainVehicle.IsAircraft)
{
if (LandingGear != (byte)MainVehicle.LandingGearState)
{
MainVehicle.LandingGearState = (VehicleLandingGearState)LandingGear;
}
}
else
{
if (MainVehicle.HasSiren && SireneActive != MainVehicle.IsSirenActive)
{
MainVehicle.IsSirenActive = SireneActive;
}
if (HornActive)
{
if (!_lastHornActive)
{
_lastHornActive = true;
MainVehicle.SoundHorn(99999);
}
}
else if (_lastHornActive)
{
_lastHornActive = false;
MainVehicle.SoundHorn(1);
}
if (MainVehicle.HasRoof && MainVehicle.RoofState!=RoofState)
{
MainVehicle.RoofState=RoofState;
}
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
MainVehicle.SetDamageModel(DamageModel);
}
MainVehicle.LockStatus=LockStatus;
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{
if (!MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(true);
}
}
else if(ModelHash==1483171323)
{
if (MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(false);
}
}
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle)!=LicensePlate)
{
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT,MainVehicle,LicensePlate);
}
if (_lastLivery!=Livery)
{
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery=Livery;
}
#endregion
}
LastUpdated=Main.Ticked;
}
private Vector3 GetCalibrationRotation()
{
var rot=Quaternion.LookRotation(Quaternion*Vector3.RelativeFront, Quaternion*Vector3.RelativeTop).ToEulerAngles();
var curRot=Quaternion.LookRotation(MainVehicle.Quaternion*Vector3.RelativeFront, MainVehicle.Quaternion*Vector3.RelativeTop).ToEulerAngles();
var r = (rot-curRot).ToDegree();
if (r.X>180) { r.X=r.X-360; }
else if(r.X<-180) { r.X=360+r.X; }
if (r.Y>180) { r.Y=r.Y-360; }
else if (r.Y<-180) { r.Y=360+r.Y; }
if (r.Z>180) { r.Z=r.Z-360; }
else if (r.Z<-180) { r.Z=360+r.Z; }
return r;
}
private void CreateVehicle()
{
MainVehicle?.Delete();
Model vehicleModel = ModelHash.ModelRequest();
if (vehicleModel == null)
{
//GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return;
}
MainVehicle = World.CreateVehicle(vehicleModel, Position);
lock (EntityPool.VehiclesLock)
{
EntityPool.Add( this);
}
MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof)
{
MainVehicle.RoofState=RoofState;
}
if (IsInvincible) { MainVehicle.IsInvincible=true; }
vehicleModel.MarkAsNoLongerNeeded();
}
#region -- PEDALING --
/*
* Thanks to @oldnapalm.
*/
private string PedalingAnimDict()
{
switch ((VehicleHash)ModelHash)
{
case VehicleHash.Bmx:
return "veh@bicycle@bmx@front@base";
case VehicleHash.Cruiser:
return "veh@bicycle@cruiserfront@base";
case VehicleHash.Scorcher:
return "veh@bicycle@mountainfront@base";
default:
return "veh@bicycle@roadfront@base";
}
}
private string PedalingAnimName(bool fast)
{
return fast ? "fast_pedal_char" : "cruise_pedal_char";
}
private void StartPedalingAnim(bool fast)
{
MainVehicle.Driver?.Task.PlayAnimation(PedalingAnimDict(), PedalingAnimName(fast), 8.0f, -8.0f, -1, AnimationFlags.Loop | AnimationFlags.AllowRotation, 1.0f);
}
private void StopPedalingAnim(bool fast)
{
MainVehicle.Driver.Task.ClearAnimation(PedalingAnimDict(), PedalingAnimName(fast));
}
#endregion
#region OUTGOING
internal float LastNozzleAngle { get; set; }
#endregion
}
}

View File

@ -0,0 +1,82 @@
using GTA;
using GTA.Math;
using RageCoop.Core;
using System.Collections.Generic;
namespace RageCoop.Client
{
public partial class SyncedVehicle
{
public Vehicle MainVehicle { get; internal set; }
#region -- SYNC DATA --
internal Vector3 RotationVelocity { get; set; }
internal float SteeringAngle { get; set; }
internal float ThrottlePower { get; set; }
internal float BrakePower { get; set; }
internal float DeluxoWingRatio { get; set; } = -1;
internal byte LandingGear { get; set; }
internal VehicleRoofState RoofState { get; set; }
internal VehicleDamageModel DamageModel { get; set; }
internal byte[] Colors { get; set; }
internal Dictionary<int, int> Mods { get; set; }
internal float EngineHealth { get; set; }
internal VehicleLockStatus LockStatus { get; set; }
internal byte RadioStation = 255;
internal string LicensePlate { get; set; }
internal int Livery { get; set; } = -1;
internal VehicleDataFlags Flags { get; set; }
#endregion
#region FLAGS
internal bool EngineRunning { get => Flags.HasVehFlag(VehicleDataFlags.IsEngineRunning); }
internal bool Transformed { get => Flags.HasVehFlag(VehicleDataFlags.IsTransformed); }
internal bool HornActive { get => Flags.HasVehFlag(VehicleDataFlags.IsHornActive); }
internal bool LightsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreLightsOn); }
internal bool BrakeLightsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn); }
internal bool HighBeamsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreHighBeamsOn); }
internal bool SireneActive { get => Flags.HasVehFlag(VehicleDataFlags.IsSirenActive); }
internal bool IsDead { get => Flags.HasVehFlag(VehicleDataFlags.IsDead); }
internal bool IsDeluxoHovering { get => Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering); }
#endregion
#region FIXED-DATA
internal bool IsFlipped
{
get => IsMotorcycle || ((Quaternion * Vector3.RelativeTop).Z - (Quaternion * Vector3.RelativeBottom).Z) < 0.5;
}
internal bool IsMotorcycle;
internal bool IsAircraft;
internal bool HasRocketBoost;
internal bool HasParachute;
internal bool HasRoof;
internal bool IsSubmarineCar;
internal bool IsDeluxo;
#endregion
#region PRIVATE
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
private bool _lastHornActive = false;
private bool _lastTransformed = false;
internal int _lastLivery = -1;
private readonly List<Vector3> _predictedTrace = new List<Vector3>();
private readonly List<Vector3> _orgTrace = new List<Vector3>();
private Vector3 _predictedPosition;
#endregion
#region OUTGOING
internal float LastNozzleAngle { get; set; }
internal float LastEngineHealth { get; set; }
internal Vector3 LastVelocity { get; set; }
#endregion
}
}

View File

@ -0,0 +1,352 @@
using GTA;
using GTA.Math;
using GTA.Native;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Linq;
namespace RageCoop.Client
{
/// <summary>
/// A synchronized vehicle instance
/// </summary>
public partial class SyncedVehicle : SyncedEntity
{
#region -- CONSTRUCTORS --
/// <summary>
/// Create a local entity (outgoing sync)
/// </summary>
/// <param name="v"></param>
internal SyncedVehicle(Vehicle v)
{
ID = EntityPool.RequestNewID();
MainVehicle = v;
MainVehicle.CanPretendOccupants = false;
OwnerID = Main.LocalPlayerID;
SetUpFixedData();
}
internal void SetUpFixedData()
{
if (MainVehicle == null) { return; }
IsAircraft = MainVehicle.IsAircraft;
IsMotorcycle = MainVehicle.IsMotorcycle;
HasRocketBoost = MainVehicle.HasRocketBoost;
HasParachute = MainVehicle.HasParachute;
HasRoof = MainVehicle.HasRoof;
IsSubmarineCar = MainVehicle.IsSubmarineCar;
IsDeluxo = MainVehicle.Model == 1483171323;
}
/// <summary>
/// Create an empty VehicleEntity
/// </summary>
internal SyncedVehicle()
{
}
internal SyncedVehicle(int id)
{
ID = id;
LastSynced = Main.Ticked;
}
#endregion
/// <summary>
/// VehicleSeat,ID
/// </summary>
internal override void Update()
{
#if DEBUG_VEH
foreach(var s in _predictedTrace)
{
World.DrawMarker(MarkerType.DebugSphere, s, default, default, new Vector3(0.3f, 0.3f, 0.3f), Color.AliceBlue);
}
foreach (var s in _orgTrace)
{
World.DrawMarker(MarkerType.DebugSphere, s, default, default, new Vector3(0.3f, 0.3f, 0.3f), Color.Orange);
}
#endif
// Check if all data avalible
if (!IsReady || Owner == null) { return; }
// Check existence
if ((MainVehicle == null) || (!MainVehicle.Exists()) || (MainVehicle.Model != Model))
{
if (!CreateVehicle())
{
return;
}
}
DisplayVehicle();
// Skip update if no new sync message has arrived.
if (!NeedUpdate)
{
return;
}
if (SteeringAngle != MainVehicle.SteeringAngle)
{
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
}
MainVehicle.ThrottlePower = ThrottlePower;
MainVehicle.BrakePower = BrakePower;
if (IsDead)
{
if (MainVehicle.IsDead)
{
return;
}
MainVehicle.Explode();
}
else
{
if (MainVehicle.IsDead)
{
Main.Delay(() =>
{
if (MainVehicle.IsDead && !IsDead)
{
MainVehicle.Repair();
}
}, 1000);
}
}
if (MainVehicle.IsOnFire)
{
if (!Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
{
Function.Call(Hash.STOP_ENTITY_FIRE, MainVehicle);
}
}
else if (Flags.HasVehFlag(VehicleDataFlags.IsOnFire))
{
Function.Call(Hash.START_ENTITY_FIRE, MainVehicle);
}
if (EngineRunning != MainVehicle.IsEngineRunning)
{
MainVehicle.IsEngineRunning = EngineRunning;
}
if (LightsOn != MainVehicle.AreLightsOn)
{
MainVehicle.AreLightsOn = LightsOn;
}
if (HighBeamsOn != MainVehicle.AreHighBeamsOn)
{
MainVehicle.AreHighBeamsOn = HighBeamsOn;
}
if (IsAircraft)
{
if (LandingGear != (byte)MainVehicle.LandingGearState)
{
MainVehicle.LandingGearState = (VehicleLandingGearState)LandingGear;
}
}
else
{
if (MainVehicle.HasSiren && SireneActive != MainVehicle.IsSirenActive)
{
MainVehicle.IsSirenActive = SireneActive;
}
if (HornActive)
{
if (!_lastHornActive)
{
_lastHornActive = true;
MainVehicle.SoundHorn(99999);
}
}
else if (_lastHornActive)
{
_lastHornActive = false;
MainVehicle.SoundHorn(1);
}
if (HasRoof && MainVehicle.RoofState != RoofState)
{
MainVehicle.RoofState = RoofState;
}
if (HasRocketBoost && Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) != MainVehicle.IsRocketBoostActive())
{
MainVehicle.SetRocketBoostActive(Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive));
}
if (HasParachute && Flags.HasFlag(VehicleDataFlags.IsParachuteActive) != MainVehicle.IsParachuteActive())
{
MainVehicle.SetParachuteActive(Flags.HasFlag(VehicleDataFlags.IsParachuteActive));
}
if (IsSubmarineCar)
{
if (Transformed)
{
if (!_lastTransformed)
{
_lastTransformed = true;
Function.Call(Hash.TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
}
}
else if (_lastTransformed)
{
_lastTransformed = false;
Function.Call(Hash.TRANSFORM_TO_CAR, MainVehicle.Handle, false);
}
}
else if (IsDeluxo)
{
MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
if (IsDeluxoHovering)
{
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
}
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
}
MainVehicle.LockStatus = LockStatus;
if (LastFullSynced >= LastUpdated)
{
if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{
MainVehicle.Repair();
}
if (Colors != null && Colors != _lastVehicleColors)
{
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, Colors[0], Colors[1]);
_lastVehicleColors = Colors;
}
MainVehicle.EngineHealth = EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods))
{
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
foreach (KeyValuePair<int, int> mod in Mods)
{
MainVehicle.Mods[(VehicleModType)mod.Key].Index = mod.Value;
}
_lastVehicleMods = Mods;
}
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle) != LicensePlate)
{
Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
}
if (_lastLivery != Livery)
{
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery = Livery;
}
MainVehicle.SetDamageModel(DamageModel);
}
LastUpdated = Main.Ticked;
}
private void DisplayVehicle()
{
_predictedPosition = Predict(Position);
var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(_predictedPosition);
var cali = dist * (_predictedPosition - current);
if (Velocity.Length() < 0.1) { cali *= 10; }
if (dist > 10)
{
MainVehicle.Position = _predictedPosition;
MainVehicle.Velocity = Velocity;
MainVehicle.Quaternion = Quaternion;
return;
}
if (dist > 0.03)
{
MainVehicle.Velocity = Velocity + cali;
}
Vector3 calirot;
if (IsFlipped || (calirot = GetCalibrationRotation()).Length() > 50)
{
MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
MainVehicle.RotationVelocity = RotationVelocity;
return;
}
MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
}
private Vector3 GetCalibrationRotation()
{
var rot = Quaternion.LookRotation(Quaternion * Vector3.RelativeFront, Quaternion * Vector3.RelativeTop).ToEulerAngles();
var curRot = Quaternion.LookRotation(MainVehicle.ReadQuaternion() * Vector3.RelativeFront, MainVehicle.ReadQuaternion() * Vector3.RelativeTop).ToEulerAngles();
var r = (rot - curRot).ToDegree();
if (r.X > 180) { r.X = r.X - 360; }
else if (r.X < -180) { r.X = 360 + r.X; }
if (r.Y > 180) { r.Y = r.Y - 360; }
else if (r.Y < -180) { r.Y = 360 + r.Y; }
if (r.Z > 180) { r.Z = r.Z - 360; }
else if (r.Z < -180) { r.Z = 360 + r.Z; }
return r;
}
private bool CreateVehicle()
{
var existing = World.GetNearbyVehicles(Position, 2).ToList().FirstOrDefault();
if (existing != null && existing != MainVehicle)
{
if (EntityPool.VehiclesByHandle.ContainsKey(existing.Handle))
{
EntityPool.RemoveVehicle(ID);
return false;
}
existing.Delete();
}
MainVehicle?.Delete();
MainVehicle = Util.CreateVehicle(Model, Position);
if (!Model.IsInCdImage)
{
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return false;
}
if (MainVehicle == null)
{
Model.Request();
return false;
}
lock (EntityPool.VehiclesLock)
{
EntityPool.Add(this);
}
MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof)
{
MainVehicle.RoofState = RoofState;
}
foreach (var w in MainVehicle.Wheels)
{
w.Fix();
}
if (IsInvincible) { MainVehicle.IsInvincible = true; }
SetUpFixedData();
Model.MarkAsNoLongerNeeded();
return true;
}
}
}

View File

@ -1,77 +1,73 @@
 using GTA;
using System;
using GTA;
using GTA.Native; using GTA.Native;
using RageCoop.Core; using Lidgren.Network;
using RageCoop.Client.Scripting;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using RageCoop.Client.Scripting;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class EntityPool internal class EntityPool
{ {
private static bool _trafficSpawning=true;
public static object PedsLock = new object(); public static object PedsLock = new object();
private static Dictionary<int, SyncedPed> ID_Peds = new Dictionary<int, SyncedPed>();
public static int CharactersCount { get { return ID_Peds.Count; } }
#if BENCHMARK #if BENCHMARK
private static Stopwatch PerfCounter=new Stopwatch(); private static Stopwatch PerfCounter=new Stopwatch();
private static Stopwatch PerfCounter2=Stopwatch.StartNew(); private static Stopwatch PerfCounter2=Stopwatch.StartNew();
#endif #endif
/// <summary> #region ACTIVE INSTANCES
/// Faster access to Character with Handle, but values may not equal to <see cref="ID_Peds"/> since Ped might not have been created.
/// </summary> public static Dictionary<int, SyncedPed> PedsByID = new Dictionary<int, SyncedPed>();
private static Dictionary<int, SyncedPed> Handle_Peds = new Dictionary<int, SyncedPed>(); public static Dictionary<int, SyncedPed> PedsByHandle = new Dictionary<int, SyncedPed>();
public static object VehiclesLock = new object(); public static Dictionary<int, SyncedVehicle> VehiclesByID = new Dictionary<int, SyncedVehicle>();
private static Dictionary<int, SyncedVehicle> ID_Vehicles = new Dictionary<int, SyncedVehicle>(); public static Dictionary<int, SyncedVehicle> VehiclesByHandle = new Dictionary<int, SyncedVehicle>();
private static Dictionary<int, SyncedVehicle> Handle_Vehicles = new Dictionary<int, SyncedVehicle>();
public static object ProjectilesLock = new object(); public static Dictionary<int, SyncedProjectile> ProjectilesByID = new Dictionary<int, SyncedProjectile>();
private static Dictionary<int, SyncedProjectile> ID_Projectiles = new Dictionary<int, SyncedProjectile>(); public static Dictionary<int, SyncedProjectile> ProjectilesByHandle = new Dictionary<int, SyncedProjectile>();
private static Dictionary<int, SyncedProjectile> Handle_Projectiles = new Dictionary<int, SyncedProjectile>();
public static object PropsLock=new object();
public static Dictionary<int, SyncedProp> ServerProps = new Dictionary<int, SyncedProp>(); public static Dictionary<int, SyncedProp> ServerProps = new Dictionary<int, SyncedProp>();
public static object BlipsLock = new object();
public static Dictionary<int, Blip> ServerBlips = new Dictionary<int, Blip>(); public static Dictionary<int, Blip> ServerBlips = new Dictionary<int, Blip>();
#endregion
#region LOCKS
public static object VehiclesLock = new object();
public static object ProjectilesLock = new object();
public static object PropsLock = new object();
public static object BlipsLock = new object();
#endregion
public static void Cleanup(bool keepPlayer = true, bool keepMine = true) public static void Cleanup(bool keepPlayer = true, bool keepMine = true)
{ {
foreach(int id in new List<int>(ID_Peds.Keys)) foreach (var ped in PedsByID.Values.ToArray())
{ {
if (keepPlayer&&(id==Main.LocalPlayerID)) { continue; } if ((keepPlayer && (ped.ID == Main.LocalPlayerID)) || (keepMine && (ped.OwnerID == Main.LocalPlayerID))) { continue; }
if (keepMine&&(ID_Peds[id].OwnerID==Main.LocalPlayerID)) { continue; } RemovePed(ped.ID);
RemovePed(id);
} }
ID_Peds.Clear(); PedsByID.Clear();
Handle_Peds.Clear(); PedsByHandle.Clear();
foreach (int id in new List<int>(ID_Vehicles.Keys)) foreach (int id in VehiclesByID.Keys.ToArray())
{ {
if (keepMine&&(ID_Vehicles[id].OwnerID==Main.LocalPlayerID)) { continue; } if (keepMine && (VehiclesByID[id].OwnerID == Main.LocalPlayerID)) { continue; }
RemoveVehicle(id); RemoveVehicle(id);
} }
ID_Vehicles.Clear(); VehiclesByID.Clear();
Handle_Vehicles.Clear(); VehiclesByHandle.Clear();
foreach(var p in ID_Projectiles.Values) foreach (var p in ProjectilesByID.Values)
{ {
if (p.ShooterID!=Main.LocalPlayerID && p.MainProjectile!=null && p.MainProjectile.Exists()) if (p.Shooter.ID != Main.LocalPlayerID && p.MainProjectile != null && p.MainProjectile.Exists())
{ {
p.MainProjectile.Delete(); p.MainProjectile.Delete();
} }
} }
ID_Projectiles.Clear(); ProjectilesByID.Clear();
Handle_Projectiles.Clear(); ProjectilesByHandle.Clear();
foreach (var p in ServerProps.Values) foreach (var p in ServerProps.Values)
{ {
@ -90,46 +86,16 @@ namespace RageCoop.Client
} }
#region PEDS #region PEDS
public static SyncedPed GetPedByID(int id) public static SyncedPed GetPedByID(int id) => PedsByID.TryGetValue(id, out var p) ? p : null;
{ public static SyncedPed GetPedByHandle(int handle) => PedsByHandle.TryGetValue(handle, out var p) ? p : null;
return ID_Peds.ContainsKey(id) ? ID_Peds[id] : null; public static List<int> GetPedIDs() => new List<int>(PedsByID.Keys);
}
public static SyncedPed GetPedByHandle(int handle)
{
return Handle_Peds.ContainsKey(handle) ? Handle_Peds[handle] : null;
}
public static List<int> GetPedIDs()
{
return new List<int>(ID_Peds.Keys);
}
public static bool AddPlayer() public static bool AddPlayer()
{ {
Ped p = Game.Player.Character; Ped p = Game.Player.Character;
// var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@";
// Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
SyncedPed player = GetPedByID(Main.LocalPlayerID); SyncedPed player = GetPedByID(Main.LocalPlayerID);
if (player!=null) if (player == null)
{
if (player.MainPed!=p)
{
// Player model changed
player.MainPed = p;
// Remove it from Handle_Characters
var pairs=Handle_Peds.Where(x=>x.Value==player);
if (pairs.Any())
{
var pair=pairs.First();
// Re-add
Handle_Peds.Remove(pair.Key);
if (Handle_Peds.ContainsKey(p.Handle))
{
RemovePed(Handle_Peds[p.Handle].ID);
}
Handle_Peds.Add(p.Handle, player);
}
}
}
else
{ {
Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}"); Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
SyncedPed c = new SyncedPed(p); SyncedPed c = new SyncedPed(p);
@ -139,26 +105,47 @@ namespace RageCoop.Client
PlayerList.SetPlayer(c.ID, Main.Settings.Username); PlayerList.SetPlayer(c.ID, Main.Settings.Username);
return true; return true;
} }
if (player.MainPed != p)
{
// Player model changed
player.MainPed = p;
// Remove it from Handle_Characters
var pairs = PedsByHandle.Where(x => x.Value == player);
if (pairs.Any())
{
var pair = pairs.First();
// Re-add
PedsByHandle.Remove(pair.Key);
if (PedsByHandle.ContainsKey(p.Handle))
{
RemovePed(PedsByHandle[p.Handle].ID);
}
PedsByHandle.Add(p.Handle, player);
}
}
return false; return false;
} }
public static void Add(SyncedPed c) public static void Add(SyncedPed c)
{ {
if (ID_Peds.ContainsKey(c.ID)) if (PedsByID.ContainsKey(c.ID))
{ {
ID_Peds[c.ID]=c; PedsByID[c.ID] = c;
} }
else else
{ {
ID_Peds.Add(c.ID, c); PedsByID.Add(c.ID, c);
} }
if (c.MainPed == null) { return; } if (c.MainPed == null) { return; }
if (Handle_Peds.ContainsKey(c.MainPed.Handle)) if (PedsByHandle.ContainsKey(c.MainPed.Handle))
{ {
Handle_Peds[c.MainPed.Handle]=c; PedsByHandle[c.MainPed.Handle] = c;
} }
else else
{ {
Handle_Peds.Add(c.MainPed.Handle, c); PedsByHandle.Add(c.MainPed.Handle, c);
} }
if (c.IsLocal) if (c.IsLocal)
{ {
@ -167,25 +154,26 @@ namespace RageCoop.Client
} }
public static void RemovePed(int id, string reason = "Cleanup") public static void RemovePed(int id, string reason = "Cleanup")
{ {
if (ID_Peds.ContainsKey(id)) if (PedsByID.ContainsKey(id))
{ {
SyncedPed c = ID_Peds[id]; SyncedPed c = PedsByID[id];
var p = c.MainPed; var p = c.MainPed;
if (p != null) if (p != null)
{ {
if (Handle_Peds.ContainsKey(p.Handle)) if (PedsByHandle.ContainsKey(p.Handle))
{ {
Handle_Peds.Remove(p.Handle); PedsByHandle.Remove(p.Handle);
} }
// Main.Logger.Debug($"Removing ped {c.ID}. Reason:{reason}"); // Main.Logger.Debug($"Removing ped {c.ID}. Reason:{reason}");
p.AttachedBlip?.Delete(); p.AttachedBlip?.Delete();
p.Kill(); p.Kill();
p.Model.MarkAsNoLongerNeeded();
p.MarkAsNoLongerNeeded(); p.MarkAsNoLongerNeeded();
p.Delete(); p.Delete();
} }
c.PedBlip?.Delete(); c.PedBlip?.Delete();
c.ParachuteProp?.Delete(); c.ParachuteProp?.Delete();
ID_Peds.Remove(id); PedsByID.Remove(id);
if (c.IsLocal) if (c.IsLocal)
{ {
API.Events.InvokePedDeleted(c); API.Events.InvokePedDeleted(c);
@ -195,36 +183,27 @@ namespace RageCoop.Client
#endregion #endregion
#region VEHICLES #region VEHICLES
public static SyncedVehicle GetVehicleByID(int id) public static SyncedVehicle GetVehicleByID(int id) => VehiclesByID.TryGetValue(id, out var v) ? v : null;
{ public static SyncedVehicle GetVehicleByHandle(int handle) => VehiclesByHandle.TryGetValue(handle, out var v) ? v : null;
return ID_Vehicles.ContainsKey(id) ? ID_Vehicles[id] : null; public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys);
}
public static SyncedVehicle GetVehicleByHandle(int handle)
{
return Handle_Vehicles.ContainsKey(handle) ? Handle_Vehicles[handle] : null;
}
public static List<int> GetVehicleIDs()
{
return new List<int>(ID_Vehicles.Keys);
}
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
if (ID_Vehicles.ContainsKey(v.ID)) if (VehiclesByID.ContainsKey(v.ID))
{ {
ID_Vehicles[v.ID]=v; VehiclesByID[v.ID] = v;
} }
else else
{ {
ID_Vehicles.Add(v.ID, v); VehiclesByID.Add(v.ID, v);
} }
if (v.MainVehicle == null) { return; } if (v.MainVehicle == null) { return; }
if (Handle_Vehicles.ContainsKey(v.MainVehicle.Handle)) if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle))
{ {
Handle_Vehicles[v.MainVehicle.Handle]=v; VehiclesByHandle[v.MainVehicle.Handle] = v;
} }
else else
{ {
Handle_Vehicles.Add(v.MainVehicle.Handle, v); VehiclesByHandle.Add(v.MainVehicle.Handle, v);
} }
if (v.IsLocal) if (v.IsLocal)
{ {
@ -233,22 +212,23 @@ namespace RageCoop.Client
} }
public static void RemoveVehicle(int id, string reason = "Cleanup") public static void RemoveVehicle(int id, string reason = "Cleanup")
{ {
if (ID_Vehicles.ContainsKey(id)) if (VehiclesByID.ContainsKey(id))
{ {
SyncedVehicle v = ID_Vehicles[id]; SyncedVehicle v = VehiclesByID[id];
var veh = v.MainVehicle; var veh = v.MainVehicle;
if (veh != null) if (veh != null)
{ {
if (Handle_Vehicles.ContainsKey(veh.Handle)) if (VehiclesByHandle.ContainsKey(veh.Handle))
{ {
Handle_Vehicles.Remove(veh.Handle); VehiclesByHandle.Remove(veh.Handle);
} }
// Main.Logger.Debug($"Removing vehicle {v.ID}. Reason:{reason}"); // Main.Logger.Debug($"Removing vehicle {v.ID}. Reason:{reason}");
veh.AttachedBlip?.Delete(); veh.AttachedBlip?.Delete();
veh.Model.MarkAsNoLongerNeeded();
veh.MarkAsNoLongerNeeded(); veh.MarkAsNoLongerNeeded();
veh.Delete(); veh.Delete();
} }
ID_Vehicles.Remove(id); VehiclesByID.Remove(id);
if (v.IsLocal) { API.Events.InvokeVehicleDeleted(v); } if (v.IsLocal) { API.Events.InvokeVehicleDeleted(v); }
} }
} }
@ -258,72 +238,68 @@ namespace RageCoop.Client
#region PROJECTILES #region PROJECTILES
public static SyncedProjectile GetProjectileByID(int id) public static SyncedProjectile GetProjectileByID(int id)
{ {
return ID_Projectiles.ContainsKey(id) ? ID_Projectiles[id] : null; return ProjectilesByID.TryGetValue(id, out var p) ? p : null;
} }
public static void Add(SyncedProjectile p) public static void Add(SyncedProjectile p)
{ {
if (!p.IsValid) { return; } if (!p.IsValid) { return; }
if (ID_Projectiles.ContainsKey(p.ID)) if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank)
{ {
ID_Projectiles[p.ID]=p; Networking.SendBullet(p.Position, p.Position + p.Velocity, (uint)VehicleWeaponHash.Tank, ((SyncedVehicle)p.Shooter).MainVehicle.Driver.GetSyncEntity().ID);
}
if (ProjectilesByID.ContainsKey(p.ID))
{
ProjectilesByID[p.ID] = p;
} }
else else
{ {
ID_Projectiles.Add(p.ID, p); ProjectilesByID.Add(p.ID, p);
} }
if (p.MainProjectile == null) { return; } if (p.MainProjectile == null) { return; }
if (Handle_Projectiles.ContainsKey(p.MainProjectile.Handle)) if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle))
{ {
Handle_Projectiles[p.MainProjectile.Handle]=p; ProjectilesByHandle[p.MainProjectile.Handle] = p;
} }
else else
{ {
Handle_Projectiles.Add(p.MainProjectile.Handle, p); ProjectilesByHandle.Add(p.MainProjectile.Handle, p);
} }
} }
public static void RemoveProjectile(int id, string reason) public static void RemoveProjectile(int id, string reason)
{ {
if (ID_Projectiles.ContainsKey(id)) if (ProjectilesByID.ContainsKey(id))
{ {
SyncedProjectile sp = ID_Projectiles[id]; SyncedProjectile sp = ProjectilesByID[id];
var p = sp.MainProjectile; var p = sp.MainProjectile;
if (p != null) if (p != null)
{ {
if (Handle_Projectiles.ContainsKey(p.Handle)) if (ProjectilesByHandle.ContainsKey(p.Handle))
{ {
Handle_Projectiles.Remove(p.Handle); ProjectilesByHandle.Remove(p.Handle);
} }
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}"); //Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
p.Explode(); if (sp.Exploded) p.Explode();
} }
ID_Projectiles.Remove(id); ProjectilesByID.Remove(id);
} }
} }
public static bool PedExists(int id) public static bool PedExists(int id) => PedsByID.ContainsKey(id);
{ public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id);
return ID_Peds.ContainsKey(id); public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id);
}
public static bool VehicleExists(int id)
{
return ID_Vehicles.ContainsKey(id);
}
public static bool ProjectileExists(int id)
{
return ID_Projectiles.ContainsKey(id);
}
#endregion #endregion
static int vehStateIndex; private static int vehStateIndex;
static int pedStateIndex; private static int pedStateIndex;
static int vehStatesPerFrame; private static int vehStatesPerFrame;
static int pedStatesPerFrame; private static int pedStatesPerFrame;
static int i; private static int i;
public static Ped[] allPeds = new Ped[0]; public static Ped[] allPeds = new Ped[0];
public static Vehicle[] allVehicles = new Vehicle[0]; public static Vehicle[] allVehicles = new Vehicle[0];
public static Projectile[] allProjectiles = new Projectile[0]; public static Projectile[] allProjectiles = new Projectile[0];
public static void DoSync() public static void DoSync()
{ {
UpdateTargets();
#if BENCHMARK #if BENCHMARK
PerfCounter.Restart(); PerfCounter.Restart();
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
@ -333,17 +309,6 @@ namespace RageCoop.Client
allProjectiles = World.GetAllProjectiles(); allProjectiles = World.GetAllProjectiles();
vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1; vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1;
pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1; pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1;
if (Main.Ticked%50==0)
{
bool flag1 = allVehicles.Length>Main.Settings.WorldVehicleSoftLimit && Main.Settings.WorldVehicleSoftLimit>-1;
bool flag2 = allPeds.Length>Main.Settings.WorldPedSoftLimit && Main.Settings.WorldPedSoftLimit>-1;
if ((flag1||flag2) && _trafficSpawning)
{ SetBudget(0); _trafficSpawning=false; }
else if(!_trafficSpawning)
{ SetBudget(1); _trafficSpawning=true; }
}
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
@ -354,31 +319,24 @@ namespace RageCoop.Client
foreach (Projectile p in allProjectiles) foreach (Projectile p in allProjectiles)
{ {
if (!Handle_Projectiles.ContainsKey(p.Handle)) if (!ProjectilesByHandle.ContainsKey(p.Handle))
{ {
Add(new SyncedProjectile(p)); Add(new SyncedProjectile(p));
} }
} }
foreach (SyncedProjectile p in ID_Projectiles.Values.ToArray()) foreach (SyncedProjectile p in ProjectilesByID.Values.ToArray())
{ {
// Outgoing sync // Outgoing sync
if (p.IsLocal) if (p.IsLocal)
{ {
if (p.MainProjectile.AttachedEntity == null) if (p.MainProjectile.AttachedEntity == null)
{ {
// Prevent projectiles from exploding next to vehicle // Prevent projectiles from exploding next to vehicle
if (WeaponUtil.VehicleProjectileWeapons.Contains((VehicleWeaponHash)p.MainProjectile.WeaponHash)) if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank || (p.MainProjectile.OwnerEntity?.EntityType == EntityType.Vehicle && p.MainProjectile.Position.DistanceTo(p.Origin) < 2))
{
if (p.MainProjectile.WeaponHash!=(WeaponHash)VehicleWeaponHash.Tank && p.Origin.DistanceTo(p.MainProjectile.Position)<2)
{ {
continue; continue;
} }
}
Networking.SendProjectile(p); Networking.SendProjectile(p);
} }
} }
@ -392,48 +350,52 @@ namespace RageCoop.Client
{ {
p.Update(); p.Update();
} }
} }
} }
} }
i = -1; i = -1;
lock (PedsLock) lock (PedsLock)
{ {
EntityPool.AddPlayer(); AddPlayer();
var mainCharacters = new List<PedHash> { PedHash.Michael, PedHash.Franklin, PedHash.Franklin02, PedHash.Trevor };
foreach (Ped p in allPeds) foreach (Ped p in allPeds)
{ {
SyncedPed c = EntityPool.GetPedByHandle(p.Handle); if (!PedsByHandle.ContainsKey(p.Handle) && p != Game.Player.Character && !mainCharacters.Contains((PedHash)p.Model.Hash))
if (c==null && (p!=Game.Player.Character))
{ {
if (PedsByID.Count(x => x.Value.IsLocal) > Main.Settings.WorldPedSoftLimit)
{
if (p.PopulationType == EntityPopulationType.RandomAmbient && !p.IsInVehicle())
{
p.Delete();
continue;
}
if (p.PopulationType == EntityPopulationType.RandomScenario) continue;
}
// Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}"); // Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
c=new SyncedPed(p);
EntityPool.Add(c);
Add(new SyncedPed(p));
} }
} }
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks;
#endif #endif
var ps = ID_Peds.Values.ToArray(); var ps = PedsByID.Values.ToArray();
pedStateIndex += pedStatesPerFrame; pedStateIndex += pedStatesPerFrame;
if (pedStateIndex >= ps.Length) if (pedStateIndex >= ps.Length)
{ {
pedStateIndex = 0; pedStateIndex = 0;
} }
foreach (SyncedPed c in ps) foreach (SyncedPed c in ps)
{ {
i++; i++;
if ((c.MainPed != null) && (!c.MainPed.Exists())) if ((c.MainPed != null) && (!c.MainPed.Exists()))
{ {
EntityPool.RemovePed(c.ID, "non-existent"); RemovePed(c.ID, "non-existent");
continue; continue;
} }
@ -446,15 +408,8 @@ namespace RageCoop.Client
// event check // event check
SyncEvents.Check(c); SyncEvents.Check(c);
Networking.SendPed(c); Networking.SendPed(c, (i - pedStateIndex) < pedStatesPerFrame);
// Send state
if ((i-pedStateIndex)<pedStatesPerFrame)
{
Networking.SendPedState(c);
}
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start; Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start;
#endif #endif
} }
@ -469,100 +424,114 @@ namespace RageCoop.Client
RemovePed(c.ID, "OutOfSync"); RemovePed(c.ID, "OutOfSync");
} }
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.UpdatePed]=PerfCounter2.ElapsedTicks-start; Debug.TimeStamps[TimeStamp.UpdatePed]=PerfCounter2.ElapsedTicks-start;
#endif #endif
} }
} }
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
#endif #endif
} }
var check = Main.Ticked % 100 == 0;
i = -1; i = -1;
lock (VehiclesLock) lock (VehiclesLock)
{ {
foreach (Vehicle veh in allVehicles) foreach (Vehicle veh in allVehicles)
{ {
if (!Handle_Vehicles.ContainsKey(veh.Handle)) if (!VehiclesByHandle.ContainsKey(veh.Handle))
{ {
if (VehiclesByID.Count(x => x.Value.IsLocal) > Main.Settings.WorldVehicleSoftLimit)
{
if (veh.PopulationType == EntityPopulationType.RandomAmbient || veh.PopulationType == EntityPopulationType.RandomParked)
{
foreach (var p in veh.Occupants)
{
p.Delete();
var c = GetPedByHandle(p.Handle);
if (c != null)
{
RemovePed(c.ID, "ThrottleTraffic");
}
}
veh.Delete();
continue;
}
}
// Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}"); // Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
EntityPool.Add(new SyncedVehicle(veh)); Add(new SyncedVehicle(veh));
} }
} }
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks;
#endif #endif
var vs = ID_Vehicles.Values.ToArray(); var vs = VehiclesByID.Values.ToArray();
vehStateIndex += vehStatesPerFrame; vehStateIndex += vehStatesPerFrame;
if (vehStateIndex >= vs.Length) if (vehStateIndex >= vs.Length)
{ {
vehStateIndex = 0; vehStateIndex = 0;
} }
foreach (SyncedVehicle v in vs) foreach (SyncedVehicle v in vs)
{ {
i++; i++;
if ((v.MainVehicle != null) && (!v.MainVehicle.Exists())) if ((v.MainVehicle != null) && (!v.MainVehicle.Exists()))
{ {
EntityPool.RemoveVehicle(v.ID,"non-existent"); RemoveVehicle(v.ID, "non-existent");
continue; continue;
} }
if (check)
{
v.SetUpFixedData();
}
// Outgoing sync // Outgoing sync
if (v.IsLocal) if (v.IsLocal)
{ {
if (!v.MainVehicle.IsVisible) { continue; } if (!v.MainVehicle.IsVisible) { continue; }
SyncEvents.Check(v); SyncEvents.Check(v);
Networking.SendVehicle(v); Networking.SendVehicle(v, (i - vehStateIndex) < vehStatesPerFrame);
// Send state
if ((i-vehStateIndex)<vehStatesPerFrame)
{
Networking.SendVehicleState(v);
}
} }
else // Incoming sync else // Incoming sync
{ {
v.Update(); v.Update();
if (v.IsOutOfSync) if (v.IsOutOfSync)
{ {
RemoveVehicle(v.ID, "OutOfSync"); RemoveVehicle(v.ID, "OutOfSync");
} }
} }
} }
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks;
#endif #endif
} }
Networking.Peer.FlushSendQueue();
} }
private static void UpdateTargets()
{
Networking.Targets = new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection };
foreach (var p in PlayerList.Players.Values.ToArray())
{
if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition) < 500)
{
Networking.Targets.Add(p.Connection);
}
}
}
public static void RemoveAllFromPlayer(int playerPedId) public static void RemoveAllFromPlayer(int playerPedId)
{ {
foreach(SyncedPed p in ID_Peds.Values.ToArray()) foreach (SyncedPed p in PedsByID.Values.ToArray())
{ {
if (p.OwnerID == playerPedId) if (p.OwnerID == playerPedId)
{ {
RemovePed(p.ID); RemovePed(p.ID);
} }
} }
foreach (SyncedVehicle v in ID_Vehicles.Values.ToArray())
foreach (SyncedVehicle v in VehiclesByID.Values.ToArray())
{ {
if (v.OwnerID == playerPedId) if (v.OwnerID == playerPedId)
{ {
@ -574,10 +543,7 @@ namespace RageCoop.Client
public static int RequestNewID() public static int RequestNewID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0) || PedsByID.ContainsKey(ID) || VehiclesByID.ContainsKey(ID) || ProjectilesByID.ContainsKey(ID))
|| ID_Peds.ContainsKey(ID)
|| ID_Vehicles.ContainsKey(ID)
|| ID_Projectiles.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -595,36 +561,34 @@ namespace RageCoop.Client
} }
public static string DumpDebug() public static string DumpDebug()
{ {
string s= ""; return $"\nID_Peds: {PedsByID.Count}" +
s+="\nID_Peds: "+ID_Peds.Count; $"\nHandle_Peds: {PedsByHandle.Count}" +
s+="\nHandle_Peds: "+Handle_Peds.Count; $"\nID_Vehicles: {VehiclesByID.Count}" +
s+="\nID_Vehicles: "+ID_Vehicles.Count; $"\nHandle_vehicles: {VehiclesByHandle.Count}" +
s+="\nHandle_Vehicles: "+Handle_Vehicles.Count; $"\nID_Projectiles: {ProjectilesByID.Count}" +
s+="\nID_Projectiles: "+ID_Projectiles.Count; $"\nHandle_Projectiles: {ProjectilesByHandle.Count}" +
s+="\nHandle_Projectiles: "+Handle_Projectiles.Count; $"\npedStatesPerFrame: {pedStatesPerFrame}" +
s+="\npedStatesPerFrame:"+pedStatesPerFrame; $"\nvehStatesPerFrame: {vehStatesPerFrame}";
s+="\nvehStatesPerFrame:"+vehStatesPerFrame;
return s;
} }
public static class ThreadSafe public static class ThreadSafe
{ {
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
lock (EntityPool.VehiclesLock) lock (VehiclesLock)
{ {
EntityPool.Add(v); EntityPool.Add(v);
} }
} }
public static void Add(SyncedPed p) public static void Add(SyncedPed p)
{ {
lock (EntityPool.PedsLock) lock (PedsLock)
{ {
EntityPool.Add(p); EntityPool.Add(p);
} }
} }
public static void Add(SyncedProjectile sp) public static void Add(SyncedProjectile sp)
{ {
lock (EntityPool.ProjectilesLock) lock (ProjectilesLock)
{ {
EntityPool.Add(sp); EntityPool.Add(sp);
} }

View File

@ -1,58 +1,27 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Math; using GTA.Math;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using GTA.Native; using System;
using System.Threading;
namespace RageCoop.Client { namespace RageCoop.Client
{
internal static class SyncEvents internal static class SyncEvents
{ {
#region TRIGGER #region TRIGGER
public static void TriggerPedKilled(SyncedPed victim) public static void TriggerPedKilled(SyncedPed victim)
{ {
Networking.Send(new Packets.PedKilled() { VictimID=victim.ID},ConnectionChannel.SyncEvents); Networking.SendSync(new Packets.PedKilled() { VictimID = victim.ID }, ConnectionChannel.SyncEvents);
} }
public static void TriggerEnteringVehicle(SyncedPed c,SyncedVehicle veh, VehicleSeat seat) public static void TriggerChangeOwner(int vehicleID, int newOwnerID)
{
Networking.
Send(new Packets.EnteringVehicle()
{
PedID=c.ID,
VehicleID= veh.ID,
VehicleSeat=(short)seat,
}, ConnectionChannel.SyncEvents);
}
public static void TriggerEnteredVehicle(SyncedPed c, SyncedVehicle veh, VehicleSeat seat)
{
if (seat==VehicleSeat.Driver)
{
veh.OwnerID=Main.LocalPlayerID;
veh.LastSynced=Main.Ticked;
TriggerChangeOwner(veh, c.ID);
}
Networking.Send(new Packets.EnteredVehicle()
{
VehicleSeat=(short)seat,
PedID=c.ID,
VehicleID=veh.ID
},ConnectionChannel.SyncEvents);
}
public static void TriggerChangeOwner(SyncedVehicle c, int newOwnerID)
{ {
Networking.Send(new Packets.OwnerChanged() Networking.SendSync(new Packets.OwnerChanged()
{ {
ID= c.ID, ID = vehicleID,
NewOwnerID = newOwnerID, NewOwnerID = newOwnerID,
}, ConnectionChannel.SyncEvents); }, ConnectionChannel.SyncEvents, NetDeliveryMethod.ReliableOrdered);
} }
@ -68,36 +37,33 @@ namespace RageCoop.Client {
// Reduce latency // Reduce latency
start = impactPosition - (impactPosition - start).Normalized * 10; start = impactPosition - (impactPosition - start).Normalized * 10;
} }
Networking.SendBulletShot(start, impactPosition, hash, owner.ID); Networking.SendBullet(start, impactPosition, hash, owner.ID);
}
public static void TriggerLeaveVehicle(int id)
{
Networking.
Send(new Packets.LeaveVehicle()
{
ID=id
}, ConnectionChannel.SyncEvents);
} }
public static void TriggerVehBulletShot(uint hash, Vehicle veh, SyncedPed owner) public static void TriggerVehBulletShot(uint hash, Vehicle veh, SyncedPed owner)
{ {
int i;
// ANNIHL // ANNIHL
if (veh.Model.Hash == 837858166) if (veh.Model.Hash == 837858166)
{ {
Networking.SendBulletShot(veh.Bones[35].Position, veh.Bones[35].Position+veh.Bones[35].ForwardVector,hash,owner.ID); Networking.SendVehicleBullet(hash, owner, veh.Bones[35]);
Networking.SendBulletShot(veh.Bones[36].Position, veh.Bones[36].Position+veh.Bones[36].ForwardVector,hash, owner.ID); Networking.SendVehicleBullet(hash, owner, veh.Bones[36]);
Networking.SendBulletShot(veh.Bones[37].Position, veh.Bones[37].Position+veh.Bones[37].ForwardVector,hash, owner.ID); Networking.SendVehicleBullet(hash, owner, veh.Bones[37]);
Networking.SendBulletShot(veh.Bones[38].Position, veh.Bones[38].Position+veh.Bones[38].ForwardVector,hash, owner.ID); Networking.SendVehicleBullet(hash, owner, veh.Bones[38]);
return; }
else if ((i = veh.GetMuzzleIndex()) != -1)
{
Networking.SendVehicleBullet(hash, owner, veh.Bones[i]);
}
else
{
Main.Logger.Warning($"Failed to get muzzle info for vehicle:{veh.DisplayName}");
} }
var info = veh.GetMuzzleInfo();
if (info==null) { Main.Logger.Warning($"Failed to get muzzle info for vehicle:{veh.DisplayName}");return; }
Networking.SendBulletShot(info.Position,info.Position+info.ForawardVector,hash,owner.ID);
} }
public static void TriggerNozzleTransform(int vehID, bool hover) public static void TriggerNozzleTransform(int vehID, bool hover)
{ {
Networking.Send(new Packets.NozzleTransform() { VehicleID=vehID, Hover=hover }, ConnectionChannel.SyncEvents); Networking.SendSync(new Packets.NozzleTransform() { VehicleID = vehID, Hover = hover }, ConnectionChannel.SyncEvents);
} }
#endregion #endregion
@ -105,58 +71,21 @@ namespace RageCoop.Client {
#region HANDLE #region HANDLE
public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core"); public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core");
private static WeaponAsset _weaponAsset = default;
private static uint _lastWeaponHash;
static WeaponAsset _weaponAsset = default;
static uint _lastWeaponHash;
private static void HandleLeaveVehicle(Packets.LeaveVehicle p)
{
var ped = EntityPool.GetPedByID(p.ID);
var veh = ped.MainPed.CurrentVehicle.GetSyncEntity();
veh._checkSeat=false;
var flag = LeaveVehicleFlags.None;
if (ped.MainPed?.CurrentVehicle==null) { return; }
// Bail out
if (ped.MainPed.CurrentVehicle.Speed>5) { flag|=LeaveVehicleFlags.BailOut;}
// ped.PauseUpdate((ulong)Game.FPS*2);
ped.MainPed.Task.LeaveVehicle(flag) ;
Task.Run(() =>
{
Thread.Sleep(1000);
veh._checkSeat=true;
});
}
private static void HandlePedKilled(Packets.PedKilled p) private static void HandlePedKilled(Packets.PedKilled p)
{ {
EntityPool.GetPedByID(p.VictimID)?.MainPed?.Kill(); EntityPool.GetPedByID(p.VictimID)?.MainPed?.Kill();
} }
private static void HandleEnteringVehicle(SyncedPed c, SyncedVehicle veh, VehicleSeat seat)
{
c.MainPed?.Task.EnterVehicle(veh.MainVehicle, seat,-1,2,EnterVehicleFlags.WarpToDoor|EnterVehicleFlags.AllowJacking);
}
private static void HandleEnteredVehicle(int pedId, int vehId, VehicleSeat seat)
{
var v = EntityPool.GetVehicleByID(vehId);
var p = EntityPool.GetPedByID(pedId)?.MainPed;
if (v==null||p==null) { return; }
if (!v.MainVehicle.IsSeatFree(seat))
{
if (v.MainVehicle.GetPedOnSeat(seat)!=p)
{
v.MainVehicle.GetPedOnSeat(seat).Task.WarpOutOfVehicle(v.MainVehicle);
}
}
p.SetIntoVehicle(v.MainVehicle, seat);
}
private static void HandleOwnerChanged(Packets.OwnerChanged p) private static void HandleOwnerChanged(Packets.OwnerChanged p)
{ {
var v = EntityPool.GetVehicleByID(p.ID); var v = EntityPool.GetVehicleByID(p.ID);
if (v == null) { return; } if (v == null) { return; }
v.OwnerID = p.NewOwnerID; v.OwnerID = p.NewOwnerID;
v.ModelHash=v.MainVehicle.Model;
v.LastSynced = Main.Ticked; v.LastSynced = Main.Ticked;
// So this vehicle doesn's get re-spawned v.Position = v.MainVehicle.Position;
v.Quaternion = v.MainVehicle.Quaternion;
} }
private static void HandleNozzleTransform(Packets.NozzleTransform p) private static void HandleNozzleTransform(Packets.NozzleTransform p)
{ {
@ -197,7 +126,7 @@ namespace RageCoop.Client {
} }
var p = EntityPool.GetPedByID(ownerID)?.MainPed; var p = EntityPool.GetPedByID(ownerID)?.MainPed;
if (p == null) { p=Game.Player.Character; Main.Logger.Warning("Failed to find owner for bullet"); } if (p == null) { return; /* p = Game.Player.Character; Main.Logger.Warning("Failed to find owner for bullet"); */ }
if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); } if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); }
if (_lastWeaponHash != weaponHash) if (_lastWeaponHash != weaponHash)
{ {
@ -216,74 +145,53 @@ namespace RageCoop.Client {
} }
else else
{ {
World.CreateParticleEffectNonLooped(CorePFXAsset, "muz_assault_rifle", p.GetMuzzlePosition(), w.Rotation, 1); World.CreateParticleEffectNonLooped(CorePFXAsset, WeaponUtil.GetFlashFX((WeaponHash)weaponHash), p.GetMuzzlePosition(), w.Rotation, 1);
} }
} }
else if (p.VehicleWeapon!=VehicleWeaponHash.Invalid) }
public static void HandleVehicleBulletShot(Packets.VehicleBulletShot p)
{ {
if (p.VehicleWeapon==VehicleWeaponHash.Tank) HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
{ var v = EntityPool.GetPedByID(p.OwnerID)?.MainPed.CurrentVehicle;
World.CreateParticleEffectNonLooped(CorePFXAsset, "muz_tank", p.CurrentVehicle.GetMuzzleInfo().Position, p.CurrentVehicle.Bones[35].ForwardVector.ToEulerRotation(p.CurrentVehicle.Bones[35].UpVector), 1); if (v == null) { return; }
var b = v.Bones[p.Bone];
World.CreateParticleEffectNonLooped(CorePFXAsset,
WeaponUtil.GetFlashFX((WeaponHash)p.WeaponHash),
b.Position, b.ForwardVector.ToEulerRotation(v.Bones[35].UpVector), 1);
} }
} public static void HandleEvent(PacketType type, NetIncomingMessage msg)
}
public static void HandleEvent(PacketType type,byte[] data)
{ {
switch (type) switch (type)
{ {
case PacketType.BulletShot: case PacketType.BulletShot:
{ {
Packets.BulletShot p = new Packets.BulletShot(); Packets.BulletShot p = new Packets.BulletShot();
p.Unpack(data); p.Deserialize(msg);
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID); HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
break; break;
} }
case PacketType.EnteringVehicle: case PacketType.VehicleBulletShot:
{ {
Packets.EnteringVehicle p = new Packets.EnteringVehicle(); HandleVehicleBulletShot(msg.GetPacket<Packets.VehicleBulletShot>());
p.Unpack(data);
HandleEnteringVehicle(EntityPool.GetPedByID(p.PedID), EntityPool.GetVehicleByID(p.VehicleID), (VehicleSeat)p.VehicleSeat);
}
break; break;
case PacketType.LeaveVehicle:
{
Packets.LeaveVehicle packet = new Packets.LeaveVehicle();
packet.Unpack(data);
HandleLeaveVehicle(packet);
} }
break;
case PacketType.OwnerChanged: case PacketType.OwnerChanged:
{ {
Packets.OwnerChanged packet = new Packets.OwnerChanged(); HandleOwnerChanged(msg.GetPacket<Packets.OwnerChanged>());
packet.Unpack(data);
HandleOwnerChanged(packet);
} }
break; break;
case PacketType.PedKilled: case PacketType.PedKilled:
{ {
var packet = new Packets.PedKilled(); HandlePedKilled(msg.GetPacket<Packets.PedKilled>());
packet.Unpack(data);
HandlePedKilled(packet);
} }
break; break;
case PacketType.EnteredVehicle:
{
var packet = new Packets.EnteredVehicle();
packet.Unpack(data);
HandleEnteredVehicle(packet.PedID,packet.VehicleID,(VehicleSeat)packet.VehicleSeat);
break;
}
case PacketType.NozzleTransform: case PacketType.NozzleTransform:
{ {
var packet = new Packets.NozzleTransform(); HandleNozzleTransform(msg.GetPacket<Packets.NozzleTransform>());
packet.Unpack(data);
HandleNozzleTransform(packet);
break; break;
} }
} }
Networking.Peer.Recycle(msg);
} }
#endregion #endregion
@ -306,14 +214,14 @@ namespace RageCoop.Client {
Vector3 endPos = subject.LastWeaponImpactPosition; Vector3 endPos = subject.LastWeaponImpactPosition;
if (endPos == default) if (endPos == default)
{ {
if (i>5) if (++i <= 5) { return false; }
{
endPos = subject.GetAimCoord(); endPos = subject.GetAimCoord();
if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid) if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid)
{ {
if (subject.IsOnTurretSeat()) if (subject.IsOnTurretSeat())
{ {
TriggerBulletShot((uint)subject.VehicleWeapon, subject.GetSyncEntity(), endPos); TriggerBulletShot((uint)subject.VehicleWeapon, c, endPos);
} }
else else
{ {
@ -322,20 +230,16 @@ namespace RageCoop.Client {
} }
else else
{ {
TriggerBulletShot((uint)subject.Weapons.Current.Hash, subject.GetSyncEntity(), endPos); TriggerBulletShot((uint)subject.Weapons.Current.Hash, c, endPos);
} }
return true; return true;
} }
i++;
return false;
}
else
{
if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid) if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid)
{ {
if (subject.IsOnTurretSeat()) if (subject.IsOnTurretSeat())
{ {
TriggerBulletShot((uint)subject.VehicleWeapon, subject.GetSyncEntity(), endPos); TriggerBulletShot((uint)subject.VehicleWeapon, c, endPos);
} }
else else
{ {
@ -344,13 +248,12 @@ namespace RageCoop.Client {
} }
else else
{ {
TriggerBulletShot((uint)subject.Weapons.Current.Hash, subject.GetSyncEntity(), endPos); TriggerBulletShot((uint)subject.Weapons.Current.Hash, c, endPos);
} }
return true; return true;
}
}); });
if (!getBulletImpact()) if (!getBulletImpact())
{ {
Main.QueueAction(getBulletImpact); Main.QueueAction(getBulletImpact);
@ -358,44 +261,18 @@ namespace RageCoop.Client {
} }
else if (subject.VehicleWeapon == VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition != default) else if (subject.VehicleWeapon == VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition != default)
{ {
TriggerBulletShot((uint)VehicleWeaponHash.Tank, subject.GetSyncEntity(),subject.LastWeaponImpactPosition); TriggerBulletShot((uint)VehicleWeaponHash.Tank, c, subject.LastWeaponImpactPosition);
}
}
// Vehicles
var g = subject.IsGettingIntoVehicle;
if ( g && (!c._lastEnteringVehicle))
{
var v = subject.VehicleTryingToEnter.GetSyncEntity();
TriggerEnteringVehicle(c, v, subject.GetSeatTryingToEnter());
}
var currentSitting= subject.IsSittingInVehicle();
if (c._lastSittingInVehicle)
{
if (!currentSitting)
{
var veh = subject.CurrentVehicle;
if (veh!=null)
{
var v = veh.GetSyncEntity();
TriggerLeaveVehicle(c.ID);
} }
} }
} }
else if (currentSitting)
{
TriggerEnteredVehicle(c, subject.CurrentVehicle.GetSyncEntity(), subject.SeatIndex);
}
c._lastSittingInVehicle=currentSitting;
c._lastEnteringVehicle=g;
}
public static void Check(SyncedVehicle v) public static void Check(SyncedVehicle v)
{ {
if (v.MainVehicle!=null&&v.MainVehicle.HasNozzle()) if (v.MainVehicle == null || !v.MainVehicle.HasNozzle())
{ {
return;
}
if ((v.LastNozzleAngle == 1) && (v.MainVehicle.GetNozzleAngel() != 1)) if ((v.LastNozzleAngle == 1) && (v.MainVehicle.GetNozzleAngel() != 1))
{ {
TriggerNozzleTransform(v.ID, false); TriggerNozzleTransform(v.ID, false);
@ -406,7 +283,6 @@ namespace RageCoop.Client {
} }
v.LastNozzleAngle = v.MainVehicle.GetNozzleAngel(); v.LastNozzleAngle = v.MainVehicle.GetNozzleAngel();
} }
}
#endregion #endregion
} }
} }

View File

@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client
{
internal class SyncParameters
{
public static float PositioinPredictionDefault = 0.01f;
}
}

View File

@ -0,0 +1,93 @@
using NAudio.Wave;
using System.Threading;
namespace RageCoop.Client
{
internal static class Voice
{
private static WaveInEvent _waveIn;
private static readonly BufferedWaveProvider _waveProvider = new BufferedWaveProvider(new WaveFormat(16000, 16, 1));
private static Thread _thread;
public static bool WasInitialized() => _thread != null;
public static bool IsRecording() => _waveIn != null;
public static void ClearAll()
{
_waveProvider.ClearBuffer();
StopRecording();
if (_thread != null && _thread.IsAlive)
{
_thread.Abort();
_thread = null;
}
}
public static void StopRecording()
{
if (!IsRecording())
return;
_waveIn.StopRecording();
_waveIn.Dispose();
_waveIn = null;
}
public static void Init()
{
if (WasInitialized())
return;
// I tried without thread but the game will lag without
_thread = new Thread(new ThreadStart(() =>
{
while (true)
{
using (var wo = new WaveOutEvent())
{
wo.Init(_waveProvider);
wo.Play();
while (wo.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(100);
}
}
}
}));
_thread.Start();
}
public static void StartRecording()
{
if (IsRecording())
return;
_waveIn = new WaveInEvent
{
DeviceNumber = 0,
BufferMilliseconds = 20,
NumberOfBuffers = 1,
WaveFormat = _waveProvider.WaveFormat
};
_waveIn.DataAvailable += WaveInDataAvailable;
_waveIn.StartRecording();
}
public static void AddVoiceData(byte[] buffer, int recorded)
{
_waveProvider.AddSamples(buffer, 0, recorded);
}
private static void WaveInDataAvailable(object sender, WaveInEventArgs e)
{
if (!IsRecording())
return;
Networking.SendVoiceMessage(e.Buffer, e.BytesRecorded);
}
}
}

View File

@ -0,0 +1,64 @@
using GTA;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client
{
/// <summary>
/// Class providing support for addon mods
/// </summary>
internal class AddOnDataProvider
{
public static int GetMuzzleIndex(Model model)
{
switch (model.Hash)
{
// f14a2
case -848721350:
return 48;
// f15e
case 881261972:
return 32;
// f16c
case -2051171080:
return 25;
// F22A
case 2061630439:
return 14;
// f35c
case -343547392:
return 44;
// mig29a
case 513887552:
return 18;
// su30sm
case -733985185:
return 34;
// su33
case -722216722:
return 34;
// su35s
case -268602544:
return 28;
// su57
case 1490050781:
return 21;
default:
return -1;
}
}
}
}

View File

@ -0,0 +1,103 @@

using GTA;
using GTA.Math;
using RageCoop.Core;
using SHVDN;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace RageCoop.Client
{
internal unsafe class MemPatch
{
private readonly byte[] _data;
private readonly byte[] _orginal;
private readonly IntPtr _address;
public MemPatch(byte* address, byte[] data)
{
_data = data;
_orginal = new byte[data.Length];
_address = (IntPtr)address;
Marshal.Copy((IntPtr)address, _orginal, 0, data.Length);
}
public void Install()
{
Marshal.Copy(_data, 0, _address, _data.Length);
}
public void Uninstall()
{
Marshal.Copy(_orginal, 0, _address, _orginal.Length);
}
}
internal static unsafe class Memory
{
public static MemPatch VignettingPatch;
public static MemPatch VignettingCallPatch;
public static MemPatch TimeScalePatch;
static Memory()
{
// Weapon/radio wheel slow-mo patch
// Thanks @CamxxCore, https://github.com/CamxxCore/GTAVWeaponWheelMod
var result = NativeMemory.FindPattern("\x38\x51\x64\x74\x19", "xxxxx");
if (result == null) { throw new NotSupportedException("Can't find memory pattern to patch weapon/radio slow-mo"); }
var address = result + 26;
address = address + *(int*)address + 4u;
VignettingPatch = new MemPatch(address, new byte[] { RET, 0x90, 0x90, 0x90, 0x90 });
VignettingCallPatch = new MemPatch(result + 8, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90 });
TimeScalePatch = new MemPatch(result + 34, new byte[] { XOR_32_64, 0xD2 });
}
public static void ApplyPatches()
{
VignettingPatch.Install();
VignettingCallPatch.Install();
TimeScalePatch.Install();
}
public static void RestorePatches()
{
VignettingPatch.Uninstall();
VignettingCallPatch.Uninstall();
TimeScalePatch.Uninstall();
}
#region PATCHES
#endregion
#region OFFSET-CONST
public const int PositionOffset = 144;
public const int VelocityOffset = 800;
public const int MatrixOffset = 96;
#endregion
#region OPCODE
private const byte XOR_32_64 = 0x31;
private const byte RET = 0xC3;
#endregion
public static Vector3 ReadPosition(this Entity e) => ReadVector3(e.MemoryAddress + PositionOffset);
public static Quaternion ReadQuaternion(this Entity e) => Quaternion.RotationMatrix(e.Matrix);
public static Vector3 ReadRotation(this Entity e) => e.ReadQuaternion().ToEulerDegrees();
public static Vector3 ReadVelocity(this Ped e) => ReadVector3(e.MemoryAddress + VelocityOffset);
public static Vector3 ReadVector3(IntPtr address)
{
float* ptr = (float*)address.ToPointer();
return new Vector3()
{
X = *ptr,
Y = ptr[1],
Z = ptr[2]
};
}
public static List<int> FindOffset(float toSearch, IntPtr start, int range = 1000, float tolerance = 0.01f)
{
var foundOffsets = new List<int>(100);
for (int i = 0; i <= range; i++)
{
var val = NativeMemory.ReadFloat(start + i);
if (Math.Abs(val - toSearch) < tolerance)
{
foundOffsets.Add(i);
}
}
return foundOffsets;
}
}
}

View File

@ -0,0 +1,145 @@
using GTA.Math;
using GTA.Native;
using System;
using System.Runtime.InteropServices;
namespace RageCoop.Client
{
[StructLayout(LayoutKind.Explicit, Size = 80)]
public struct HeadBlendData
{
[FieldOffset(0)]
public int ShapeFirst;
[FieldOffset(8)]
public int ShapeSecond;
[FieldOffset(16)]
public int ShapeThird;
[FieldOffset(24)]
public int SkinFirst;
[FieldOffset(32)]
public int SkinSecond;
[FieldOffset(40)]
public int SkinThird;
[FieldOffset(48)]
public float ShapeMix;
[FieldOffset(56)]
public float SkinMix;
[FieldOffset(64)]
public float ThirdMix;
}
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct NativeVector3
{
[FieldOffset(0)]
public float X;
[FieldOffset(8)]
public float Y;
[FieldOffset(16)]
public float Z;
public static implicit operator Vector3(NativeVector3 vec)
{
return new Vector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
}
public static implicit operator NativeVector3(Vector3 vec)
{
return new NativeVector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
}
}
public static class NativeCaller
{
// These are borrowed from ScriptHookVDotNet's
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")]
private static extern void NativeInit(ulong hash);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")]
private static extern void NativePush64(ulong val);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")]
private static extern unsafe ulong* NativeCall();
// These are from ScriptHookV's nativeCaller.h
private static unsafe void NativePush<T>(T val) where T : unmanaged
{
ulong val64 = 0;
*(T*)(&val64) = val;
NativePush64(val64);
}
public static unsafe R Invoke<R>(ulong hash) where R : unmanaged
{
NativeInit(hash);
return *(R*)(NativeCall());
}
public static unsafe R Invoke<R>(Hash hash, params object[] args)
where R : unmanaged
{
NativeInit((ulong)hash);
var arguments = ConvertPrimitiveArguments(args);
foreach (var arg in arguments)
NativePush(arg);
return *(R*)(NativeCall());
}
/// <summary>
/// Helper function that converts an array of primitive values to a native stack.
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private static unsafe ulong[] ConvertPrimitiveArguments(object[] args)
{
var result = new ulong[args.Length];
for (int i = 0; i < args.Length; ++i)
{
if (args[i] is bool valueBool)
{
result[i] = valueBool ? 1ul : 0ul;
continue;
}
if (args[i] is byte valueByte)
{
result[i] = valueByte;
continue;
}
if (args[i] is int valueInt32)
{
result[i] = (ulong)valueInt32;
continue;
}
if (args[i] is ulong valueUInt64)
{
result[i] = valueUInt64;
continue;
}
if (args[i] is float valueFloat)
{
result[i] = *(ulong*)&valueFloat;
continue;
}
if (args[i] is IntPtr valueIntPtr)
{
result[i] = (ulong)valueIntPtr.ToInt64();
continue;
}
throw new ArgumentException("Unknown primitive type in native argument list", nameof(args));
}
return result;
}
}
}

View File

@ -1,10 +1,4 @@
using System; namespace RageCoop.Client
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client
{ {
// Potential names and hash collisions included as comments // Potential names and hash collisions included as comments
internal enum PedConfigFlags internal enum PedConfigFlags

View File

@ -1,14 +1,9 @@
using System; using GTA;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using RageCoop.Core;
using GTA;
using GTA.Native;
using GTA.Math; using GTA.Math;
using System.Linq; using GTA.Native;
using System.Diagnostics; using RageCoop.Core;
using System;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -49,18 +44,32 @@ namespace RageCoop.Client
public static byte GetPedSpeed(this Ped ped) public static byte GetPedSpeed(this Ped ped)
{ {
if (ped.IsSprinting)
if (ped.IsSittingInVehicle())
{ {
return 3; return 4;
} }
if (ped.IsRunning) if (ped.IsTaskActive(TaskType.CTaskEnterVehicle))
{ {
return 2; return 5;
}
if (ped.IsTaskActive(TaskType.CTaskExitVehicle))
{
return 6;
} }
if (ped.IsWalking) if (ped.IsWalking)
{ {
return 1; return 1;
} }
if (ped.IsRunning)
{
return 2;
}
if (ped.IsSprinting)
{
return 3;
}
return 0; return 0;
} }
@ -133,6 +142,23 @@ namespace RageCoop.Client
if (ped.IsInCover || ped.IsGoingIntoCover) if (ped.IsInCover || ped.IsGoingIntoCover)
{ {
flags |= PedDataFlags.IsInCover; flags |= PedDataFlags.IsInCover;
if (ped.IsInCoverFacingLeft)
{
flags |= PedDataFlags.IsInCover;
}
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped))
{
flags |= PedDataFlags.IsInLowCover;
}
if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire))
{
flags |= PedDataFlags.IsBlindFiring;
}
}
if (ped.IsInvincible)
{
flags |= PedDataFlags.IsInvincible;
} }
if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped)) if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
@ -140,6 +166,7 @@ namespace RageCoop.Client
flags |= PedDataFlags.IsInStealthMode; flags |= PedDataFlags.IsInStealthMode;
} }
return flags; return flags;
} }
@ -269,7 +296,7 @@ namespace RageCoop.Client
{ {
result = true; result = true;
} }
else else if (veh.GetPedOnSeat(seat) != null)
{ {
bool isDead = veh.GetPedOnSeat(seat).IsDead; bool isDead = veh.GetPedOnSeat(seat).IsDead;
@ -320,22 +347,20 @@ namespace RageCoop.Client
} }
public static Vector3 GetLookingCoord(this Ped p) public static Vector3 GetLookingCoord(this Ped p)
{ {
if (p == Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
{
return RaycastEverything(default);
}
EntityBone b = p.Bones[Bone.FacialForehead]; EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized; Vector3 v = b.UpVector.Normalized;
return b.Position + 200 * v; return b.Position + 200 * v;
} }
public static void StayInCover(this Ped p)
{
Function.Call(Hash.TASK_STAY_IN_COVER, p);
}
public static VehicleSeat GetSeatTryingToEnter(this Ped p) public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{ {
return (VehicleSeat)Function.Call<int>(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p); return (VehicleSeat)Function.Call<int>(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p);
} }
#endregion #endregion
@ -344,6 +369,93 @@ namespace RageCoop.Client
public static Vector3 RaycastEverything(Vector2 screenCoord)
{
Vector3 camPos = GameplayCamera.Position;
Vector3 camRot = GameplayCamera.Rotation;
const float raycastToDist = 100.0f;
const float raycastFromDist = 1f;
Vector3 target3D = ScreenRelToWorld(camPos, camRot, screenCoord);
Vector3 source3D = camPos;
Entity ignoreEntity = Game.Player.Character;
if (Game.Player.Character.IsInVehicle())
{
ignoreEntity = Game.Player.Character.CurrentVehicle;
}
Vector3 dir = target3D - source3D;
dir.Normalize();
RaycastResult raycastResults = World.Raycast(source3D + dir * raycastFromDist,
source3D + dir * raycastToDist,
IntersectFlags.Everything,
ignoreEntity);
if (raycastResults.DidHit)
{
return raycastResults.HitPosition;
}
return camPos + dir * raycastToDist;
}
public static Vector3 ScreenRelToWorld(Vector3 camPos, Vector3 camRot, Vector2 coord)
{
Vector3 camForward = camRot.ToDirection();
Vector3 rotUp = camRot + new Vector3(10, 0, 0);
Vector3 rotDown = camRot + new Vector3(-10, 0, 0);
Vector3 rotLeft = camRot + new Vector3(0, 0, -10);
Vector3 rotRight = camRot + new Vector3(0, 0, 10);
Vector3 camRight = rotRight.ToDirection() - rotLeft.ToDirection();
Vector3 camUp = rotUp.ToDirection() - rotDown.ToDirection();
double rollRad = -camRot.Y.ToRadians();
Vector3 camRightRoll = camRight * (float)Math.Cos(rollRad) - camUp * (float)Math.Sin(rollRad);
Vector3 camUpRoll = camRight * (float)Math.Sin(rollRad) + camUp * (float)Math.Cos(rollRad);
Vector3 point3D = camPos + camForward * 10.0f + camRightRoll + camUpRoll;
if (!WorldToScreenRel(point3D, out Vector2 point2D))
{
return camPos + camForward * 10.0f;
}
Vector3 point3DZero = camPos + camForward * 10.0f;
if (!WorldToScreenRel(point3DZero, out Vector2 point2DZero))
{
return camPos + camForward * 10.0f;
}
const double eps = 0.001;
if (Math.Abs(point2D.X - point2DZero.X) < eps || Math.Abs(point2D.Y - point2DZero.Y) < eps)
{
return camPos + camForward * 10.0f;
}
float scaleX = (coord.X - point2DZero.X) / (point2D.X - point2DZero.X);
float scaleY = (coord.Y - point2DZero.Y) / (point2D.Y - point2DZero.Y);
return camPos + camForward * 10.0f + camRightRoll * scaleX + camUpRoll * scaleY;
}
public static bool WorldToScreenRel(Vector3 worldCoords, out Vector2 screenCoords)
{
OutputArgument num1 = new OutputArgument();
OutputArgument num2 = new OutputArgument();
if (!Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, worldCoords.X, worldCoords.Y, worldCoords.Z, num1, num2))
{
screenCoords = new Vector2();
return false;
}
screenCoords = new Vector2((num1.GetResult<float>() - 0.5f) * 2, (num2.GetResult<float>() - 0.5f) * 2);
return true;
}
public static void StayInCover(this Ped p)
{
Function.Call(Hash.TASK_STAY_IN_COVER, p);
}
public static bool IsTurretSeat(this Vehicle veh, int seat) public static bool IsTurretSeat(this Vehicle veh, int seat)
{ {

View File

@ -1,21 +1,52 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GTA.Math; using GTA.Math;
using GTA;
using RageCoop.Core;
using GTA.Native; using GTA.Native;
using RageCoop.Core;
using System;
using System.Drawing;
using System.IO; using System.IO;
using System.Xml.Serialization; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Windows.Forms;
using System.Xml.Serialization;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static class Util internal static class Util
{ {
public static SizeF ResolutionMaintainRatio
{
get
{
// Get the game width and height
int screenw = GTA.UI.Screen.Resolution.Width;
int screenh = GTA.UI.Screen.Resolution.Height;
// Calculate the ratio
float ratio = (float)screenw / screenh;
// And the width with that ratio
float width = 1080f * ratio;
// Finally, return a SizeF
return new SizeF(width, 1080f);
}
}
public static bool WorldToScreen(Vector3 pos, ref Point screenPos)
{
float x, y;
unsafe
{
var res = ResolutionMaintainRatio;
if (Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y))
{
screenPos = new Point((int)(res.Width * x), (int)(y * 1080));
return true;
}
}
return false;
}
#region -- POINTER -- #region -- POINTER --
private static int _steeringAngleOffset { get; set; } private static int _steeringAngleOffset { get; set; }
@ -29,17 +60,6 @@ namespace RageCoop.Client
_steeringAngleOffset = *(int*)(address + 6) + 8; _steeringAngleOffset = *(int*)(address + 6) + 8;
} }
// breaks some stuff.
/*
address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != IntPtr.Zero)
{
for (int i = 0; i < 6; i++)
{
*(byte*)(address + i).ToPointer() = 0x90;
}
}
*/
} }
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value) public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
@ -91,11 +111,11 @@ namespace RageCoop.Client
#endregion #endregion
public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml"; public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml";
public static Settings ReadSettings() public static Settings ReadSettings(string path = null)
{ {
path = path ?? SettingsPath;
XmlSerializer ser = new XmlSerializer(typeof(Settings)); XmlSerializer ser = new XmlSerializer(typeof(Settings));
string path = SettingsPath;
Directory.CreateDirectory(Directory.GetParent(path).FullName); Directory.CreateDirectory(Directory.GetParent(path).FullName);
Settings settings = null; Settings settings = null;
@ -103,12 +123,7 @@ namespace RageCoop.Client
{ {
using (FileStream stream = File.OpenRead(path)) using (FileStream stream = File.OpenRead(path))
{ {
settings = (RageCoop.Client.Settings)ser.Deserialize(stream); settings = (Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
} }
} }
else else
@ -121,46 +136,38 @@ namespace RageCoop.Client
return settings; return settings;
} }
public static void SaveSettings() public static bool SaveSettings(string path = null, Settings settings = null)
{ {
try try
{ {
string path = SettingsPath ; path = path ?? SettingsPath;
settings = settings ?? Main.Settings;
Directory.CreateDirectory(Directory.GetParent(path).FullName); Directory.CreateDirectory(Directory.GetParent(path).FullName);
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite)) using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{ {
XmlSerializer ser = new XmlSerializer(typeof(Settings)); XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings); ser.Serialize(stream, settings);
} }
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
GTA.UI.Notification.Show("Error saving player settings: " + ex.Message); return false;
// GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
} }
} }
public static Vector3 PredictPosition(this Entity e, bool applyDefault = true)
public static Vehicle CreateVehicle(Model model, Vector3 position, float heading = 0f)
{ {
return e.Position+e.Velocity*((applyDefault ? SyncParameters.PositioinPredictionDefault : 0)+Networking.Latency); if (!model.IsLoaded) { return null; }
return (Vehicle)Entity.FromHandle(Function.Call<int>(Hash.CREATE_VEHICLE, model.Hash, position.X, position.Y, position.Z, heading, false, false));
} }
public static Ped CreatePed(Model model, Vector3 position, float heading = 0f)
public static Model ModelRequest(this int hash)
{ {
Model model = new Model(hash); if (!model.IsLoaded) { return null; }
return (Ped)Entity.FromHandle(Function.Call<int>(Hash.CREATE_PED, 26, model.Hash, position.X, position.Y, position.Z, heading, false, false));
if (!model.IsValid)
{
//GTA.UI.Notification.Show("~y~Not valid!");
return null;
}
if (!model.IsLoaded)
{
return model.Request(1000) ? model : null;
}
return model;
} }
public static void SetOnFire(this Entity e, bool toggle) public static void SetOnFire(this Entity e, bool toggle)
{ {
@ -194,6 +201,10 @@ namespace RageCoop.Client
return v; return v;
} }
public static void ApplyForce(this Entity e, int boneIndex, Vector3 direction, Vector3 rotation = default(Vector3), ForceType forceType = ForceType.MaxForceRot2)
{
Function.Call(Hash.APPLY_FORCE_TO_ENTITY, e.Handle, forceType, direction.X, direction.Y, direction.Z, rotation.X, rotation.Y, rotation.Z, boneIndex, false, true, true, false, true);
}
public static byte GetPlayerRadioIndex() public static byte GetPlayerRadioIndex()
{ {
return (byte)Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX); return (byte)Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX);
@ -202,14 +213,10 @@ namespace RageCoop.Client
{ {
Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index); Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index);
} }
public static byte[] GetHash(this string inputString)
{
using (HashAlgorithm algorithm = SHA256.Create())
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
}
#region WIN32
const UInt32 WM_KEYDOWN = 0x0100; private const UInt32 WM_KEYDOWN = 0x0100;
public static void Reload() public static void Reload()
{ {
string reloadKey = "None"; string reloadKey = "None";
@ -228,6 +235,7 @@ namespace RageCoop.Client
foreach (var l in lines) foreach (var l in lines)
{ {
var ss = l.Split('='); var ss = l.Split('=');
ss.ForEach(s => s.Replace(" ", ""));
if (ss.Length > 0 && ss[0] == "ReloadKey") if (ss.Length > 0 && ss[0] == "ReloadKey")
{ {
reloadKey = ss[1]; reloadKey = ss[1];
@ -236,16 +244,33 @@ namespace RageCoop.Client
} }
lineList.Add("ReloadKey=Insert"); lineList.Add("ReloadKey=Insert");
File.WriteAllLines("ScriptHookVDotNet.ini", lineList.ToArray()); File.WriteAllLines("ScriptHookVDotNet.ini", lineList.ToArray());
GTA.UI.Notification.Show("Reload cannot be performed automatically, please type \"Reload()\" manually in the SHVDN console.");
} }
Keys key = (Keys)Enum.Parse(typeof(Keys), reloadKey, true); Keys key = (Keys)Enum.Parse(typeof(Keys), reloadKey, true);
// Move log file so it doesn't get deleted
Main.Logger.Dispose();
var path = Main.Logger.LogPath + ".last.log";
try
{
if (File.Exists(path)) { File.Delete(path); }
if (File.Exists(Main.Logger.LogPath)) { File.Move(Main.Logger.LogPath, path); }
}
catch (Exception ex)
{
GTA.UI.Notification.Show(ex.Message);
}
PostMessage(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, WM_KEYDOWN, (int)key, 0); PostMessage(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, WM_KEYDOWN, (int)key, 0);
} }
[DllImport("user32.dll")] [DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam); private static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
public static extern ulong GetTickCount64(); public static extern ulong GetTickCount64();
#endregion
} }
} }

View File

@ -1,12 +1,8 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native; using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using GTA.Math; using System;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -14,8 +10,9 @@ namespace RageCoop.Client
{ {
#region VEHICLE #region VEHICLE
public static VehicleDataFlags GetVehicleFlags(this Vehicle veh) public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v)
{ {
var veh = v.MainVehicle;
VehicleDataFlags flags = 0; VehicleDataFlags flags = 0;
if (veh.IsEngineRunning) if (veh.IsEngineRunning)
@ -53,34 +50,59 @@ namespace RageCoop.Client
flags |= VehicleDataFlags.IsHornActive; flags |= VehicleDataFlags.IsHornActive;
} }
if (veh.IsSubmarineCar && Function.Call<bool>(Hash._GET_IS_SUBMARINE_VEHICLE_TRANSFORMED, veh.Handle)) if (v.IsSubmarineCar && Function.Call<bool>(Hash.IS_VEHICLE_IN_SUBMARINE_MODE, veh.Handle))
{ {
flags |= VehicleDataFlags.IsTransformed; flags |= VehicleDataFlags.IsTransformed;
} }
if (veh.HasRoof && (veh.RoofState == VehicleRoofState.Opened || veh.RoofState == VehicleRoofState.Opening)) if (v.IsAircraft)
{
flags |= VehicleDataFlags.RoofOpened;
}
if (veh.IsAircraft)
{ {
flags |= VehicleDataFlags.IsAircraft; flags |= VehicleDataFlags.IsAircraft;
} }
if (veh.Model.Hash==1483171323 && veh.IsDeluxoHovering())
if (v.IsDeluxo && veh.IsDeluxoHovering())
{ {
flags |= VehicleDataFlags.IsDeluxoHovering; flags |= VehicleDataFlags.IsDeluxoHovering;
} }
if (veh.HasRoof)
if (v.HasRoof)
{ {
flags |= VehicleDataFlags.HasRoof; flags |= VehicleDataFlags.HasRoof;
} }
if (v.HasRocketBoost && veh.IsRocketBoostActive())
{
flags |= VehicleDataFlags.IsRocketBoostActive;
}
if (v.HasParachute && veh.IsParachuteActive())
{
flags |= VehicleDataFlags.IsParachuteActive;
}
if (veh.IsOnFire)
{
flags |= VehicleDataFlags.IsOnFire;
}
return flags; return flags;
} }
public static bool IsRocketBoostActive(this Vehicle veh)
{
return Function.Call<bool>(Hash.IS_ROCKET_BOOST_ACTIVE, veh);
}
public static bool IsParachuteActive(this Vehicle veh)
{
return Function.Call<bool>((Hash)0x3DE51E9C80B116CF, veh);
}
public static void SetRocketBoostActive(this Vehicle veh, bool toggle)
{
Function.Call(Hash.SET_ROCKET_BOOST_ACTIVE, veh, toggle);
}
public static void SetParachuteActive(this Vehicle veh, bool toggle)
{
Function.Call((Hash)0x0BFFB028B3DD0A97, veh, toggle);
}
public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods) public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
{ {
Dictionary<int, int> result = new Dictionary<int, int>(); Dictionary<int, int> result = new Dictionary<int, int>();
@ -118,7 +140,6 @@ namespace RageCoop.Client
} }
} }
// Bursted tires // Bursted tires
short burstedTires = 0; short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels()) foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
@ -151,6 +172,7 @@ namespace RageCoop.Client
{ {
door.Break(leavedoors); door.Break(leavedoors);
} }
continue;
} }
else if (door.IsBroken) else if (door.IsBroken)
{ {
@ -212,16 +234,15 @@ namespace RageCoop.Client
{ {
if (p.IsSittingInVehicle()) if (p.IsSittingInVehicle())
{ {
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID); ps.Add((int)p.SeatIndex, p.GetSyncEntity().ID);
} }
} }
return ps; return ps;
} }
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover) public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
{ {
Function.Call(Hash._SET_VEHICLE_HOVER_TRANSFORM_PERCENTAGE, deluxo, hover ? 1f : 0f); Function.Call(Hash.SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
} }
public static bool IsDeluxoHovering(this Vehicle deluxo) public static bool IsDeluxoHovering(this Vehicle deluxo)
{ {
@ -229,7 +250,7 @@ namespace RageCoop.Client
} }
public static void SetDeluxoWingRatio(this Vehicle v, float ratio) public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
{ {
Function.Call(Hash._SET_SPECIALFLIGHT_WING_RATIO, v, ratio); Function.Call(Hash.SET_HOVER_MODE_WING_RATIO, v, ratio);
} }
public static float GetDeluxoWingRatio(this Vehicle v) public static float GetDeluxoWingRatio(this Vehicle v)
{ {
@ -237,7 +258,7 @@ namespace RageCoop.Client
} }
public static float GetNozzleAngel(this Vehicle plane) public static float GetNozzleAngel(this Vehicle plane)
{ {
return Function.Call<float>(Hash._GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane); return Function.Call<float>(Hash.GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
} }
public static bool HasNozzle(this Vehicle v) public static bool HasNozzle(this Vehicle v)
{ {
@ -267,7 +288,6 @@ namespace RageCoop.Client
{ {
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio); Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
} }
#endregion #endregion
} }
} }

View File

@ -1,11 +1,7 @@
using System; using GTA;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native;
using GTA.Math; using GTA.Math;
using GTA.Native;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -49,7 +45,8 @@ namespace RageCoop.Client
} }
return p.Bones[Bone.SkelRightHand].Position; return p.Bones[Bone.SkelRightHand].Position;
} }
static long BulletsShot=0;
private static long BulletsShot = 0;
public static float GetWeaponDamage(this Ped P, uint hash) public static float GetWeaponDamage(this Ped P, uint hash)
{ {
@ -77,148 +74,316 @@ namespace RageCoop.Client
*/ */
} }
public static MuzzleInfo GetMuzzleInfo(this Vehicle v) public static int GetMuzzleIndex(this Vehicle v)
{ {
BulletsShot++; BulletsShot++;
int i;
switch (v.Model.Hash) switch (v.Model.Hash)
{ {
// cerberus3
case 1909700336:
return 53;
// cerberus2
case 679453769:
return 54;
// cerberus
case -801550069:
return 90;
/*
// cerberus (flame)
case -801550069:
i=BulletsShot%2==0 ? 89 : 88;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// cerberus (passenger flame)
case -801550069:
i=BulletsShot%2==0 ? 76 : 75;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
*/
// ISSI6
case 1239571361:
return BulletsShot % 2 == 0 ? 12 : 14;
// ISSI5
case 1537277726:
return BulletsShot % 2 == 0 ? 30 : 32;
// ISSI4
case 628003514:
return BulletsShot % 2 == 0 ? 14 : 12;
// DOMINATOR6
case -1293924613:
return BulletsShot % 2 == 0 ? 51 : 55;
// IMPALER4
case -1744505657:
return BulletsShot % 2 == 0 ? 64 : 63;
// IMPERATOR3
case -755532233:
return BulletsShot % 2 == 0 ? 86 : 88;
// SLAMVAN6
case 1742022738:
return BulletsShot % 2 == 0 ? 78 : 76;
// CHAMPION
case -915234475:
return BulletsShot % 2 == 0 ? 60 : 61;
// MONSTER4
case 840387324:
return BulletsShot % 2 == 0 ? 63 : 65;
// BRUTUS2
case -1890996696:
return 67;
// BRUISER2
case -1694081890:
return BulletsShot % 2 == 0 ? 45 : 51;
// TECHNICAL3
case 1356124575:
return 67;
// TECHNICAL2
case 1180875963:
return 54;
// TECHNICAL
case -2096818938:
return 63;
// PATRIOT3
case -670086588:
return BulletsShot % 2 == 0 ? 87 : 89;
// NIGHTSHARK
case 433954513:
return BulletsShot % 2 == 0 ? 1 : 2;
/*
// NIGHTSHARK (second)
case 433954513:
return BulletsShot%2==0 ? 3 : 4;
*/
// MENACER
case 2044532910:
return BulletsShot % 2 == 0 ? 91 : 90;
/*
// MENACER
case 2044532910:
return new MuzzleInfo(v.Bones[75].Position, v.Bones[75].ForwardVector);
// MENACER
case 2044532910:
return new MuzzleInfo(v.Bones[78].Position, v.Bones[78].ForwardVector);
*/
// CARACARA
case 1254014755:
return 83;
/*
// CARACARA
case 1254014755:
return BulletsShot%2==0 ? 93 : 94;
*/
// INSURGENT
case -1860900134:
return 49;
// INSURGENT3
case -1924433270:
return 81;
/*
// INSURGENT3
case -1924433270:
return BulletsShot%2==0 ? 86 : 91;
*/
// BLAZER5
case -1590337689:
return BulletsShot % 2 == 0 ? 17 : 18;
// BRUISER
case 668439077:
return BulletsShot % 2 == 0 ? 66 : 68;
// BRUTUS
case 2139203625:
return 84;
// MONSTER3
case 1721676810:
return BulletsShot % 2 == 0 ? 53 : 55;
// BRUISER3
case -2042350822:
return BulletsShot % 2 == 0 ? 52 : 50;
// BRUTUS3
case 2038858402:
return 84;
// MONSTER5
case -715746948:
return BulletsShot % 2 == 0 ? 63 : 65;
// JB7002 // JB7002
case 394110044: case 394110044:
i=BulletsShot%2==0 ? 54 : 53; return BulletsShot % 2 == 0 ? 54 : 53;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// DOMINATOR5 // DOMINATOR5
case -1375060657: case -1375060657:
i=BulletsShot%2==0 ? 35 : 36; return BulletsShot % 2 == 0 ? 35 : 36;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPALER3 // IMPALER3
case -1924800695: case -1924800695:
i=BulletsShot%2==0 ? 75 : 76; return BulletsShot % 2 == 0 ? 75 : 76;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPERATOR2 // IMPERATOR2
case 1637620610: case 1637620610:
i=BulletsShot%2==0 ? 97 : 99; return BulletsShot % 2 == 0 ? 97 : 99;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// SLAMVAN5 // SLAMVAN5
case 373261600: case 373261600:
i=BulletsShot%2==0 ? 51 : 53; return BulletsShot % 2 == 0 ? 51 : 53;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// RUINER2 // RUINER2
case 941494461: case 941494461:
i=BulletsShot%2==0 ? 65 : 66; return BulletsShot % 2 == 0 ? 65 : 66;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// TAMPA3 // TAMPA3
case -1210451983: case -1210451983:
return new MuzzleInfo(v.Bones[87].Position, v.Bones[87].ForwardVector); return 87;
// SCRAMJET // SCRAMJET
case -638562243: case -638562243:
i=BulletsShot%2==0 ? 44 : 45; return BulletsShot % 2 == 0 ? 44 : 45;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// VIGILANTE // VIGILANTE
case -1242608589: case -1242608589:
i=BulletsShot%2==0 ? 42 : 43; return BulletsShot % 2 == 0 ? 42 : 43;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR380 // ZR380
case 540101442: case 540101442:
i=BulletsShot%2==0 ? 57 : 63; return BulletsShot % 2 == 0 ? 57 : 63;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR3802 // ZR3802
case -1106120762: case -1106120762:
i=BulletsShot%2==0 ? 57 : 63; return BulletsShot % 2 == 0 ? 57 : 63;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR3803 // ZR3803
case -1478704292: case -1478704292:
i=BulletsShot%2==0 ? 53 : 59; return BulletsShot % 2 == 0 ? 53 : 59;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// STROMBERG // STROMBERG
case 886810209: case 886810209:
i=BulletsShot%2==0 ? 85 : 84; return BulletsShot % 2 == 0 ? 85 : 84;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// SLAMVAN4 // SLAMVAN4
case -2061049099: case -2061049099:
i=BulletsShot%2==0 ? 76 : 78; return BulletsShot % 2 == 0 ? 76 : 78;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPERATOR // IMPERATOR
case 444994115: case 444994115:
i=BulletsShot%2==0 ? 88 : 86; return BulletsShot % 2 == 0 ? 88 : 86;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPALER2 // IMPALER2
case 1009171724: case 1009171724:
i=BulletsShot%2==0 ? 63 : 64; return BulletsShot % 2 == 0 ? 63 : 64;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// DOMINATOR4 // DOMINATOR4
case -688189648: case -688189648:
i=BulletsShot%2==0 ? 59 : 60; return BulletsShot % 2 == 0 ? 59 : 60;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// SAVAGE // SAVAGE
case -82626025: case -82626025:
return new MuzzleInfo(v.Bones[30].Position, v.Bones[30].ForwardVector); return 30;
// BUZZARD // BUZZARD
case 788747387: case 788747387:
i=BulletsShot%2==0 ? 28 : 23; return BulletsShot % 2 == 0 ? 28 : 23;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ANNIHL // ANNIHL
case 837858166: case 837858166:
i=(int)BulletsShot%4+35; return (int)BulletsShot % 4 + 35;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// HYDRA // HYDRA
case 970385471: case 970385471:
i=BulletsShot%2==0 ? 29 : 28; return BulletsShot % 2 == 0 ? 29 : 28;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// STARLING // STARLING
case -1700874274: case -1700874274:
i=BulletsShot%2==0 ? 24 : 12; return BulletsShot % 2 == 0 ? 24 : 12;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// RHINO // RHINO
case 782665360: case 782665360:
return new MuzzleInfo(v.Bones[35].Position,v.Bones[35].ForwardVector); return 30;
default: default:
return null; return AddOnDataProvider.GetMuzzleIndex(v.Model.Hash);
} }
} }
public static bool IsUsingProjectileWeapon(this Ped p) public static bool IsUsingProjectileWeapon(this Ped p)
{ {
var vp = p.VehicleWeapon; var vp = p.VehicleWeapon;
var type = Function.Call<int>(Hash.GET_WEAPON_DAMAGE_TYPE, vp);
if (vp != VehicleWeaponHash.Invalid) if (vp != VehicleWeaponHash.Invalid)
{ {
return VehicleProjectileWeapons.Contains(vp); return type == 3 ? false : VehicleProjectileWeapons.Contains(vp) || (type == 5 && !ExplosiveBullets.Contains((uint)vp));
} }
else
{
var w = p.Weapons.Current; var w = p.Weapons.Current;
return w.Group == WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash); return w.Group == WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
} }
}
public static readonly HashSet<uint> ExplosiveBullets = new HashSet<uint>
{
(uint)VehicleWeaponHash.PlayerLazer,
(uint)WeaponHash.Railgun,
1638077257
};
public static readonly Dictionary<WeaponHash, int> MuzzleBoneIndexes = new Dictionary<WeaponHash, int> public static readonly Dictionary<WeaponHash, int> MuzzleBoneIndexes = new Dictionary<WeaponHash, int>
{ {
@ -289,7 +454,6 @@ namespace RageCoop.Client
}; };
public static readonly HashSet<WeaponHash> ProjectileWeapons = new HashSet<WeaponHash> { public static readonly HashSet<WeaponHash> ProjectileWeapons = new HashSet<WeaponHash> {
WeaponHash.HomingLauncher, WeaponHash.HomingLauncher,
WeaponHash.RPG, WeaponHash.RPG,
@ -307,5 +471,73 @@ namespace RageCoop.Client
(VehicleWeaponHash)3565779982, // STROMBERG missiles (VehicleWeaponHash)3565779982, // STROMBERG missiles
(VehicleWeaponHash)3169388763, // SCRAMJET missiles (VehicleWeaponHash)3169388763, // SCRAMJET missiles
}; };
public static string GetFlashFX(this WeaponHash w)
{
switch (w.GetWeaponGroup())
{
case WeaponGroup.SMG:
return "muz_smg";
case WeaponGroup.Shotgun:
return "muz_smg";
case WeaponGroup.AssaultRifle:
return "muz_assault_rifle";
case WeaponGroup.Pistol:
return "muz_pistol";
case WeaponGroup.Stungun:
return "muz_stungun";
case WeaponGroup.Heavy:
switch (w)
{
case WeaponHash.Minigun:
return "muz_minigun";
case WeaponHash.RPG:
return "muz_rpg";
default:
return "muz_minigun";
}
case WeaponGroup.Sniper:
return "muz_alternate_star";
case WeaponGroup.PetrolCan:
return "weap_petrol_can";
case WeaponGroup.FireExtinguisher:
return "weap_extinguisher";
}
switch ((VehicleWeaponHash)w)
{
case VehicleWeaponHash.Tank:
return "muz_tank";
case VehicleWeaponHash.PlayerBuzzard:
return "muz_buzzard";
}
return "muz_assault_rifle";
}
public static WeaponGroup GetWeaponGroup(this WeaponHash hash)
{
return Function.Call<WeaponGroup>(Hash.GET_WEAPONTYPE_GROUP, hash);
} }
} }
/*
class WeaponInfo
{
public string Name;
public string MuzzleFx;
}
public class AimingInfo
{
public string Name;
public float HeadingLimit;
public float SweepPitchMin;
public float SweepPitchMax;
}
*/
}

View File

@ -1,8 +1,6 @@
using System; using GTA;
using System.Linq;
using RageCoop.Core;
using GTA;
using GTA.Native; using GTA.Native;
using System;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -11,7 +9,6 @@ namespace RageCoop.Client
/// </summary> /// </summary>
public class WorldThread : Script public class WorldThread : Script
{ {
private static bool _lastDisableTraffic = false;
/// <summary> /// <summary>
/// Don't use it! /// Don't use it!
@ -21,43 +18,55 @@ namespace RageCoop.Client
Tick += OnTick; Tick += OnTick;
Aborted += (sender, e) => Aborted += (sender, e) =>
{ {
if (_lastDisableTraffic) ChangeTraffic(true);
{
Traffic(true);
}
}; };
} }
private static bool _trafficEnabled;
private void OnTick(object sender, EventArgs e) private void OnTick(object sender, EventArgs e)
{ {
if (Game.IsLoading) if (Game.IsLoading || !Networking.IsOnServer)
{ {
return; return;
} }
if (!Networking.IsOnServer)
{
return ;
}
Game.DisableControlThisFrame(Control.FrontendPause); Game.DisableControlThisFrame(Control.FrontendPause);
if (Main.Settings.DisableAlternatePause) if (Main.Settings.DisableAlternatePause)
{ {
Game.DisableControlThisFrame(Control.FrontendPauseAlternate); Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
} }
// Sets a value that determines how aggressive the ocean waves will be. // Sets a value that determines how aggressive the ocean waves will be.
// Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm. // Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm.
Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
// Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, Game.Player.Character.Handle, true, false); if (Main.Settings.ShowEntityOwnerName)
if (Main.Settings==null) { return; }
if (Main.Settings.DisableTraffic)
{ {
if (!_lastDisableTraffic) unsafe
{ {
Traffic(false); int handle;
if (Function.Call<bool>(Hash.GET_ENTITY_PLAYER_IS_FREE_AIMING_AT, 0, &handle))
{
var entity = Entity.FromHandle(handle);
if (entity != null)
{
var owner = "invalid";
if (entity.EntityType == EntityType.Vehicle)
{
owner = (entity as Vehicle).GetSyncEntity()?.Owner?.Username ?? "unknown";
}
if (entity.EntityType == EntityType.Ped)
{
owner = (entity as Ped).GetSyncEntity()?.Owner?.Username ?? "unknown";
}
GTA.UI.Screen.ShowHelpTextThisFrame("Entity owner: " + owner);
} }
}
}
}
if (!_trafficEnabled)
{
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0); Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0); Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f); Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
@ -66,15 +75,13 @@ namespace RageCoop.Client
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME); Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME); Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
} }
else if (_lastDisableTraffic) }
public static void Traffic(bool enable)
{ {
Traffic(true); ChangeTraffic(enable);
_trafficEnabled = enable;
} }
private static void ChangeTraffic(bool enable)
_lastDisableTraffic = Main.Settings.DisableTraffic;
}
private void Traffic(bool enable)
{ {
if (enable) if (enable)
{ {
@ -83,15 +90,15 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RANDOM_TRAINS, true); Function.Call(Hash.SET_RANDOM_TRAINS, true);
Function.Call(Hash.SET_RANDOM_BOATS, true); Function.Call(Hash.SET_RANDOM_BOATS, true);
Function.Call(Hash.SET_GARBAGE_TRUCKS, true); Function.Call(Hash.SET_GARBAGE_TRUCKS, true);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 1); // 0 - 3 Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 1); // 0 - 3 Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3); // 0 - 3
Function.Call(Hash.SET_ALL_VEHICLE_GENERATORS_ACTIVE); Function.Call(Hash.SET_ALL_VEHICLE_GENERATORS_ACTIVE);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true); Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1); Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true); Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false); Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, false);
} }
else else if (Networking.IsOnServer)
{ {
Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f, 1000.0f, 0, 1, 1, 1); Function.Call(Hash.ADD_SCENARIO_BLOCKING_AREA, -10000.0f, -10000.0f, -1000.0f, 10000.0f, 10000.0f, 1000.0f, 0, 1, 1, 1);
Function.Call(Hash.SET_CREATE_RANDOM_COPS, false); Function.Call(Hash.SET_CREATE_RANDOM_COPS, false);
@ -99,41 +106,38 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RANDOM_BOATS, false); Function.Call(Hash.SET_RANDOM_BOATS, false);
Function.Call(Hash.SET_GARBAGE_TRUCKS, false); Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
Function.Call(Hash.DELETE_ALL_TRAINS); Function.Call(Hash.DELETE_ALL_TRAINS);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3); Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false); Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false); Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0); Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false); Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true); Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
foreach (Ped ped in World.GetAllPeds()) foreach (Ped ped in World.GetAllPeds())
{ {
if (ped == Game.Player.Character) { continue; }
SyncedPed c = EntityPool.GetPedByHandle(ped.Handle); SyncedPed c = EntityPool.GetPedByHandle(ped.Handle);
if ((c == null) || (c.IsLocal && (ped.Handle != Game.Player.Character.Handle) && ped.PopulationType != EntityPopulationType.Mission)) if ((c == null) || (c.IsLocal && (ped.Handle != Game.Player.Character.Handle) && ped.PopulationType != EntityPopulationType.Mission))
{ {
if (ped.Handle==Game.Player.Character.Handle) { continue; }
Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic"); // Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete(); ped.CurrentVehicle?.Delete();
ped.Kill(); ped.Kill();
ped.Delete(); ped.Delete();
} }
} }
foreach (Vehicle veh in World.GetAllVehicles()) foreach (Vehicle veh in World.GetAllVehicles())
{ {
SyncedVehicle v = veh.GetSyncEntity(); SyncedVehicle v = veh.GetSyncEntity();
if (v.MainVehicle==Game.Player.LastVehicle) if (v.MainVehicle == Game.Player.LastVehicle || v.MainVehicle == Game.Player.Character.CurrentVehicle)
{ {
// Don't delete player's vehicle // Don't delete player's vehicle
continue; continue;
} }
if ((v == null) || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission)) if ((v == null) || (v.IsLocal && veh.PopulationType != EntityPopulationType.Mission))
{ {
Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic"); // Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
veh.Delete(); veh.Delete();
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="5.7.0" targetFramework="net48" developmentDependency="true" />
<package id="Fody" version="6.6.3" targetFramework="net48" developmentDependency="true" />
<package id="Microsoft.Extensions.ObjectPool" version="6.0.8" targetFramework="net48" />
<package id="Microsoft.NETCore.Platforms" version="1.1.0" targetFramework="net48" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net48" />
<package id="Microsoft.Win32.Registry" version="4.7.0" targetFramework="net481" />
<package id="NAudio" version="2.1.0" targetFramework="net481" />
<package id="NAudio.Asio" version="2.1.0" targetFramework="net481" />
<package id="NAudio.Core" version="2.1.0" targetFramework="net481" />
<package id="NAudio.Midi" version="2.1.0" targetFramework="net481" />
<package id="NAudio.Wasapi" version="2.1.0" targetFramework="net481" />
<package id="NAudio.WinForms" version="2.1.0" targetFramework="net481" />
<package id="NAudio.WinMM" version="2.1.0" targetFramework="net481" />
<package id="NETStandard.Library" version="1.6.1" targetFramework="net48" />
<package id="SharpZipLib" version="1.3.3" targetFramework="net48" />
<package id="System.AppContext" version="4.3.0" targetFramework="net48" />
<package id="System.Collections" version="4.3.0" targetFramework="net48" />
<package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net48" />
<package id="System.Console" version="4.3.0" targetFramework="net48" />
<package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net48" />
<package id="System.Diagnostics.DiagnosticSource" version="4.3.0" targetFramework="net48" />
<package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net48" />
<package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net48" />
<package id="System.Globalization" version="4.3.0" targetFramework="net48" />
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net48" />
<package id="System.IO" version="4.3.0" targetFramework="net48" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net48" />
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net48" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net48" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Linq" version="4.3.0" targetFramework="net48" />
<package id="System.Linq.Expressions" version="4.3.0" targetFramework="net48" />
<package id="System.Net.Http" version="4.3.0" targetFramework="net48" />
<package id="System.Net.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net48" />
<package id="System.ObjectModel" version="4.3.0" targetFramework="net48" />
<package id="System.Reflection" version="4.3.0" targetFramework="net48" />
<package id="System.Reflection.Extensions" version="4.3.0" targetFramework="net48" />
<package id="System.Reflection.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime.Handles" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net48" />
<package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net48" />
<package id="System.Security.AccessControl" version="4.7.0" targetFramework="net481" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="4.7.0" targetFramework="net481" />
<package id="System.Text.Encoding" version="4.3.0" targetFramework="net48" />
<package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net48" />
<package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net48" />
<package id="System.Threading" version="4.3.0" targetFramework="net48" />
<package id="System.Threading.Tasks" version="4.3.0" targetFramework="net48" />
<package id="System.Threading.Timer" version="4.3.0" targetFramework="net48" />
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net48" />
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net48" />
</packages>

View File

@ -1,145 +1,56 @@
using System; using GTA.Math;
using System.IO;
using System.Text; using System.Text;
using System.Linq;
using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal class BitReader internal class BitReader : BinaryReader
{ {
public int CurrentIndex { get; set; }
private byte[] ResultArray; public BitReader(byte[] array) : base(new MemoryStream(array))
public BitReader(byte[] array)
{ {
CurrentIndex = 0;
ResultArray = array;
} }
~BitReader() ~BitReader()
{ {
ResultArray = null; Close();
Dispose();
} }
public bool CanRead(int bytes)
{
return ResultArray.Length >= CurrentIndex + bytes;
}
public bool ReadBool()
{
bool value = BitConverter.ToBoolean(ResultArray, CurrentIndex);
CurrentIndex += 1;
return value;
}
public float ReadFloat()
{
float value = BitConverter.ToSingle(ResultArray, CurrentIndex);
CurrentIndex += 4;
return value;
}
public byte ReadByte()
{
byte value = ResultArray[CurrentIndex];
CurrentIndex += 1;
return value;
}
public byte[] ReadByteArray(int length)
{
byte[] value = new byte[length];
Array.Copy(ResultArray, CurrentIndex,value,0,length);
CurrentIndex += length;
return value;
}
public byte[] ReadByteArray() public byte[] ReadByteArray()
{ {
return ReadByteArray(ReadInt()); return base.ReadBytes(ReadInt32());
} }
public short ReadShort() public override string ReadString()
{ {
short value = BitConverter.ToInt16(ResultArray, CurrentIndex); return Encoding.UTF8.GetString(ReadBytes(ReadInt32()));
CurrentIndex += 2;
return value;
}
public ushort ReadUShort()
{
ushort value = BitConverter.ToUInt16(ResultArray, CurrentIndex);
CurrentIndex += 2;
return value;
}
public int ReadInt()
{
int value = BitConverter.ToInt32(ResultArray, CurrentIndex);
CurrentIndex += 4;
return value;
}
public uint ReadUInt()
{
uint value = BitConverter.ToUInt32(ResultArray, CurrentIndex);
CurrentIndex += 4;
return value;
}
public long ReadLong()
{
long value = BitConverter.ToInt64(ResultArray, CurrentIndex);
CurrentIndex += 8;
return value;
}
public ulong ReadULong()
{
ulong value = BitConverter.ToUInt64(ResultArray, CurrentIndex);
CurrentIndex += 8;
return value;
}
public string ReadString(int index)
{
string value = Encoding.UTF8.GetString(ResultArray.Skip(CurrentIndex).Take(index).ToArray());
CurrentIndex += index;
return value;
}
public string ReadString()
{
var len = ReadInt();
string value = Encoding.UTF8.GetString(ResultArray.Skip(CurrentIndex).Take(len).ToArray());
CurrentIndex += len;
return value;
} }
public Vector3 ReadVector3() public Vector3 ReadVector3()
{ {
return new Vector3() return new Vector3()
{ {
X = ReadFloat(), X = ReadSingle(),
Y = ReadFloat(), Y = ReadSingle(),
Z = ReadFloat() Z = ReadSingle()
}; };
} }
public Vector2 ReadVector2() public Vector2 ReadVector2()
{ {
return new Vector2() return new Vector2()
{ {
X = ReadFloat(), X = ReadSingle(),
Y = ReadFloat() Y = ReadSingle()
}; };
} }
public Quaternion ReadQuaternion() public Quaternion ReadQuaternion()
{ {
return new Quaternion() return new Quaternion()
{ {
X = ReadFloat(), X = ReadSingle(),
Y = ReadFloat(), Y = ReadSingle(),
Z = ReadFloat(), Z = ReadSingle(),
W = ReadFloat() W = ReadSingle()
}; };
} }
} }

View File

@ -1,135 +1,245 @@
using System; using GTA.Math;
using Lidgren.Network;
using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA.Math;
using System.Security.Cryptography;
using System.Net;
using System.IO; using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
[assembly: InternalsVisibleTo("RageCoop.Server")] [assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")] [assembly: InternalsVisibleTo("RageCoop.Client")]
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal class CoreUtils internal static class CoreUtils
{ {
private static readonly HashSet<string> ToIgnore = new HashSet<string>()
public static (byte, byte[]) GetBytesFromObject(object obj) {
"RageCoop.Client.dll",
"RageCoop.Core.dll",
"RageCoop.Server.dll",
"ScriptHookVDotNet3.dll",
"ScriptHookVDotNet.dll"
};
public static bool CanBeIgnored(this string name)
{
return ToIgnore.Contains(name);
}
public static void GetBytesFromObject(object obj, NetOutgoingMessage m)
{ {
switch (obj) switch (obj)
{ {
case byte _: case byte value:
return (0x01, new byte[] { (byte)obj }); m.Write((byte)0x01); m.Write(value); break;
case short _: case short value:
return (0x02, BitConverter.GetBytes((short)obj)); m.Write((byte)0x02); m.Write(value); break;
case ushort _: case ushort value:
return (0x03, BitConverter.GetBytes((ushort)obj)); m.Write((byte)0x03); m.Write(value); break;
case int _: case int value:
return (0x04, BitConverter.GetBytes((int)obj)); m.Write((byte)0x04); m.Write(value); break;
case uint _: case uint value:
return (0x05, BitConverter.GetBytes((uint)obj)); m.Write((byte)0x05); m.Write(value); break;
case long _: case long value:
return (0x06, BitConverter.GetBytes((long)obj)); m.Write((byte)0x06); m.Write(value); break;
case ulong _: case ulong value:
return (0x07, BitConverter.GetBytes((ulong)obj)); m.Write((byte)0x07); m.Write(value); break;
case float _: case float value:
return (0x08, BitConverter.GetBytes((float)obj)); m.Write((byte)0x08); m.Write(value); break;
case bool _: case bool value:
return (0x09, BitConverter.GetBytes((bool)obj)); m.Write((byte)0x09); m.Write(value); break;
case string _: case string value:
return (0x10, ((string)obj).GetBytesWithLength()); m.Write((byte)0x10); m.Write(value); break;
case Vector3 _: case Vector3 value:
return (0x11,((Vector3)obj).GetBytes()); m.Write((byte)0x11); m.Write(value); break;
case Quaternion _: case Quaternion value:
return (0x12, ((Quaternion)obj).GetBytes()); m.Write((byte)0x12); m.Write(value); break;
case GTA.Model _: case GTA.Model value:
return (0x13, BitConverter.GetBytes((GTA.Model)obj)); m.Write((byte)0x13); m.Write(value); break;
case Vector2 _: case Vector2 value:
return (0x14, ((Vector2)obj).GetBytes()); m.Write((byte)0x14); m.Write(value); break;
case Tuple<byte, byte[]> _: case byte[] value:
var tup = (Tuple<byte, byte[]>)obj; m.Write((byte)0x15); m.WriteByteArray(value); break;
return (tup.Item1, tup.Item2); case Tuple<byte, byte[]> value:
m.Write(value.Item1); m.Write(value.Item2); break;
default: default:
return (0x0, null); throw new Exception("Unsupported object type: " + obj.GetType());
} }
} }
public static IPEndPoint StringToEndPoint(string endpointstring)
{
return StringToEndPoint(endpointstring, -1);
}
public static IPEndPoint StringToEndPoint(string endpointstring, int defaultport)
{
if (string.IsNullOrEmpty(endpointstring)
|| endpointstring.Trim().Length == 0)
{
throw new ArgumentException("Endpoint descriptor may not be empty.");
}
if (defaultport != -1 &&
(defaultport < IPEndPoint.MinPort
|| defaultport > IPEndPoint.MaxPort))
{
throw new ArgumentException(string.Format("Invalid default port '{0}'", defaultport));
}
string[] values = endpointstring.Split(new char[] { ':' });
IPAddress ipaddy;
int port = -1;
//check if we have an IPv6 or ports
if (values.Length <= 2) // ipv4 or hostname
{
if (values.Length == 1)
//no port is specified, default
port = defaultport;
else
port = getPort(values[1]);
//try to use the address as IPv4, otherwise get hostname
if (!IPAddress.TryParse(values[0], out ipaddy))
ipaddy = GetIPfromHost(values[0]);
}
else if (values.Length > 2) //ipv6
{
//could [a:b:c]:d
if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]"))
{
string ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
ipaddy = IPAddress.Parse(ipaddressstring);
port = getPort(values[values.Length - 1]);
}
else //[a:b:c] or a:b:c
{
ipaddy = IPAddress.Parse(endpointstring);
port = defaultport;
}
}
else
{
throw new FormatException(string.Format("Invalid endpoint ipaddress '{0}'", endpointstring));
}
if (port == -1)
throw new ArgumentException(string.Format("No port specified: '{0}'", endpointstring));
return new IPEndPoint(ipaddy, port);
}
private static int getPort(string p)
{
if (!int.TryParse(p, out int port)
|| port < IPEndPoint.MinPort
|| port > IPEndPoint.MaxPort)
{
throw new FormatException(string.Format("Invalid end point port '{0}'", p));
}
return port;
}
public static IPAddress GetLocalAddress(string target = "8.8.8.8")
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
socket.Connect(target, 65530);
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
return endPoint.Address;
}
}
public static IPAddress GetIPfromHost(string p)
{
var hosts = Dns.GetHostAddresses(p);
if (hosts == null || hosts.Length == 0)
throw new ArgumentException(string.Format("Host not found: {0}", p));
return hosts[0];
}
public static IpInfo GetIPInfo()
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var httpClient = new HttpClient();
HttpResponseMessage response = httpClient.GetAsync("https://ipinfo.io/json").GetAwaiter().GetResult();
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"IPv4 request failed! [{(int)response.StatusCode}/{response.ReasonPhrase}]");
}
string content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<IpInfo>(content);
}
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), true);
}
public static string GetInvariantRID()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
return "unknown";
}
/// <summary>
/// Get local ip addresses on all network interfaces
/// </summary>
/// <returns></returns>
public static List<IPAddress> GetLocalAddress()
{
var addresses = new List<IPAddress>();
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
{
addresses.Add(addr.Address);
}
}
return addresses;
}
}
internal class IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
} }
internal static class Extensions internal static class Extensions
{ {
public static void AddVector3(this List<byte> bytes, Vector3 vec3)
{
bytes.AddRange(BitConverter.GetBytes(vec3.X));
bytes.AddRange(BitConverter.GetBytes(vec3.Y));
bytes.AddRange(BitConverter.GetBytes(vec3.Z));
}
public static void AddQuaternion(this List<byte> bytes, Quaternion quat)
{
bytes.AddRange(BitConverter.GetBytes(quat.X));
bytes.AddRange(BitConverter.GetBytes(quat.Y));
bytes.AddRange(BitConverter.GetBytes(quat.Z));
bytes.AddRange(BitConverter.GetBytes(quat.W));
}
public static void AddInt(this List<byte> bytes,int i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUint(this List<byte> bytes, uint i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddShort(this List<byte> bytes, short i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUshort(this List<byte> bytes, ushort i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddLong(this List<byte> bytes, long i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUlong(this List<byte> bytes, ulong i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddFloat(this List<byte> bytes, float i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddString(this List<byte> bytes, string s)
{
var sb = Encoding.UTF8.GetBytes(s);
bytes.AddInt(sb.Length);
bytes.AddRange(sb);
}
public static void AddArray(this List<byte> bytes, byte[] toadd)
{
bytes.AddInt(toadd.Length);
bytes.AddRange(toadd);
}
public static int GetHash(string s)
{
MD5 md5Hasher = MD5.Create();
var hashed = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(s));
return BitConverter.ToInt32(hashed, 0);
}
public static byte[] GetBytes(this string s) public static byte[] GetBytes(this string s)
{ {
return Encoding.UTF8.GetBytes(s); return Encoding.UTF8.GetBytes(s);
} }
public static byte[] GetBytesWithLength(this string s)
{
var data = new List<byte>(100);
var sb = Encoding.UTF8.GetBytes(s);
data.AddInt(sb.Length);
data.AddRange(sb);
return data.ToArray();
}
public static string GetString(this byte[] data) public static string GetString(this byte[] data)
{ {
return Encoding.UTF8.GetString(data); return Encoding.UTF8.GetString(data);
@ -156,11 +266,20 @@ namespace RageCoop.Core
// 16 bytes // 16 bytes
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
} }
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
{
var p = new T();
p.Deserialize(msg);
return p;
}
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag) public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{ {
return (flagToCheck & flag) != 0; return (flagToCheck & flag) != 0;
} }
public static bool HasProjDataFlag(this ProjectileDataFlags flagToCheck, ProjectileDataFlags flag)
{
return (flagToCheck & flag) != 0;
}
public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag) public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
{ {
@ -243,7 +362,7 @@ namespace RageCoop.Core
} }
public static string Dump<T>(this IEnumerable<T> objects) public static string Dump<T>(this IEnumerable<T> objects)
{ {
return "{"+string.Join(",",objects)+"}"; return $"{{{string.Join(",", objects)}}}";
} }
public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action) public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action)
{ {
@ -263,6 +382,12 @@ namespace RageCoop.Core
return memoryStream.ToArray(); return memoryStream.ToArray();
} }
} }
public static MemoryStream ToMemStream(this Stream stream)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream;
}
public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1) public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1)
{ {
if (arrays.Count == 1) { return arrays[0]; } if (arrays.Count == 1) { return arrays[0]; }
@ -284,4 +409,42 @@ namespace RageCoop.Core
return false; return false;
} }
} }
/// <summary>
/// Some extension methods provided by RageCoop
/// </summary>
public static class PublicExtensions
{
/// <summary>
/// Get a SHA256 hashed byte array of the input string, internally used to hash password at client side.
/// </summary>
/// <param name="inputString"></param>
/// <returns></returns>
public static byte[] GetSHA256Hash(this string inputString)
{
using (HashAlgorithm algorithm = SHA256.Create())
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
}
/// <summary>
/// Convert a byte array to hex-encoded string, internally used to trigger handshake event
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string ToHexString(this byte[] data)
{
return BitConverter.ToString(data).Replace("-", String.Empty);
}
/// <summary>
/// Convert a string to IP address
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public static IPAddress ToIP(this string ip)
{
return IPAddress.Parse(ip);
}
}
} }

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura />
</Weavers>

View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text;
using System.Threading; using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
@ -32,9 +31,9 @@ namespace RageCoop.Core
private StreamWriter logWriter; private StreamWriter logWriter;
private string Buffer = ""; private string Buffer = "";
private Thread LoggerThread; private readonly Thread LoggerThread;
private bool Stopping = false; private bool Stopping = false;
private bool FlushImmediately; private readonly bool FlushImmediately;
internal Logger(bool flushImmediately = false, bool overwrite = true) internal Logger(bool flushImmediately = false, bool overwrite = true)
{ {

View File

@ -1,13 +1,27 @@
using System; using GTA.Math;
using GTA.Math; using System;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal static class MathExtensions internal static class MathExtensions
{ {
public const float Deg2Rad = (float)(Math.PI * 2) / 360; public const float Deg2Rad = (float)(Math.PI * 2) / 360;
public const float Rad2Deg = 360 / (float)(Math.PI * 2); public const float Rad2Deg = 360 / (float)(Math.PI * 2);
public static Vector3 ToDirection(this Vector3 rotation)
{
double z = DegToRad(rotation.Z);
double x = DegToRad(rotation.X);
double num = Math.Abs(Math.Cos(x));
return new Vector3
{
X = (float)(-Math.Sin(z) * num),
Y = (float)(Math.Cos(z) * num),
Z = (float)Math.Sin(x)
};
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@ -67,14 +81,14 @@ namespace RageCoop.Core
vect = vect.ToRadians(); vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f; float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2); float sinRollOver2 = (float)Math.Sin(rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2); float cosRollOver2 = (float)Math.Cos(rollOver2);
float pitchOver2 = vect.Y * 0.5f; float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2); float sinPitchOver2 = (float)Math.Sin(pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2); float cosPitchOver2 = (float)Math.Cos(pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin((double)yawOver2); float sinYawOver2 = (float)Math.Sin(yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2); float cosYawOver2 = (float)Math.Cos(yawOver2);
Quaternion result = new Quaternion() Quaternion result = new Quaternion()
{ {
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2, X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
@ -98,6 +112,10 @@ namespace RageCoop.Core
{ {
return radian * (float)(180 / Math.PI); return radian * (float)(180 / Math.PI);
} }
public static Vector3 ToEulerDegrees(this Quaternion q)
{
return q.ToEulerAngles().ToDegree();
}
public static Vector3 ToEulerAngles(this Quaternion q) public static Vector3 ToEulerAngles(this Quaternion q)
{ {
Vector3 angles = new Vector3(); Vector3 angles = new Vector3();
@ -127,16 +145,12 @@ namespace RageCoop.Core
} }
private static float CopySign(double x, double y) private static float CopySign(double x, double y)
{ {
bool isPositive = y>=0; if (y >= 0)
if (isPositive)
{ {
if (x>=0) { return (float)x; } else { return (float)-x; } return x >= 0 ? (float)x : (float)-x;
} }
else
{
if (x>=0) { return (float)-x; } else { return (float)x; }
} return x >= 0 ? (float)-x : (float)x;
} }
public static double AngelTo(this Vector3 v1, Vector3 v2) public static double AngelTo(this Vector3 v1, Vector3 v2)
{ {
@ -144,7 +158,6 @@ namespace RageCoop.Core
} }
public static float GetCosTheta(this Vector3 v1, Vector3 v2) public static float GetCosTheta(this Vector3 v1, Vector3 v2)
{ {
return Vector3.Dot(v1, v2) / (v1.Length() * v2.Length()); return Vector3.Dot(v1, v2) / (v1.Length() * v2.Length());
} }

View File

@ -0,0 +1,60 @@
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Threading;
namespace RageCoop.Core
{
internal class CoopPeer : NetPeer, IDisposable
{
public EventHandler<NetIncomingMessage> OnMessageReceived;
private readonly Thread ListenerThread;
private bool _stopping = false;
public CoopPeer(NetPeerConfiguration config) : base(config)
{
Start();
NetIncomingMessage msg;
ListenerThread = new Thread(() =>
{
while (!_stopping)
{
msg = WaitMessage(200);
if (msg != null)
{
OnMessageReceived?.Invoke(this, msg);
}
}
});
ListenerThread.Start();
}
/// <summary>
/// Terminate all connections and background thread
/// </summary>
public void Dispose()
{
_stopping = true;
Shutdown("Bye!");
ListenerThread.Join();
}
public void SendTo(Packet p, NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connection, method, (int)channel);
}
public void SendTo(Packet p, IList<NetConnection> connections, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connections, method, (int)channel);
}
public void Send(Packet p, IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, cons, method, (int)channel);
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.IO;
using System.Net;
using System.Threading;
namespace RageCoop.Core
{
internal static class HttpHelper
{
public static void DownloadFile(string url, string destination, Action<int> progressCallback)
{
if (File.Exists(destination)) { File.Delete(destination); }
AutoResetEvent ae = new AutoResetEvent(false);
WebClient client = new WebClient();
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => progressCallback?.Invoke(e1.ProgressPercentage);
client.DownloadFileCompleted += (s, e2) =>
{
ae.Set();
};
client.DownloadFileAsync(new Uri(url), destination);
ae.WaitOne();
}
public static string DownloadString(string url)
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient();
return client.DownloadString(url);
}
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace RageCoop.Core
{
internal class PublicKey
{
public PublicKey()
{
}
public static PublicKey FromServerInfo(ServerInfo info)
{
return new PublicKey
{
Modulus = Convert.FromBase64String(info.publicKeyModulus),
Exponent = Convert.FromBase64String(info.publicKeyExponent)
};
}
public byte[] Modulus;
public byte[] Exponent;
}
}

View File

@ -0,0 +1,32 @@
namespace RageCoop.Core
{
/// <summary>
/// A json object representing a server's information as annouced to master server.
/// </summary>
public class ServerInfo
{
#pragma warning disable 1591
public string address { get; set; }
public string port { get; set; }
public string name { get; set; }
public string version { get; set; }
public string players { get; set; }
public string maxPlayers { get; set; }
public string country { get; set; }
public string description { get; set; }
public string website { get; set; }
public string gameMode { get; set; }
public string language { get; set; }
public bool useP2P { get; set; }
public bool useZT { get; set; }
public string ztID { get; set; }
public string ztAddress { get; set; }
public string publicKeyModulus { get; set; }
public string publicKeyExponent { get; set; }
}
}

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace RageCoop.Core
{
internal class ZeroTierNetwork
{
public ZeroTierNetwork(string line)
{
// <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
var v = Regex.Split(line, " ").Skip(2).ToArray();
ID = v[0];
Name = v[1];
Mac = v[2];
Status = v[3];
Type = v[4];
Device = v[5];
foreach (var i in v[6].Split(','))
{
Addresses.Add(i.Split('/')[0]);
}
}
public string ID;
public string Name;
public string Mac;
public string Status;
public string Type;
public string Device;
public List<string> Addresses = new List<string>();
}
internal static class ZeroTierHelper
{
private static readonly string _path = "zerotier-cli";
private static readonly string _arg = "";
static ZeroTierHelper()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat");
_arg = $"/c \"{batpath}\" ";
_path = "cmd.exe";
}
var status = RunCommand("status");
if (!status.StartsWith("200"))
{
throw new Exception("ZeroTier not ready: " + status);
}
}
public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
{
var p = Run("join " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK"))
{
throw new Exception(o + p.StandardError.ReadToEnd());
}
if (timeout == 0) { return Networks[networkId]; }
int i = 0;
while (i <= timeout)
{
i += 100;
if (Networks.TryGetValue(networkId, out var n))
{
if (n.Addresses.Count != 0 && (!n.Addresses.Where(x => x == "-").Any()))
{
return n;
}
System.Threading.Thread.Sleep(100);
}
else
{
break;
}
}
return null;
}
public static void Leave(string networkId)
{
var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK"))
{
throw new Exception(o + p.StandardError.ReadToEnd());
}
}
public static Dictionary<string, ZeroTierNetwork> Networks
{
get
{
Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>();
var p = Run("listnetworks");
var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
foreach (var line in lines)
{
var l = line.Replace("\r", "");
if (!string.IsNullOrWhiteSpace(l))
{
var n = new ZeroTierNetwork(l);
networks.Add(n.ID, n);
}
}
return networks;
}
}
private static Process Run(string args)
{
var p = new Process();
p.StartInfo = new ProcessStartInfo()
{
FileName = _path,
Arguments = _arg + args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
p.Start();
p.WaitForExit();
return p;
}
private static string RunCommand(string command)
{
var p = Run(command);
return p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
}
public static void Check()
{
}
}
}

View File

@ -0,0 +1,71 @@
using GTA.Math;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Text;
namespace RageCoop.Core
{
internal static class PacketExtensions
{
#region MESSAGE-READ
public static Vector3 ReadVector3(this NetIncomingMessage m)
{
return new Vector3
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
};
}
public static Vector2 ReadVector2(this NetIncomingMessage m)
{
return new Vector2
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
};
}
public static Quaternion ReadQuaternion(this NetIncomingMessage m)
{
return new Quaternion
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
W = m.ReadFloat(),
};
}
public static byte[] ReadByteArray(this NetIncomingMessage m)
{
return m.ReadBytes(m.ReadInt32());
}
#endregion
#region MESSAGE-WRITE
public static void Write(this NetOutgoingMessage m, Vector3 v)
{
m.Write(v.X);
m.Write(v.Y);
m.Write(v.Z);
}
public static void Write(this NetOutgoingMessage m, Quaternion q)
{
m.Write(q.X);
m.Write(q.Y);
m.Write(q.Z);
m.Write(q.W);
}
public static void WriteByteArray(this NetOutgoingMessage m, byte[] b)
{
m.Write(b.Length);
m.Write(b);
}
#endregion
internal static bool IsSyncEvent(this PacketType p)
{
return (30 <= (byte)p) && ((byte)p <= 40);
}
}
}

View File

@ -0,0 +1,56 @@
using Lidgren.Network;
using System;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class ChatMessage : Packet
{
public override PacketType Type => PacketType.ChatMessage;
private readonly Func<string, byte[]> crypt;
private readonly Func<byte[], byte[]> decrypt;
public ChatMessage(Func<string, byte[]> crypter)
{
crypt = crypter;
}
public ChatMessage(Func<byte[], byte[]> decrypter)
{
decrypt = decrypter;
}
public string Username { get; set; }
public string Message { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
// Write Username
m.Write(Username);
// Write Message
m.WriteByteArray(crypt(Message));
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
// Read username
Username = m.ReadString();
Message = decrypt(m.ReadByteArray()).GetString();
#endregion
}
}
}
}

View File

@ -1,7 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal partial class Packets internal partial class Packets
@ -9,89 +7,79 @@ namespace RageCoop.Core
internal class CustomEvent : Packet internal class CustomEvent : Packet
{ {
public CustomEvent(Func<byte,BitReader,object> onResolve = null,bool queued=false) public override PacketType Type => (_queued ? PacketType.CustomEventQueued : PacketType.CustomEvent);
public CustomEvent(Func<byte, NetIncomingMessage, object> onResolve = null, bool queued = false)
{ {
_resolve = onResolve; _resolve = onResolve;
_queued = queued; _queued = queued;
} }
private bool _queued; private readonly bool _queued;
private Func<byte, BitReader, object> _resolve { get; set; } private Func<byte, NetIncomingMessage, object> _resolve { get; set; }
public int Hash { get; set; } public int Hash { get; set; }
public bool IsStaged { get; set; }=false;
public object[] Args { get; set; } public object[] Args { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
Args = Args ?? new object[] { }; Args = Args ?? new object[] { };
message.Write(_queued ? (byte)PacketType.CustomEventQueued: (byte)PacketType.CustomEvent);
List<byte> result = new List<byte>(); m.Write(Hash);
result.AddInt(Hash); m.Write(Args.Length);
result.AddInt(Args.Length);
(byte, byte[]) tup;
foreach (var arg in Args) foreach (var arg in Args)
{ {
tup=CoreUtils.GetBytesFromObject(arg); CoreUtils.GetBytesFromObject(arg, m);
if (tup.Item1==0||tup.Item2==null) }
}
public override void Deserialize(NetIncomingMessage m)
{ {
throw new ArgumentException($"Object of type {arg.GetType()} is not supported");
}
result.Add(tup.Item1);
result.AddRange(tup.Item2);
}
message.Write(result.Count);
message.Write(result.ToArray());
}
public override void Unpack(byte[] array) Hash = m.ReadInt32();
{ var len = m.ReadInt32();
BitReader reader = new BitReader(array);
Hash = reader.ReadInt();
var len=reader.ReadInt();
Args = new object[len]; Args = new object[len];
for (int i = 0; i < len; i++) for (int i = 0; i < len; i++)
{ {
byte type = reader.ReadByte(); byte type = m.ReadByte();
switch (type) switch (type)
{ {
case 0x01: case 0x01:
Args[i]=reader.ReadByte(); break; Args[i] = m.ReadByte(); break;
case 0x02: case 0x02:
Args[i]=reader.ReadShort(); break; Args[i] = m.ReadInt32(); break;
case 0x03: case 0x03:
Args[i]=reader.ReadUShort(); break; Args[i] = m.ReadUInt16(); break;
case 0x04: case 0x04:
Args[i]=reader.ReadInt(); break; Args[i] = m.ReadInt32(); break;
case 0x05: case 0x05:
Args[i]=reader.ReadUInt(); break; Args[i] = m.ReadUInt32(); break;
case 0x06: case 0x06:
Args[i]=reader.ReadLong(); break; Args[i] = m.ReadInt64(); break;
case 0x07: case 0x07:
Args[i]=reader.ReadULong(); break; Args[i] = m.ReadUInt64(); break;
case 0x08: case 0x08:
Args[i]=reader.ReadFloat(); break; Args[i] = m.ReadFloat(); break;
case 0x09: case 0x09:
Args[i]=reader.ReadBool(); break; Args[i] = m.ReadBoolean(); break;
case 0x10: case 0x10:
Args[i]=reader.ReadString(); break; Args[i] = m.ReadString(); break;
case 0x11: case 0x11:
Args[i]=reader.ReadVector3(); break; Args[i] = m.ReadVector3(); break;
case 0x12: case 0x12:
Args[i]=reader.ReadQuaternion(); break; Args[i] = m.ReadQuaternion(); break;
case 0x13: case 0x13:
Args[i]=(GTA.Model)reader.ReadInt(); break; Args[i] = (GTA.Model)m.ReadInt32(); break;
case 0x14: case 0x14:
Args[i]=reader.ReadVector2(); break; Args[i] = m.ReadVector2(); break;
case 0x15:
Args[i] = m.ReadByteArray(); break;
default: default:
if (_resolve == null) if (_resolve == null)
{ {
throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}"); throw new InvalidOperationException($"Unexpected type: {type}");
} }
else else
{ {
Args[i]=_resolve(type, reader); break; Args[i] = _resolve(type, m); break;
} }
} }
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -18,166 +15,113 @@ namespace RageCoop.Core
{ {
internal class FileTransferRequest : Packet internal class FileTransferRequest : Packet
{ {
public override PacketType Type => PacketType.FileTransferRequest;
public int ID { get; set; } public int ID { get; set; }
public string Name { get; set; } public string Name { get; set; }
public long FileLength { get; set; } public long FileLength { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.FileTransferRequest);
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
// The name of the file // The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(Name); m.Write(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
// The length of the file // The length of the file
byteArray.AddRange(BitConverter.GetBytes(FileLength)); m.Write(FileLength);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadInt();
int nameArrayLength = reader.ReadInt(); ID = m.ReadInt32();
Name = reader.ReadString(nameArrayLength); Name = m.ReadString();
FileLength = reader.ReadLong(); FileLength = m.ReadInt64();
#endregion
} }
} }
internal class FileTransferResponse : Packet internal class FileTransferResponse : Packet
{ {
public override PacketType Type => PacketType.FileTransferResponse;
public int ID { get; set; } public int ID { get; set; }
public FileResponse Response { get; set; } public FileResponse Response { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
message.Write((byte)PacketType.FileTransferResponse);
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
byteArray.Add((byte)Response); m.Write((byte)Response);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt(); ID = m.ReadInt32();
Response = (FileResponse)reader.ReadByte(); Response = (FileResponse)m.ReadByte();
} }
} }
internal class FileTransferChunk : Packet internal class FileTransferChunk : Packet
{ {
public override PacketType Type => PacketType.FileTransferChunk;
public int ID { get; set; } public int ID { get; set; }
public byte[] FileChunk { get; set; } public byte[] FileChunk { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.FileTransferChunk);
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
m.WriteByteArray(FileChunk);
// The chunk of the file
byteArray.AddRange(BitConverter.GetBytes(FileChunk.Length));
byteArray.AddRange(FileChunk);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadInt(); ID = m.ReadInt32();
int chunkLength = reader.ReadInt(); FileChunk = m.ReadByteArray();
FileChunk = reader.ReadByteArray(chunkLength);
#endregion
} }
} }
internal class FileTransferComplete : Packet internal class FileTransferComplete : Packet
{ {
public override PacketType Type => PacketType.FileTransferComplete;
public int ID { get; set; } public int ID { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.FileTransferComplete);
List<byte> byteArray = new List<byte>();
// The ID for the download // The ID for the download
byteArray.AddInt(ID); m.Write(ID);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadInt();
#endregion ID = m.ReadInt32();
} }
} }
internal class AllResourcesSent : Packet internal class AllResourcesSent : Packet
{ {
public override void Pack(NetOutgoingMessage message) public override PacketType Type => PacketType.AllResourcesSent;
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.AllResourcesSent);
message.Write(0);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
#endregion
}
} }
} }
} }

View File

@ -0,0 +1,67 @@
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class HolePunchInit : Packet
{
public override PacketType Type => PacketType.HolePunchInit;
public int TargetID { get; set; }
public string TargetInternal { get; set; }
public string TargetExternal { get; set; }
public bool Connect { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(TargetID);
m.Write(TargetInternal);
m.Write(TargetExternal);
m.Write(Connect);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
TargetID = m.ReadInt32();
TargetInternal = m.ReadString();
TargetExternal = m.ReadString();
Connect = m.ReadBoolean();
#endregion
}
}
internal class HolePunch : Packet
{
public override PacketType Type => PacketType.HolePunch;
public int Puncher { get; set; }
/// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed
/// </summary>
public byte Status { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(Puncher);
m.Write(Status);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
Puncher = m.ReadInt32();
Status = m.ReadByte();
#endregion
}
}
}
}

View File

@ -0,0 +1,48 @@
using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core
{
internal partial class Packets
{
/// <summary>
/// Request direct connection to another client
/// </summary>
internal class ConnectionRequest : Packet
{
public int TargetID { get; set; }
public override PacketType Type => PacketType.ConnectionRequest;
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>(10);
m.Write(TargetID);
}
public override void Deserialize(NetIncomingMessage m)
{
TargetID = m.ReadInt32();
}
}
/// <summary>
/// Sent to the host when a direct connection has been established
/// </summary>
internal class P2PConnect : Packet
{
public int ID { get; set; }
public override PacketType Type => PacketType.P2PConnect;
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>(10);
m.Write(ID);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
}
}
}
}

View File

@ -1,9 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic; using System;
using System.Text;
using Lidgren.Network;
using Newtonsoft.Json;
using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -17,12 +13,9 @@ namespace RageCoop.Core
PublicKeyResponse = 5, PublicKeyResponse = 5,
Request = 6, Request = 6,
Response = 7, Response = 7,
PingPong = 8,
HandshakeSuccess = 9,
ChatMessage = 10, ChatMessage = 10,
// NativeCall=11,
// NativeResponse=12,
// Mod=13,
// CleanUpWorld=14,
FileTransferChunk = 11, FileTransferChunk = 11,
FileTransferRequest = 12, FileTransferRequest = 12,
@ -32,53 +25,47 @@ namespace RageCoop.Core
CustomEvent = 16, CustomEvent = 16,
CustomEventQueued = 17, CustomEventQueued = 17,
#region Sync
#region INTERVAL ConnectionRequest = 18,
VehicleSync = 20, P2PConnect = 19,
VehicleStateSync = 21, HolePunchInit = 20,
PedSync = 22, HolePunch = 21,
PedStateSync = 23,
ProjectileSync=24, Voice = 22,
#region Sync
PedSync = 23,
VehicleSync = 24,
ProjectileSync = 25,
#endregion #endregion
#region EVENT #region EVENT
PedKilled = 30, PedKilled = 30,
BulletShot = 31, BulletShot = 31,
EnteringVehicle=32, VehicleBulletShot = 32,
LeaveVehicle = 33,
EnteredVehicle=34,
OwnerChanged = 35, OwnerChanged = 35,
VehicleBulletShot = 36,
NozzleTransform = 37, NozzleTransform = 37,
#endregion #endregion
#endregion
Unknown = 255 Unknown = 255
} }
internal static class PacketExtensions
{
internal static bool IsSyncEvent(this PacketType p)
{
return (30<=(byte)p)&&((byte)p<=40);
}
}
internal enum ConnectionChannel internal enum ConnectionChannel
{ {
Default = 0, Default = 0,
Chat = 5, Chat = 1,
Native = 6, Voice = 2,
Mod = 7, Native = 3,
File = 8, Mod = 4,
Event = 9, File = 5,
RequestResponse=10, Event = 6,
VehicleSync=20, RequestResponse = 7,
PedSync=21, PingPong = 8,
ProjectileSync = 22, VehicleSync = 9,
SyncEvents =30, PedSync = 10,
ProjectileSync = 11,
SyncEvents = 12,
} }
[Flags] [Flags]
@ -96,8 +83,21 @@ namespace RageCoop.Core
IsOnLadder = 1 << 8, IsOnLadder = 1 << 8,
IsVaulting = 1 << 9, IsVaulting = 1 << 9,
IsInCover = 1 << 10, IsInCover = 1 << 10,
IsInLowCover = 1 << 11,
IsInCoverFacingLeft = 1 << 12,
IsBlindFiring = 1 << 13,
IsInvincible = 1 << 14,
IsFullSync = 1 << 15,
} }
internal enum ProjectileDataFlags : byte
{
None = 0,
Exploded = 1 << 0,
IsAttached = 1 << 1,
IsOrgin = 1 << 2,
IsShotByVehicle = 1 << 3,
}
#region ===== VEHICLE DATA ===== #region ===== VEHICLE DATA =====
internal enum VehicleDataFlags : ushort internal enum VehicleDataFlags : ushort
{ {
@ -110,11 +110,14 @@ namespace RageCoop.Core
IsDead = 1 << 5, IsDead = 1 << 5,
IsHornActive = 1 << 6, IsHornActive = 1 << 6,
IsTransformed = 1 << 7, IsTransformed = 1 << 7,
RoofOpened = 1 << 8, IsParachuteActive = 1 << 8,
OnTurretSeat = 1 << 9, IsRocketBoostActive = 1 << 9,
IsAircraft = 1 << 10, IsAircraft = 1 << 10,
IsDeluxoHovering = 1 << 11, IsDeluxoHovering = 1 << 11,
HasRoof = 1 << 12, HasRoof = 1 << 12,
IsFullSync = 1 << 13,
IsOnFire = 1 << 14,
Repaired = 1 << 15,
} }
internal enum PlayerConfigFlags : byte internal enum PlayerConfigFlags : byte
@ -135,101 +138,22 @@ namespace RageCoop.Core
} }
#endregion #endregion
interface IPacket internal interface IPacket
{ {
void Pack(NetOutgoingMessage message); PacketType Type { get; }
void Unpack(byte[] array);
void Deserialize(NetIncomingMessage m);
} }
internal abstract class Packet : IPacket internal abstract class Packet : IPacket
{ {
public abstract void Pack(NetOutgoingMessage message); public abstract PacketType Type { get; }
public abstract void Unpack(byte[] array); public void Pack(NetOutgoingMessage m)
}
internal partial class Packets
{ {
m.Write((byte)Type);
internal class ChatMessage : Packet Serialize(m);
{
public string Username { get; set; }
public string Message { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.ChatMessage);
List<byte> byteArray = new List<byte>();
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
byte[] messageBytes = Encoding.UTF8.GetBytes(Message);
// Write UsernameLength
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
// Write Username
byteArray.AddRange(usernameBytes);
// Write MessageLength
byteArray.AddRange(BitConverter.GetBytes(messageBytes.Length));
// Write Message
byteArray.AddRange(messageBytes);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read username
int usernameLength = reader.ReadInt();
Username = reader.ReadString(usernameLength);
// Read message
int messageLength = reader.ReadInt();
Message = reader.ReadString(messageLength);
#endregion
}
}
}
internal static class CoopSerializer
{
/// <summary>
/// ?
/// </summary>
public static byte[] Serialize(this object obj)
{
if (obj == null)
{
return null;
}
string jsonString = JsonConvert.SerializeObject(obj);
return System.Text.Encoding.UTF8.GetBytes(jsonString);
}
/// <summary>
/// ?
/// </summary>
public static T Deserialize<T>(this byte[] bytes) where T : class
{
if (bytes == null)
{
return null;
}
var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
return JsonConvert.DeserializeObject<T>(jsonString);
} }
protected virtual void Serialize(NetOutgoingMessage m) { }
public virtual void Deserialize(NetIncomingMessage m) { }
} }
} }

View File

@ -1,251 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using GTA;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
/// <summary>
/// For non-critical properties, synced every 20 frames.
/// </summary>
internal class PedStateSync : Packet
{
public int ID { get; set; }
public int ModelHash { get; set; }
public byte[] Clothes { get; set; }
public int OwnerID { get; set; }
public Dictionary<uint, bool> WeaponComponents { get; set; }
public byte WeaponTint { get;set; }
public BlipColor BlipColor { get; set; } = (BlipColor)255;
public BlipSprite BlipSprite { get; set; }= 0;
public float BlipScale { get; set; } = 1;
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PedStateSync);
List<byte> byteArray = new List<byte>();
// Write ID
byteArray.AddInt(ID);
// Write model hash
byteArray.AddInt(ModelHash);
byteArray.AddRange(Clothes);
//Write OwnerID for this ped
byteArray.AddRange(BitConverter.GetBytes(OwnerID));
// Write player weapon components
if (WeaponComponents != null)
{
byteArray.Add(0x01);
byteArray.AddRange(BitConverter.GetBytes((ushort)WeaponComponents.Count));
foreach (KeyValuePair<uint, bool> component in WeaponComponents)
{
byteArray.AddRange(BitConverter.GetBytes(component.Key));
byteArray.AddRange(BitConverter.GetBytes(component.Value));
}
}
else
{
// Player weapon doesn't have any components
byteArray.Add(0x00);
}
byteArray.Add(WeaponTint);
byteArray.Add((byte)BlipColor);
if ((byte)BlipColor!=255)
{
byteArray.AddUshort((ushort)BlipSprite);
byteArray.AddFloat(BlipScale);
}
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle
ID = reader.ReadInt();
// Read player model hash
ModelHash = reader.ReadInt();
// Read player clothes
Clothes =reader.ReadByteArray(36);
// Read ped OwnerID
OwnerID= reader.ReadInt();
// Read player weapon components
if (reader.ReadBool())
{
WeaponComponents = new Dictionary<uint, bool>();
ushort comCount = reader.ReadUShort();
for (ushort i = 0; i < comCount; i++)
{
WeaponComponents.Add(reader.ReadUInt(), reader.ReadBool());
}
}
WeaponTint=reader.ReadByte();
BlipColor=(BlipColor)reader.ReadByte();
if ((byte)BlipColor!=255)
{
BlipSprite=(BlipSprite)reader.ReadUShort();
BlipScale=reader.ReadFloat();
}
#endregion
}
}
internal class PedSync : Packet
{
public int ID { get; set; }
public PedDataFlags Flag { get; set; }
public int Health { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public Vector3 Velocity { get; set; }
public Vector3 RotationVelocity { get; set; }
public byte Speed { get; set; }
public Vector3 AimCoords { get; set; }
public uint CurrentWeaponHash { get; set; }
public float Heading { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PedSync);
List<byte> byteArray = new List<byte>();
// Write ped ID
byteArray.AddInt(ID);
// Write ped flags
byteArray.AddRange(BitConverter.GetBytes((ushort)Flag));
// Write ped health
byteArray.AddRange(BitConverter.GetBytes(Health));
// Write ped position
byteArray.AddVector3(Position);
// Write ped rotation
byteArray.AddVector3(Rotation);
// Write ped velocity
byteArray.AddVector3(Velocity);
if (Flag.HasPedFlag(PedDataFlags.IsRagdoll))
{
byteArray.AddVector3(RotationVelocity);
}
// Write ped speed
byteArray.Add(Speed);
// Write ped weapon hash
byteArray.AddRange(BitConverter.GetBytes(CurrentWeaponHash));
if (Flag.HasPedFlag(PedDataFlags.IsAiming))
{
// Write ped aim coords
byteArray.AddVector3(AimCoords);
}
byteArray.AddFloat(Heading);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle
ID = reader.ReadInt();
// Read player flags
Flag = (PedDataFlags)reader.ReadUShort();
// Read player health
Health = reader.ReadInt();
// Read player position
Position = reader.ReadVector3();
// Read player rotation
Rotation = reader.ReadVector3();
// Read player velocity
Velocity = reader.ReadVector3();
// Read rotation velocity if in ragdoll
if (Flag.HasPedFlag(PedDataFlags.IsRagdoll))
{
RotationVelocity=reader.ReadVector3();
}
// Read player speed
Speed = reader.ReadByte();
// Read player weapon hash
CurrentWeaponHash = reader.ReadUInt();
// Try to read aim coords
if (Flag.HasPedFlag(PedDataFlags.IsAiming))
{
// Read player aim coords
AimCoords = reader.ReadVector3();
}
Heading=reader.ReadFloat();
#endregion
}
}
}
}

View File

@ -0,0 +1,213 @@
using GTA;
using GTA.Math;
using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class PedSync : Packet
{
public override PacketType Type => PacketType.PedSync;
public int ID { get; set; }
public int OwnerID { get; set; }
public int VehicleID { get; set; }
public VehicleSeat Seat { get; set; }
public PedDataFlags Flags { get; set; }
public int Health { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public Vector3 Velocity { get; set; }
#region RAGDOLL
public Vector3 HeadPosition { get; set; }
public Vector3 RightFootPosition { get; set; }
public Vector3 LeftFootPosition { get; set; }
#endregion
public byte Speed { get; set; }
public Vector3 AimCoords { get; set; }
public float Heading { get; set; }
#region FULL
public int ModelHash { get; set; }
public uint CurrentWeaponHash { get; set; }
public byte[] Clothes { get; set; }
public Dictionary<uint, bool> WeaponComponents { get; set; }
public byte WeaponTint { get; set; }
public BlipColor BlipColor { get; set; } = (BlipColor)255;
public BlipSprite BlipSprite { get; set; } = 0;
public float BlipScale { get; set; } = 1;
#endregion
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(ID);
m.Write(OwnerID);
m.Write((ushort)Flags);
m.Write(Health);
m.Write(Speed);
if (Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{
m.Write(HeadPosition);
m.Write(RightFootPosition);
m.Write(LeftFootPosition);
}
else
{
if (Speed >= 4)
{
m.Write(VehicleID);
m.Write((byte)(Seat + 3));
}
m.Write(Position);
}
m.Write(Rotation);
m.Write(Velocity);
if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{
m.Write(AimCoords);
}
m.Write(Heading);
if (Flags.HasPedFlag(PedDataFlags.IsFullSync))
{
m.Write(ModelHash);
m.Write(CurrentWeaponHash);
m.Write(Clothes);
if (WeaponComponents != null)
{
m.Write(true);
m.Write((ushort)WeaponComponents.Count);
foreach (KeyValuePair<uint, bool> component in WeaponComponents)
{
m.Write(component.Key);
m.Write(component.Value);
}
}
else
{
// Player weapon doesn't have any components
m.Write(false);
}
m.Write(WeaponTint);
m.Write((byte)BlipColor);
if ((byte)BlipColor != 255)
{
m.Write((ushort)BlipSprite);
m.Write(BlipScale);
}
}
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
ID = m.ReadInt32();
OwnerID = m.ReadInt32();
Flags = (PedDataFlags)m.ReadUInt16();
Health = m.ReadInt32();
Speed = m.ReadByte();
if (Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{
HeadPosition = m.ReadVector3();
RightFootPosition = m.ReadVector3();
LeftFootPosition = m.ReadVector3();
Position = HeadPosition;
}
else
{
// Vehicle related
if (Speed >= 4)
{
VehicleID = m.ReadInt32();
Seat = (VehicleSeat)(m.ReadByte() - 3);
}
// Read player position
Position = m.ReadVector3();
}
Rotation = m.ReadVector3();
Velocity = m.ReadVector3();
if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{
// Read player aim coords
AimCoords = m.ReadVector3();
}
Heading = m.ReadFloat();
if (Flags.HasPedFlag(PedDataFlags.IsFullSync))
{
// Read player model hash
ModelHash = m.ReadInt32();
// Read player weapon hash
CurrentWeaponHash = m.ReadUInt32();
// Read player clothes
Clothes = m.ReadBytes(36);
// Read player weapon components
if (m.ReadBoolean())
{
WeaponComponents = new Dictionary<uint, bool>();
ushort comCount = m.ReadUInt16();
for (ushort i = 0; i < comCount; i++)
{
WeaponComponents.Add(m.ReadUInt32(), m.ReadBoolean());
}
}
WeaponTint = m.ReadByte();
BlipColor = (BlipColor)m.ReadByte();
if ((byte)BlipColor != 255)
{
BlipSprite = (BlipSprite)m.ReadUInt16();
BlipScale = m.ReadFloat();
}
}
#endregion
}
}
}
}

View File

@ -1,15 +1,19 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
using System.Net;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal partial class Packets internal partial class Packets
{ {
internal class Handshake : Packet internal struct PlayerData
{ {
public int ID;
public string Username;
}
public class Handshake : Packet
{
public override PacketType Type => PacketType.Handshake;
public int PedID { get; set; } public int PedID { get; set; }
public string Username { get; set; } public string Username { get; set; }
@ -29,227 +33,209 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// The password hash with client Aes /// The password hash with client Aes
/// </summary> /// </summary>
public byte[] PassHashEncrypted { get; set; } public byte[] PasswordEncrypted { get; set; }
public override void Pack(NetOutgoingMessage message) public IPEndPoint InternalEndPoint { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.Handshake);
List<byte> byteArray = new List<byte>();
// Write Player Ped ID // Write Player Ped ID
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Write Username // Write Username
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username); m.Write(Username);
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
byteArray.AddRange(usernameBytes);
// Write ModVersion // Write ModVersion
byte[] modVersionBytes = Encoding.UTF8.GetBytes(ModVersion); m.Write(ModVersion);
byteArray.AddRange(BitConverter.GetBytes(modVersionBytes.Length));
byteArray.AddRange(modVersionBytes); m.Write(InternalEndPoint.ToString());
// Write AesKeyCrypted // Write AesKeyCrypted
byteArray.AddArray(AesKeyCrypted); m.WriteByteArray(AesKeyCrypted);
// Write AesIVCrypted // Write AesIVCrypted
byteArray.AddArray(AesIVCrypted); m.WriteByteArray(AesIVCrypted);
// Write PassHash // Write PassHash
byteArray.AddArray(PassHashEncrypted); m.WriteByteArray(PasswordEncrypted);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle // Read player netHandle
PedID = reader.ReadInt(); PedID = m.ReadInt32();
// Read Username // Read Username
Username = reader.ReadString(reader.ReadInt()); Username = m.ReadString();
// Read ModVersion // Read ModVersion
ModVersion = reader.ReadString(reader.ReadInt()); ModVersion = m.ReadString();
AesKeyCrypted=reader.ReadByteArray(); InternalEndPoint = CoreUtils.StringToEndPoint(m.ReadString());
AesIVCrypted=reader.ReadByteArray(); AesKeyCrypted = m.ReadByteArray();
AesIVCrypted = m.ReadByteArray();
PassHashEncrypted=reader.ReadByteArray(); PasswordEncrypted = m.ReadByteArray();
#endregion #endregion
} }
} }
public class HandshakeSuccess : Packet
{
public PlayerData[] Players { get; set; }
public override PacketType Type => PacketType.HandshakeSuccess;
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(Players.Length);
foreach (var p in Players)
{
m.Write(p.ID);
m.Write(p.Username);
}
}
public override void Deserialize(NetIncomingMessage m)
{
Players = new PlayerData[m.ReadInt32()];
for (int i = 0; i < Players.Length; i++)
{
Players[i] = new PlayerData()
{
ID = m.ReadInt32(),
Username = m.ReadString(),
};
}
}
}
public class PlayerConnect : Packet public class PlayerConnect : Packet
{ {
public override PacketType Type => PacketType.PlayerConnect;
public int PedID { get; set; } public int PedID { get; set; }
public string Username { get; set; } public string Username { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PlayerConnect);
List<byte> byteArray = new List<byte>();
// Write NetHandle // Write NetHandle
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Get Username bytes m.Write(Username);
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
// Write UsernameLength
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
// Write Username
byteArray.AddRange(usernameBytes);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle // Read player netHandle
PedID = reader.ReadInt(); PedID = m.ReadInt32();
// Read Username // Read Username
int usernameLength = reader.ReadInt(); Username = m.ReadString();
Username = reader.ReadString(usernameLength);
#endregion #endregion
} }
} }
public class PlayerDisconnect : Packet public class PlayerDisconnect : Packet
{ {
public override PacketType Type => PacketType.PlayerDisconnect;
public int PedID { get; set; } public int PedID { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PlayerDisconnect);
List<byte> byteArray = new List<byte>(); m.Write(PedID);
byteArray.AddRange(BitConverter.GetBytes(PedID));
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
PedID = reader.ReadInt(); PedID = m.ReadInt32();
#endregion #endregion
} }
} }
public class PlayerInfoUpdate : Packet public class PlayerInfoUpdate : Packet
{ {
public override PacketType Type => PacketType.PlayerInfoUpdate;
/// <summary> /// <summary>
/// Ped ID for this Player /// Ped ID for this Player
/// </summary> /// </summary>
public int PedID { get; set; } public int PedID { get; set; }
public string Username { get; set; } public string Username { get; set; }
public float Latency { get; set; } public float Latency { get; set; }
public override void Pack(NetOutgoingMessage message) public Vector3 Position { get; set; }
public bool IsHost;
protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PlayerInfoUpdate);
List<byte> byteArray = new List<byte>();
// Write ID // Write ID
byteArray.AddRange(BitConverter.GetBytes(PedID)); m.Write(PedID);
// Get Username bytes
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
// Write UsernameLength
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
// Write Username // Write Username
byteArray.AddRange(usernameBytes); m.Write(Username);
// Write Latency // Write Latency
byteArray.AddFloat(Latency); m.Write(Latency);
byte[] result = byteArray.ToArray(); m.Write(Position);
message.Write(result.Length); m.Write(IsHost);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
// Read player ID // Read player ID
PedID = reader.ReadInt(); PedID = m.ReadInt32();
// Read Username // Read Username
int usernameLength = reader.ReadInt(); Username = m.ReadString();
Username = reader.ReadString(usernameLength);
Latency=reader.ReadFloat(); Latency = m.ReadFloat();
Position = m.ReadVector3();
IsHost = m.ReadBoolean();
} }
} }
public class PublicKeyResponse : Packet public class PublicKeyResponse : Packet
{ {
public override PacketType Type => PacketType.PublicKeyResponse;
public byte[] Modulus; public byte[] Modulus;
public byte[] Exponent; public byte[] Exponent;
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PublicKeyResponse);
List<byte> byteArray = new List<byte>();
byteArray.AddArray(Modulus);
byteArray.AddArray(Exponent);
byte[] result = byteArray.ToArray();
message.Write(result.Length); m.WriteByteArray(Modulus);
message.Write(result);
#endregion m.WriteByteArray(Exponent);
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
var reader=new BitReader(array);
Modulus=reader.ReadByteArray(); Modulus = m.ReadByteArray();
Exponent=reader.ReadByteArray(); Exponent = m.ReadByteArray();
#endregion #endregion
} }
@ -257,15 +243,7 @@ namespace RageCoop.Core
public class PublicKeyRequest : Packet public class PublicKeyRequest : Packet
{ {
public override void Pack(NetOutgoingMessage message) public override PacketType Type => PacketType.PublicKeyRequest;
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PublicKeyRequest);
#endregion
}
public override void Unpack(byte[] array)
{
}
} }
} }
} }

View File

@ -1,7 +1,4 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,7 +7,7 @@ namespace RageCoop.Core
{ {
internal class ProjectileSync : Packet internal class ProjectileSync : Packet
{ {
public override PacketType Type => PacketType.ProjectileSync;
public int ID { get; set; } public int ID { get; set; }
public int ShooterID { get; set; } public int ShooterID { get; set; }
@ -22,70 +19,61 @@ namespace RageCoop.Core
public Vector3 Velocity { get; set; } public Vector3 Velocity { get; set; }
public bool Exploded { get; set; } public ProjectileDataFlags Flags { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.ProjectileSync);
List<byte> byteArray = new List<byte>();
// Write id // Write id
byteArray.AddInt(ID); m.Write(ID);
// Write ShooterID // Write ShooterID
byteArray.AddInt(ShooterID); m.Write(ShooterID);
byteArray.AddUint(WeaponHash); m.Write(WeaponHash);
// Write position // Write position
byteArray.AddVector3(Position); m.Write(Position);
// Write rotation // Write rotation
byteArray.AddVector3(Rotation); m.Write(Rotation);
// Write velocity // Write velocity
byteArray.AddVector3(Velocity); m.Write(Velocity);
m.Write((byte)Flags);
if (Exploded) { byteArray.Add(1); }
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read id // Read id
ID = reader.ReadInt(); ID = m.ReadInt32();
// Read ShooterID // Read ShooterID
ShooterID= reader.ReadInt(); ShooterID = m.ReadInt32();
WeaponHash= reader.ReadUInt(); WeaponHash = m.ReadUInt32();
// Read position // Read position
Position = reader.ReadVector3(); Position = m.ReadVector3();
// Read rotation // Read rotation
Rotation = reader.ReadVector3(); Rotation = m.ReadVector3();
// Read velocity // Read velocity
Velocity =reader.ReadVector3(); Velocity = m.ReadVector3();
if (reader.CanRead(1)) Flags = (ProjectileDataFlags)m.ReadByte();
{
Exploded=true;
}
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,6 +8,7 @@ namespace RageCoop.Core
internal class BulletShot : Packet internal class BulletShot : Packet
{ {
public override PacketType Type => PacketType.BulletShot;
public int OwnerID { get; set; } public int OwnerID { get; set; }
public uint WeaponHash { get; set; } public uint WeaponHash { get; set; }
@ -18,49 +16,44 @@ namespace RageCoop.Core
public Vector3 StartPosition { get; set; } public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; } public Vector3 EndPosition { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.BulletShot);
List<byte> byteArray = new List<byte>();
// Write OwnerID // Write OwnerID
byteArray.AddRange(BitConverter.GetBytes(OwnerID)); m.Write(OwnerID);
// Write weapon hash // Write weapon hash
byteArray.AddRange(BitConverter.GetBytes(WeaponHash)); m.Write(WeaponHash);
// Write StartPosition // Write StartPosition
byteArray.AddVector3(StartPosition); m.Write(StartPosition);
// Write EndPosition // Write EndPosition
byteArray.AddVector3(EndPosition); m.Write(EndPosition);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read OwnerID // Read OwnerID
OwnerID=reader.ReadInt(); OwnerID = m.ReadInt32();
// Read WeponHash // Read WeponHash
WeaponHash=reader.ReadUInt(); WeaponHash = m.ReadUInt32();
// Read StartPosition // Read StartPosition
StartPosition=reader.ReadVector3(); StartPosition = m.ReadVector3();
// Read EndPosition // Read EndPosition
EndPosition=reader.ReadVector3(); EndPosition = m.ReadVector3();
#endregion #endregion
} }
} }

View File

@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class EnteredVehicle : Packet
{
public int PedID { get; set; }
public int VehicleID { get; set; }
public short VehicleSeat { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.EnteredVehicle);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(PedID);
byteArray.AddInt(VehicleID);
byteArray.AddInt(VehicleSeat);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
PedID=reader.ReadInt();
VehicleID=reader.ReadInt();
VehicleSeat=reader.ReadShort();
#endregion
}
}
}
}

View File

@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class EnteringVehicle : Packet
{
public int PedID { get; set; }
public int VehicleID { get; set; }
public short VehicleSeat { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.EnteringVehicle);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(PedID);
byteArray.AddInt(VehicleID);
byteArray.AddInt(VehicleSeat);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
PedID=reader.ReadInt();
VehicleID=reader.ReadInt();
VehicleSeat=reader.ReadShort();
#endregion
}
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class LeaveVehicle : Packet
{
public int ID { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.LeaveVehicle);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(ID);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID=reader.ReadInt();
#endregion
}
}
}
}

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,33 +7,29 @@ namespace RageCoop.Core
{ {
internal class NozzleTransform : Packet internal class NozzleTransform : Packet
{ {
public override PacketType Type => PacketType.NozzleTransform;
public int VehicleID { get; set; } public int VehicleID { get; set; }
public bool Hover { get; set; } public bool Hover { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.NozzleTransform);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VehicleID);
if (Hover) { byteArray.Add(1); }
byte[] result = byteArray.ToArray(); m.Write(VehicleID);
m.Write(Hover);
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VehicleID=reader.ReadInt(); VehicleID = m.ReadInt32();
Hover=reader.CanRead(1); Hover = m.ReadBoolean();
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,34 +8,24 @@ namespace RageCoop.Core
internal class OwnerChanged : Packet internal class OwnerChanged : Packet
{ {
public override PacketType Type => PacketType.OwnerChanged;
public int ID { get; set; } public int ID { get; set; }
public int NewOwnerID { get; set; } public int NewOwnerID { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage m.Write(ID);
message.Write((byte)PacketType.OwnerChanged); m.Write(NewOwnerID);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(ID);
byteArray.AddInt(NewOwnerID);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID=reader.ReadInt();
NewOwnerID=reader.ReadInt(); ID = m.ReadInt32();
NewOwnerID = m.ReadInt32();
#endregion #endregion
} }

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,29 +8,25 @@ namespace RageCoop.Core
internal class PedKilled : Packet internal class PedKilled : Packet
{ {
public override PacketType Type => PacketType.PedKilled;
public int VictimID { get; set; } public int VictimID { get; set; }
public override void Pack(NetOutgoingMessage message) protected override void Serialize(NetOutgoingMessage m)
{ {
#region PacketToNetOutGoingMessage
message.Write((byte)PacketType.PedKilled);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VictimID);
byte[] result = byteArray.ToArray();
message.Write(result.Length); m.Write(VictimID);
message.Write(result);
#endregion
} }
public override void Unpack(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VictimID=reader.ReadInt();
VictimID = m.ReadInt32();
#endregion #endregion
} }

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