129 Commits
1.5.1 ... 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
124 changed files with 4737 additions and 3967 deletions

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
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
on: push
jobs:
build:
runs-on: windows-latest
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: ['6.0.x']
@ -23,16 +19,16 @@ jobs:
- name: Restore dependencies
run: dotnet restore
- name: Restore nuget packages
run: nuget restore
- name: Build client
run: dotnet build RageCoop.Client/RageCoop.Client.csproj --configuration Release -o bin/Release/Client/RageCoop
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
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
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
- 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
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
- uses: vimtor/action-zip@v1
with:
files: bin/Release/Client
@ -40,62 +36,10 @@ jobs:
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/win-x64
files: bin/Release/Server/win-x64
dest: RageCoop.Server-win-x64.zip
- uses: vimtor/action-zip@v1
with:
files: RageCoop.Server/bin/linux-x64
files: bin/Release/Server/linux-x64
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

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
**/packages
**/.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,7 @@
# 🌐 RAGECOOP
[![Downloads][downloads-shield]][downloads-url]
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
@ -9,24 +10,37 @@
# 🧠 That's it
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
- Visual Studio 2022
- .NET 6.0
- .NET Framework 4.8
# 👁 Requirements
- ScriptHookV
- ScriptHookVDotNet 3.6.0 or later
- .NET Framework 4.8 Runtime or SDK
# 📚 Libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.4.0)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/releases/tag/v1.6)
- Lidgren Network Custom (***PRIVATE***)
# 📋 Building the project
You'll need:
- .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)
- [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)
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
- [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
# Features
# 👋 Features
1. Synchronized bullets
2. Synchronized vehicle/player/NPC
@ -35,17 +49,17 @@ RAGECOOP brings multiplayer experience to the story mode, you can complete missi
5. Decent compatibility with other mods, set up a private modded server to have some fun!
6. Weaponized vehicle sync(WIP).
7. Optimization for high-Ping condition, play with friends around the world!
8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.online).
8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.com).
# Known issues
# Known issues
See [Bugs](https://github.com/RAGECOOP/RAGECOOP-V/issues/33)
## Installation
# 🔫 Installation
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)
@ -56,6 +70,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
# 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
- - For testing, ideas, contributions and the first modification with the API
- [crosire](https://github.com/crosire)
@ -64,6 +79,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
- - For the extensive work in LemonUI
# 📝 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
@ -77,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-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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x64.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
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

@ -36,14 +36,14 @@ namespace RageCoop.Client
string s = "";
foreach (KeyValuePair<TimeStamp, long> kvp in d)
{
s+=kvp.Key+":"+kvp.Value+"\n";
s += kvp.Key + ":" + kvp.Value + "\n";
}
return s;
}
public static void ShowTimeStamps()
{
GTA.UI.Notification.Hide(_lastNfHandle);
_lastNfHandle=GTA.UI.Notification.Show(Debug.TimeStamps.Dump());
_lastNfHandle = GTA.UI.Notification.Show(Debug.TimeStamps.Dump());
}
}

View File

@ -16,14 +16,14 @@ namespace RageCoop.Client
public static MuzzleDir Direction = MuzzleDir.Forward;
public DevTool()
{
Tick+=OnTick;
KeyDown+=OnKeyDown;
Tick += OnTick;
KeyDown += OnKeyDown;
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (ToMark==null||(!ToMark.Exists())) { return; }
if (DevToolMenu.Menu.SelectedItem==DevToolMenu.boneIndexItem)
if (ToMark == null || (!ToMark.Exists())) { return; }
if (DevToolMenu.Menu.SelectedItem == DevToolMenu.boneIndexItem)
{
switch (e.KeyCode)
@ -36,7 +36,7 @@ namespace RageCoop.Client
break;
}
}
else if (DevToolMenu.Menu.SelectedItem==DevToolMenu.secondaryBoneIndexItem)
else if (DevToolMenu.Menu.SelectedItem == DevToolMenu.secondaryBoneIndexItem)
{
switch (e.KeyCode)
@ -54,24 +54,24 @@ namespace RageCoop.Client
private static void Update()
{
if (Current>ToMark.Bones.Count-1)
if (Current > ToMark.Bones.Count - 1)
{
Current=0;
Current = 0;
}
else if (Current< 0)
else if (Current < 0)
{
Current=ToMark.Bones.Count-1;
Current = ToMark.Bones.Count - 1;
}
DevToolMenu.boneIndexItem.AltTitle= Current.ToString();
if (Secondary>ToMark.Bones.Count-1)
DevToolMenu.boneIndexItem.AltTitle = Current.ToString();
if (Secondary > ToMark.Bones.Count - 1)
{
Secondary=0;
Secondary = 0;
}
else if (Secondary< 0)
else if (Secondary < 0)
{
Secondary=ToMark.Bones.Count-1;
Secondary = ToMark.Bones.Count - 1;
}
DevToolMenu.secondaryBoneIndexItem.AltTitle= Secondary.ToString();
DevToolMenu.secondaryBoneIndexItem.AltTitle = Secondary.ToString();
}
private static void OnTick(object sender, EventArgs e)
{
@ -87,44 +87,44 @@ namespace RageCoop.Client
private static void Draw(int boneindex)
{
var bone = ToMark.Bones[boneindex];
World.DrawLine(bone.Position, bone.Position+2*bone.ForwardVector, Color.Blue);
World.DrawLine(bone.Position, bone.Position+2*bone.UpVector, Color.Green);
World.DrawLine(bone.Position, bone.Position+2*bone.RightVector, Color.Yellow);
World.DrawLine(bone.Position, bone.Position + 2 * bone.ForwardVector, Color.Blue);
World.DrawLine(bone.Position, bone.Position + 2 * bone.UpVector, Color.Green);
World.DrawLine(bone.Position, bone.Position + 2 * bone.RightVector, Color.Yellow);
Vector3 todraw = bone.ForwardVector;
switch ((byte)Direction)
{
case 0:
todraw=bone.ForwardVector;
todraw = bone.ForwardVector;
break;
case 1:
todraw=bone.RightVector;
todraw = bone.RightVector;
break;
case 2:
todraw=bone.UpVector;
todraw = bone.UpVector;
break;
case 3:
todraw=bone.ForwardVector*-1;
todraw = bone.ForwardVector * -1;
break;
case 4:
todraw=bone.RightVector*-1;
todraw = bone.RightVector * -1;
break;
case 5:
todraw=bone.UpVector*-1;
todraw = bone.UpVector * -1;
break;
}
World.DrawLine(bone.Position, bone.Position+10*todraw, Color.Red);
World.DrawLine(bone.Position, bone.Position + 10 * todraw, Color.Red);
}
public static void CopyToClipboard(MuzzleDir dir)
{
if (ToMark!=null)
if (ToMark != null)
{
string s;
if (UseSecondary)
{
if ((byte)dir<3)
if ((byte)dir < 3)
{
s=$@"
s = $@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
return BulletsShot%2==0 ? {Current} : {Secondary};
@ -132,7 +132,7 @@ namespace RageCoop.Client
}
else
{
s=$@"
s = $@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
return BulletsShot%2==0 ? {Current} : {Secondary};
@ -141,9 +141,9 @@ namespace RageCoop.Client
}
else
{
if ((byte)dir<3)
if ((byte)dir < 3)
{
s=$@"
s = $@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
return {Current};
@ -151,7 +151,7 @@ namespace RageCoop.Client
}
else
{
s=$@"
s = $@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
return {Current};

View File

@ -6,12 +6,11 @@ using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RageCoop.Client
{
@ -21,7 +20,7 @@ namespace RageCoop.Client
internal class Main : Script
{
private bool _gameLoaded = false;
internal static Version Version=typeof(Main).Assembly.GetName().Version;
internal static Version Version = typeof(Main).Assembly.GetName().Version;
internal static int LocalPlayerID = 0;
@ -39,7 +38,7 @@ namespace RageCoop.Client
internal static ulong Ticked = 0;
internal static Vector3 PlayerPosition;
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>
@ -47,9 +46,6 @@ namespace RageCoop.Client
/// </summary>
public Main()
{
#if DEBUG_HIGH_PING
Networking.SimulatedLatency=0.3f;
#endif
Worker = new Worker("RageCoop.Client.Main.Worker", Logger);
try
{
@ -58,14 +54,14 @@ namespace RageCoop.Client
catch
{
GTA.UI.Notification.Show("Malformed configuration, overwriting with default values...");
Settings=new Settings();
Settings = new Settings();
Util.SaveSettings();
}
Directory.CreateDirectory(Settings.DataDirectory);
Logger=new Logger()
Logger = new Logger()
{
LogPath=$"{Settings.DataDirectory}\\RageCoop.Client.log",
UseConsole=false,
LogPath = $"{Settings.DataDirectory}\\RageCoop.Client.log",
UseConsole = false,
#if DEBUG
LogLevel = 0,
#else
@ -90,7 +86,7 @@ namespace RageCoop.Client
return;
}
BaseScript.OnStart();
SyncedPedsGroup=World.AddRelationshipGroup("SYNCPED");
SyncedPedsGroup = World.AddRelationshipGroup("SYNCPED");
Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(SyncedPedsGroup, Relationship.Neutral, true);
#if !NON_INTERACTIVE
#endif
@ -98,9 +94,9 @@ namespace RageCoop.Client
Tick += OnTick;
Tick += (s, e) => { Scripting.API.Events.InvokeTick(); };
KeyDown += OnKeyDown;
KeyDown+=(s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); };
KeyUp+=(s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); };
Aborted += (object sender, EventArgs e) => CleanUp();
KeyDown += (s, e) => { Scripting.API.Events.InvokeKeyDown(s, e); };
KeyUp += (s, e) => { Scripting.API.Events.InvokeKeyUp(s, e); };
Aborted += (object sender, EventArgs e) => Disconnected("Abort");
Util.NativeMemory();
Counter.Restart();
@ -111,25 +107,9 @@ namespace RageCoop.Client
private bool _lastDead;
private void OnTick(object sender, EventArgs e)
{
/*
unsafe
{
var stationName = Function.Call<string>(Hash.GET_RADIO_STATION_NAME, Game.RadioStation);
//_GET_CURRENT_RADIO_TRACK_NAME
var currentTrack = Function.Call<int>((Hash)0x34D66BC058019CE0, stationName);
Function.Call(Hash.SET_RADIO_TRACK, "RADIO_03_HIPHOP_NEW", "ARM1_RADIO_STARTS");
return currentTrack;
var h1 = Function.Call<int>(Hash._GET_CURRENT_RADIO_STATION_HASH);
return $"{h1},{h2},{s},{s1}";
}
*/
P= Game.Player.Character;
PlayerPosition=P.ReadPosition();
FPS=Game.FPS;
// World.DrawMarker(MarkerType.DebugSphere, PedExtensions.RaycastEverything(default), default, default, new Vector3(0.2f, 0.2f, 0.2f), Color.AliceBlue);
P = Game.Player.Character;
PlayerPosition = P.ReadPosition();
FPS = Game.FPS;
if (Game.IsLoading)
{
return;
@ -151,9 +131,9 @@ namespace RageCoop.Client
{
return;
}
if (Game.TimeScale!=1)
if (Game.TimeScale != 1)
{
Game.TimeScale=1;
Game.TimeScale = 1;
}
try
{
@ -161,7 +141,9 @@ namespace RageCoop.Client
}
catch (Exception ex)
{
#if DEBUG
Main.Logger.Error(ex);
#endif
}
if (Networking.ShowNetworkInfo)
@ -183,12 +165,12 @@ namespace RageCoop.Client
{
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
if (P.Health!=1)
if (P.Health != 1)
{
P.Health=1;
Game.Player.WantedLevel=0;
P.Health = 1;
Game.Player.WantedLevel = 0;
Main.Logger.Debug("Player died.");
Scripting.API.Events.InvokePlayerDied();
Scripting.API.Events.InvokePlayerDied(KillMessage());
}
GTA.UI.Screen.StopEffects();
}
@ -199,10 +181,10 @@ namespace RageCoop.Client
}
else if (P.IsDead && !_lastDead)
{
Scripting.API.Events.InvokePlayerDied();
Scripting.API.Events.InvokePlayerDied(KillMessage());
}
_lastDead=P.IsDead;
_lastDead = P.IsDead;
Ticked++;
}
private void OnKeyDown(object sender, KeyEventArgs e)
@ -233,7 +215,7 @@ namespace RageCoop.Client
Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
return;
}
if (Game.IsControlPressed(GTA.Control.FrontendPauseAlternate)&&Settings.DisableAlternatePause)
if (Game.IsControlPressed(GTA.Control.FrontendPauseAlternate) && Settings.DisableAlternatePause)
{
Function.Call(Hash.ACTIVATE_FRONTEND_MENU, Function.Call<int>(Hash.GET_HASH_KEY, "FE_MENU_VERSION_SP_PAUSE"), false, 0);
return;
@ -247,8 +229,8 @@ namespace RageCoop.Client
{
if (x.Visible)
{
CoopMenu.LastMenu=x;
x.Visible=false;
CoopMenu.LastMenu = x;
x.Visible = false;
}
});
}
@ -273,26 +255,7 @@ namespace RageCoop.Client
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp;
}
}
else if (Game.IsControlJustPressed(GTA.Control.VehicleExit))
{
if (P.IsInVehicle())
{
P.Task.LeaveVehicle();
}
else if (P.IsTaskActive(TaskType.CTaskMoveGoToVehicleDoor))
{
P.Task.ClearAll();
}
else
{
var v = World.GetClosestVehicle(P.Position, 10);
if (v!=null)
{
P.Task.EnterVehicle(v, VehicleSeat.Driver, -1, 5, EnterVehicleFlags.AllowJacking);
}
}
}
else if (e.KeyCode==Settings.PassengerKey)
else if (e.KeyCode == Settings.PassengerKey)
{
var P = Game.Player.Character;
@ -306,23 +269,71 @@ namespace RageCoop.Client
{
var V = World.GetClosestVehicle(P.ReadPosition(), 50);
if (V!=null)
if (V != null)
{
var seat = P.GetNearestSeat(V);
P.Task.EnterVehicle(V, seat);
var p = V.GetPedOnSeat(seat);
if (p != null && !p.IsDead)
{
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);
}
}
}
}
}
public static void CleanUp()
internal static void Connected()
{
MainChat.Clear();
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();
EntityPool.Cleanup();
WorldThread.Traffic(true);
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();
EntityPool.Cleanup();
PlayerList.Cleanup();
LocalPlayerID=default;
WorldThread.Traffic(true);
Resources.Unload();
}
private static void DoQueuedActions()
{
@ -339,7 +350,9 @@ namespace RageCoop.Client
}
catch (Exception ex)
{
#if DEBUG
Logger.Error(ex);
#endif
QueuedActions.Remove(action);
}
}
@ -380,5 +393,16 @@ namespace RageCoop.Client
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,4 +1,5 @@
using GTA;
using GTA.Native;
using LemonUI;
using LemonUI.Menus;
using LemonUI.Scaleform;
@ -19,12 +20,12 @@ namespace RageCoop.Client.Menus
};
public static PopUp PopUp = new PopUp()
{
Title="",
Prompt="",
Title = "",
Prompt = "",
Subtitle = "",
Error="",
Error = "",
ShowBackground = true,
Visible=false,
Visible = false,
};
public static NativeMenu LastMenu { get; set; } = Menu;
#region ITEMS
@ -49,10 +50,10 @@ namespace RageCoop.Client.Menus
{
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;
_passwordItem.Activated+=_passwordActivated;
_passwordItem.Activated += _passwordActivated;
ServerIpItem.Activated += ServerIpActivated;
_serverConnectItem.Activated += (sender, item) => { Networking.ToggleConnection(Main.Settings.LastServerAddress); };
@ -66,17 +67,18 @@ namespace RageCoop.Client.Menus
Menu.AddSubMenu(SettingsMenu.Menu);
Menu.AddSubMenu(DevToolMenu.Menu);
#if DEBUG
Menu.AddSubMenu(DebugMenu.Menu);
Menu.AddSubMenu(UpdateMenu.Menu);
#endif
MenuPool.Add(Menu);
MenuPool.Add(SettingsMenu.Menu);
MenuPool.Add(DevToolMenu.Menu);
#if DEBUG
MenuPool.Add(DebugMenu.Menu);
MenuPool.Add(DebugMenu.DiagnosticMenu);
#endif
MenuPool.Add(ServersMenu.Menu);
MenuPool.Add(UpdateMenu.Menu);
MenuPool.Add(PopUp);
Menu.Add(_aboutItem);
@ -85,20 +87,30 @@ namespace RageCoop.Client.Menus
public static bool ShowPopUp(string prompt, string title, string subtitle, string error, bool showbackground)
{
PopUp.Prompt=prompt;
PopUp.Title=title;
PopUp.Subtitle=subtitle;
PopUp.Error=error;
PopUp.ShowBackground=showbackground;
PopUp.Visible=true;
PopUp.Prompt = prompt;
PopUp.Title = title;
PopUp.Subtitle = subtitle;
PopUp.Error = error;
PopUp.ShowBackground = showbackground;
PopUp.Visible = true;
Script.Yield();
while (true)
{
Game.DisableAllControlsThisFrame();
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))
{
PopUp.Visible=false;
PopUp.Visible = false;
return true;
}
else if (Game.IsControlJustPressed(Control.FrontendCancel))
@ -107,6 +119,8 @@ namespace RageCoop.Client.Menus
return false;
}
Script.Yield();
Game.DisableAllControlsThisFrame();
}
}
public static void UsernameActivated(object a, System.EventArgs b)

View File

@ -1,7 +1,8 @@
using GTA;
#if DEBUG
using GTA;
using LemonUI.Menus;
using System.Drawing;
using System;
using System.Drawing;
namespace RageCoop.Client
{
@ -18,15 +19,16 @@ namespace RageCoop.Client
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
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()
{
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);
DiagnosticMenu.Opening+=(sender, e) =>
DiagnosticMenu.Opening += (sender, e) =>
{
DiagnosticMenu.Clear();
DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug()));
@ -35,17 +37,19 @@ namespace RageCoop.Client
DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
}
};
SimulatedLatencyItem.Activated+=(s, e) =>
SimulatedLatencyItem.Activated += (s, e) =>
{
try
{
SimulatedLatencyItem.AltTitle=((Networking.SimulatedLatency=int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle))*0.002f)*500).ToString();
SimulatedLatencyItem.AltTitle = ((Networking.SimulatedLatency = int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle)) * 0.002f) * 500).ToString();
}
catch(Exception ex) { Main.Logger.Error(ex); }
catch (Exception ex) { Main.Logger.Error(ex); }
};
ShowNetworkInfoItem.CheckboxChanged += (s,e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; };
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);
}
@ -53,3 +57,4 @@ namespace RageCoop.Client
}
}
#endif

View File

@ -12,9 +12,9 @@ namespace RageCoop.Client
UseMouse = false,
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 secondaryBoneIndexItem = new NativeItem("Secondary bone index");
public static NativeItem clipboardItem = new NativeItem("Copy to clipboard");
@ -22,20 +22,20 @@ namespace RageCoop.Client
static DevToolMenu()
{
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.Checked=false;
enableSecondaryItem.CheckboxChanged+=EnableSecondaryItem_Changed;
enableItem.Activated += enableItem_Activated;
enableItem.Checked = false;
enableSecondaryItem.CheckboxChanged += EnableSecondaryItem_Changed;
secondaryBoneIndexItem.Enabled=false;
clipboardItem.Activated+=ClipboardItem_Activated;
dirItem.ItemChanged+=DirItem_ItemChanged;
secondaryBoneIndexItem.Enabled = false;
clipboardItem.Activated += ClipboardItem_Activated;
dirItem.ItemChanged += DirItem_ItemChanged;
foreach (var d in Enum.GetValues(typeof(MuzzleDir)))
{
dirItem.Items.Add((MuzzleDir)d);
}
dirItem.SelectedIndex=0;
dirItem.SelectedIndex = 0;
Menu.Add(enableItem);
Menu.Add(boneIndexItem);
@ -49,19 +49,19 @@ namespace RageCoop.Client
{
if (enableSecondaryItem.Checked)
{
DevTool.UseSecondary=true;
secondaryBoneIndexItem.Enabled=true;
DevTool.UseSecondary = true;
secondaryBoneIndexItem.Enabled = true;
}
else
{
DevTool.UseSecondary=false;
secondaryBoneIndexItem.Enabled=false;
DevTool.UseSecondary = false;
secondaryBoneIndexItem.Enabled = false;
}
}
private static void DirItem_ItemChanged(object sender, ItemChangedEventArgs<MuzzleDir> e)
{
DevTool.Direction=dirItem.SelectedItem;
DevTool.Direction = dirItem.SelectedItem;
}
private static void ClipboardItem_Activated(object sender, EventArgs e)
@ -73,11 +73,11 @@ namespace RageCoop.Client
{
if (enableItem.Checked)
{
DevTool.ToMark=Game.Player.Character.CurrentVehicle;
DevTool.ToMark = Game.Player.Character.CurrentVehicle;
}
else
{
DevTool.ToMark=null;
DevTool.ToMark = null;
}
}
}

View File

@ -1,12 +1,12 @@
using LemonUI.Menus;
using GTA.UI;
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 RageCoop.Core;
using GTA.UI;
namespace RageCoop.Client.Menus
{
@ -30,7 +30,7 @@ namespace RageCoop.Client.Menus
static ServersMenu()
{
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) =>
{
@ -38,7 +38,7 @@ namespace RageCoop.Client.Menus
Menu.Add(ResultItem = new NativeItem("Loading..."));
// Prevent freezing
GetServersThread=new Thread(() => GetAllServers());
GetServersThread = new Thread(() => GetAllServers());
GetServersThread.Start();
};
Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
@ -76,7 +76,7 @@ namespace RageCoop.Client.Menus
foreach (ServerInfo server in serverList)
{
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) =>
{
try
@ -84,14 +84,14 @@ namespace RageCoop.Client.Menus
Menu.Visible = false;
if (server.useZT)
{
address=$"{server.ztAddress}:{server.port}";
Main.QueueAction(() => { Notification.Show($"~y~Joining ZeroTier network... {server.ztID}"); });
if (ZeroTierHelper.Join(server.ztID)==null)
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));
Networking.ToggleConnection(address, null, null, PublicKey.FromServerInfo(server));
#if !NON_INTERACTIVE
CoopMenu.ServerIpItem.AltTitle = address;

View File

@ -16,25 +16,39 @@ 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 _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 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());
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());
static SettingsMenu()
{
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;
_disablePauseAlt.CheckboxChanged+= DisablePauseAltCheckboxChanged;
_disablePauseAlt.CheckboxChanged += DisablePauseAltCheckboxChanged;
_disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged;
_flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
_menuKey.Activated+= ChaneMenuKey;
_passengerKey.Activated+= ChangePassengerKey;
_vehicleSoftLimit.Activated+= VehicleSoftLimitActivated;
_menuKey.Activated += ChaneMenuKey;
_passengerKey.Activated += ChangePassengerKey;
_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(_disablePauseAlt);
@ -43,6 +57,9 @@ namespace RageCoop.Client.Menus
Menu.Add(_menuKey);
Menu.Add(_passengerKey);
Menu.Add(_vehicleSoftLimit);
Menu.Add(_pedSoftLimit);
Menu.Add(_showBlip);
Menu.Add(_showNametag);
}
private static void DisableVoiceCheckboxChanged(object sender, EventArgs e)
@ -53,7 +70,9 @@ namespace RageCoop.Client.Menus
{
Voice.Init();
}
} else {
}
else
{
Voice.ClearAll();
}
@ -63,17 +82,29 @@ namespace RageCoop.Client.Menus
private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
{
Main.Settings.DisableAlternatePause=_disablePauseAlt.Checked;
Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked;
Util.SaveSettings();
}
private static void VehicleSoftLimitActivated(object sender, EventArgs e)
{
try
{
Main.Settings.WorldVehicleSoftLimit =int.Parse(
Main.Settings.WorldVehicleSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20,
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();
}
catch { }
@ -82,11 +113,11 @@ namespace RageCoop.Client.Menus
{
try
{
Main.Settings.MenuKey =(Keys)Enum.Parse(
Main.Settings.MenuKey = (Keys)Enum.Parse(
typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.MenuKey.ToString(), 20));
_menuKey.AltTitle=Main.Settings.MenuKey.ToString();
_menuKey.AltTitle = Main.Settings.MenuKey.ToString();
Util.SaveSettings();
}
catch { }
@ -96,11 +127,11 @@ namespace RageCoop.Client.Menus
{
try
{
Main.Settings.PassengerKey =(Keys)Enum.Parse(
Main.Settings.PassengerKey = (Keys)Enum.Parse(
typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.PassengerKey.ToString(), 20));
_passengerKey.AltTitle=Main.Settings.PassengerKey.ToString();
_passengerKey.AltTitle = Main.Settings.PassengerKey.ToString();
Util.SaveSettings();
}
catch { }
@ -108,6 +139,7 @@ namespace RageCoop.Client.Menus
public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{
WorldThread.Traffic(!_disableTrafficItem.Checked);
Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
Util.SaveSettings();
}

View File

@ -1,103 +0,0 @@
using ICSharpCode.SharpZipLib.Zip;
using LemonUI.Menus;
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace RageCoop.Client.Menus
{
internal class UpdateMenu
{
public static bool IsUpdating { get; private set; } = false;
private static NativeItem _updatingItem = new NativeItem("Updating...");
private static NativeItem _downloadItem = new NativeItem("Download", "Download and update to latest nightly");
private static string _downloadPath = Path.Combine(Main.Settings.DataDirectory, "RageCoop.Client.zip");
public static NativeMenu Menu = new NativeMenu("Update", "Update", "Download and install latest nightly build from GitHub")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
static UpdateMenu()
{
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0);
Menu.Opening+=Opening;
_downloadItem.Activated+=StartUpdate;
}
private static void StartUpdate(object sender, EventArgs e)
{
IsUpdating=true;
Menu.Clear();
Menu.Add(_updatingItem);
Task.Run(() =>
{
try
{
if (File.Exists(_downloadPath)) { File.Delete(_downloadPath); }
WebClient client = new WebClient();
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => { Main.QueueAction(() => { _updatingItem.AltTitle=$"{e1.ProgressPercentage}%"; }); };
client.DownloadFileCompleted +=(s, e2) => { Install(); };
client.DownloadFileAsync(new Uri("https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Client.zip"), _downloadPath);
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
});
}
private static void Install()
{
try
{
Main.QueueAction(() =>
{
_updatingItem.AltTitle="Installing...";
});
Directory.CreateDirectory(@"Scripts\RageCoop");
foreach(var f in Directory.GetFiles(@"Scripts\RageCoop", "*.dll", SearchOption.AllDirectories))
{
try { File.Delete(f); }
catch { }
}
new FastZip().ExtractZip(_downloadPath, "Scripts", FastZip.Overwrite.Always, null, null, null, true);
Main.QueueAction(() =>
{
Util.Reload();
IsUpdating=false;
});
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
}
private static void Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
Menu.Clear();
if (Networking.IsOnServer)
{
Menu.Add(new NativeItem("Disconnect from the server first"));
}
else if (IsUpdating)
{
Menu.Add(_updatingItem);
}
else
{
Menu.Add(_downloadItem);
}
}
}
}

View File

@ -16,7 +16,7 @@ namespace RageCoop.Client
private bool CurrentFocused { get; set; }
public bool Focused
{
get { return CurrentFocused; }
get => CurrentFocused;
set
{
if (value && Hidden)
@ -35,7 +35,7 @@ namespace RageCoop.Client
private bool CurrentHidden { get; set; }
private bool Hidden
{
get { return CurrentHidden; }
get => CurrentHidden;
set
{
if (value)

View File

@ -21,8 +21,8 @@ namespace RageCoop.Client
}
return new Packets.FileTransferResponse()
{
ID= fr.ID,
Response=AddFile(fr.ID, fr.Name, fr.FileLength) ? FileResponse.NeedToDownload : FileResponse.AlreadyExists
ID = fr.ID,
Response = AddFile(fr.ID, fr.Name, fr.FileLength) ? FileResponse.NeedToDownload : FileResponse.AlreadyExists
};
});
Networking.RequestHandlers.Add(PacketType.FileTransferComplete, (data) =>
@ -36,8 +36,8 @@ namespace RageCoop.Client
// Inform the server that the download is completed
return new Packets.FileTransferResponse()
{
ID= packet.ID,
Response=FileResponse.Completed
ID = packet.ID,
Response = FileResponse.Completed
};
});
Networking.RequestHandlers.Add(PacketType.AllResourcesSent, (data) =>
@ -45,13 +45,13 @@ namespace RageCoop.Client
try
{
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)
{
Main.Logger.Error("Error occurred when loading server resource:");
Main.Logger.Error(ex);
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.LoadFailed };
return new Packets.FileTransferResponse() { ID = 0, Response = FileResponse.LoadFailed };
}
});
}
@ -122,8 +122,7 @@ namespace RageCoop.Client
{
lock (InProgressDownloads)
{
DownloadFile file;
if (InProgressDownloads.TryGetValue(id, out file))
if (InProgressDownloads.TryGetValue(id, out DownloadFile file))
{
file.Stream.Write(chunk, 0, chunk.Length);
@ -137,9 +136,8 @@ namespace RageCoop.Client
public static void Complete(int id)
{
DownloadFile f;
if (InProgressDownloads.TryGetValue(id, out f))
if (InProgressDownloads.TryGetValue(id, out DownloadFile f))
{
InProgressDownloads.Remove(id);
f.Dispose();
@ -176,7 +174,7 @@ namespace RageCoop.Client
public FileStream Stream { get; set; }
public void Dispose()
{
if (Stream!= null)
if (Stream != null)
{
Stream.Flush();
Stream.Close();

View File

@ -1,12 +1,10 @@
using System;
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using RageCoop.Core;
using Lidgren.Network;
using System.Net;
using System.Timers;
namespace RageCoop.Client
{
@ -15,9 +13,9 @@ namespace RageCoop.Client
static HolePunch()
{
// Periodically send hole punch message as needed
var timer=new Timer(1000);
timer.Elapsed+=DoPunch;
timer.Enabled=true;
var timer = new Timer(1000);
timer.Elapsed += DoPunch;
timer.Enabled = true;
}
private static void DoPunch(object sender, ElapsedEventArgs e)
@ -27,14 +25,14 @@ namespace RageCoop.Client
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))
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.PedID}");
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
Puncher = Main.LocalPlayerID,
Status = p.HolePunchStatus
}.Pack(msg);
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint });
}
@ -48,34 +46,34 @@ namespace RageCoop.Client
public static void Add(Packets.HolePunchInit p)
{
if(PlayerList.Players.TryGetValue(p.TargetID,out var player))
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;
player.ConnectWhenPunched = p.Connect;
}
else
{
Main.Logger.Warning("No player with specified TargetID found for hole punching:"+p.TargetID);
Main.Logger.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
}
}
public static void Punched(Packets.HolePunch p,IPEndPoint from)
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))
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("Puncher identified as: " + puncher.Username);
puncher.HolePunchStatus = (byte)(p.Status + 1);
if (p.Status >= 3)
{
Main.Logger.Debug("HolePunch sucess: "+from+", "+puncher.PedID);
if (puncher.ConnectWhenPunched && (puncher.Connection==null || puncher.Connection.Status==NetConnectionStatus.Disconnected))
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);
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);
new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
puncher.Connection = Networking.Peer.Connect(from, msg);
Networking.Peer.FlushSendQueue();
}
}

View File

@ -1,44 +1,45 @@
using Lidgren.Network;
using GTA.UI;
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using GTA.UI;
using System.Net;
namespace RageCoop.Client
{
internal static partial class Networking
{
public static CoopPeer Peer;
public static float Latency => ServerConnection.AverageRoundtripTime/2;
public static float Latency => ServerConnection.AverageRoundtripTime / 2;
public static bool ShowNetworkInfo = false;
public static Security Security;
public static NetConnection ServerConnection;
private static readonly Dictionary<int, Action<PacketType, byte[]>> PendingResponses = new Dictionary<int, Action<PacketType, byte[]>>();
internal static readonly Dictionary<PacketType, Func<byte[], Packet>> RequestHandlers = new Dictionary<PacketType, Func<byte[], Packet>>();
internal static float SimulatedLatency=0;
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()
{
Security=new Security(Main.Logger);
Security = new Security(Main.Logger);
}
public static void ToggleConnection(string address, string username = null, string password = null,PublicKey publicKey=null)
public static void ToggleConnection(string address, string username = null, string password = null, PublicKey publicKey = null)
{
Menus.CoopMenu.Menu.Visible=false;
Peer?.Shutdown("Bye");
if (IsOnServer)
Menus.CoopMenu.Menu.Visible = false;
if (IsConnecting)
{
// ?
}
else if (IsConnecting) {
_publicKeyReceived.Set();
IsConnecting = false;
Notification.Show("Connection has been canceled");
Main.QueueAction(() => Notification.Show("Connection has been canceled"));
Peer?.Shutdown("Bye");
}
else if (IsOnServer)
{
Peer?.Shutdown("Bye");
}
else
{
@ -46,19 +47,20 @@ namespace RageCoop.Client
IsConnecting = true;
password = password ?? Main.Settings.Password;
username=username ?? Main.Settings.Username;
username = username ?? Main.Settings.Username;
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
{
AutoFlushSendQueue = false,
SimulatedMinimumLatency =SimulatedLatency,
SimulatedRandomLatency=0,
AcceptIncomingConnections = true,
MaximumConnections = 32,
PingInterval = 5
};
#if DEBUG
config.SimulatedMinimumLatency = SimulatedLatency;
config.SimulatedRandomLatency = 0;
#endif
config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
@ -78,45 +80,54 @@ namespace RageCoop.Client
PlayerList.Cleanup();
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))
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;
IsConnecting = false;
return;
}
Task.Run(() =>
{
try
{
_targetServerEP=CoreUtils.StringToEndPoint(address);
_targetServerEP = CoreUtils.StringToEndPoint(address);
// Ensure static constructor invocation
DownloadManager.Cleanup();
Peer = new CoopPeer(config);
Peer.OnMessageReceived+= (s, m) =>
Peer.OnMessageReceived += (s, m) =>
{
try { ProcessMessage(m); }
catch (Exception ex) { Main.Logger.Error(ex); }
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();
if(publicKey==null){
if (!GetServerPublicKey(ip[0],int.Parse(ip[1])))
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");
}
}
else{
Security.SetServerPublicKey(publicKey.Modulus,publicKey.Exponent);
else
{
Security.SetServerPublicKey(publicKey.Modulus, publicKey.Exponent);
}
// Send handshake packet
NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
var handshake = new Packets.Handshake()
{
PedID = Main.LocalPlayerID,
Username =username,
PedID = Main.LocalPlayerID,
Username = username,
ModVersion = Main.Version.ToString(),
PasswordEncrypted=Security.Encrypt(password.GetBytes()),
PasswordEncrypted = Security.Encrypt(password.GetBytes()),
InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port)
};
@ -128,9 +139,9 @@ namespace RageCoop.Client
catch (Exception ex)
{
Main.Logger.Error("Cannot connect to server: ", ex);
Main.QueueAction(() => Notification.Show("Cannot connect to server: "+ex.Message));
Main.QueueAction(() => Notification.Show("Cannot connect to server: " + ex.Message));
}
IsConnecting=false;
IsConnecting = false;
});
}
}
@ -141,8 +152,8 @@ namespace RageCoop.Client
{
var p = new Player
{
PedID = packet.PedID,
Username= packet.Username,
ID = packet.PedID,
Username = packet.Username,
};
PlayerList.SetPlayer(packet.PedID, packet.Username);
@ -153,9 +164,10 @@ namespace RageCoop.Client
private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{
var player = PlayerList.GetPlayer(packet.PedID);
if (player==null) { return; }
if (player == null) { return; }
PlayerList.RemovePlayer(packet.PedID);
Main.QueueAction(() => {
Main.QueueAction(() =>
{
EntityPool.RemoveAllFromPlayer(packet.PedID);
GTA.UI.Notification.Show($"~h~{player.Username}~h~ left.");
});
@ -163,13 +175,13 @@ namespace RageCoop.Client
#endregion // -- PLAYER --
#region -- GET --
private static bool GetServerPublicKey(string host,int port, int timeout = 10000)
private static bool GetServerPublicKey(string host, int port, int timeout = 10000)
{
Security.ServerRSA=null;
Security.ServerRSA = null;
var msg = Peer.CreateMessage();
new Packets.PublicKeyRequest().Pack(msg);
Peer.SendUnconnectedMessage(msg, host, port);
return _publicKeyReceived.WaitOne(timeout) && Security.ServerRSA!=null;
return _publicKeyReceived.WaitOne(timeout) && Security.ServerRSA != null;
}
public static void GetResponse<T>(Packet request, Action<T> callback, ConnectionChannel channel = ConnectionChannel.RequestResponse) where T : Packet, new()
@ -186,14 +198,14 @@ namespace RageCoop.Client
msg.Write((byte)PacketType.Request);
msg.Write(id);
request.Pack(msg);
Peer.SendMessage(msg,ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel);
Peer.SendMessage(msg, ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel);
}
#endregion
private static int NewRequestID()
{
int ID = 0;
while ((ID==0) || PendingResponses.ContainsKey(ID))
while ((ID == 0) || PendingResponses.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];

View File

@ -3,7 +3,7 @@ using Lidgren.Network;
using RageCoop.Client.Menus;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace RageCoop.Client
@ -14,7 +14,7 @@ namespace RageCoop.Client
/// <summary>
/// Reduce GC pressure by reusing frequently used packets
/// </summary>
static class ReceivedPackets
private static class ReceivedPackets
{
public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -24,7 +24,7 @@ namespace RageCoop.Client
/// <summary>
/// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/>
/// </summary>
private static readonly Func<byte, BitReader, object> _resolveHandle = (t, reader) =>
private static readonly Func<byte, NetIncomingMessage, object> _resolveHandle = (t, reader) =>
{
switch (t)
{
@ -37,14 +37,15 @@ namespace RageCoop.Client
case 60:
return EntityPool.ServerBlips[reader.ReadInt32()].Handle;
default:
throw new ArgumentException("Cannot resolve server side argument: "+t);
throw new ArgumentException("Cannot resolve server side argument: " + t);
}
};
private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false);
private static bool _recycle;
public static void ProcessMessage(NetIncomingMessage message)
{
if (message == null) { return; }
_recycle = true;
switch (message.MessageType)
{
case NetIncomingMessageType.StatusChanged:
@ -53,48 +54,36 @@ namespace RageCoop.Client
switch (status)
{
case NetConnectionStatus.InitiatedConnect:
if (message.SenderConnection==ServerConnection)
if (message.SenderConnection == ServerConnection)
{
CoopMenu.InitiateConnectionMenuSetting();
}
break;
case NetConnectionStatus.Connected:
if (message.SenderConnection==ServerConnection)
if (message.SenderConnection == ServerConnection)
{
Memory.ApplyPatches();
var response = message.SenderConnection.RemoteHailMessage;
if ((PacketType)response.ReadByte()!=PacketType.HandshakeSuccess)
if ((PacketType)response.ReadByte() != PacketType.HandshakeSuccess)
{
throw new Exception("Invalid handshake response!");
}
var p = new Packets.HandshakeSuccess();
p.Deserialize(response.ReadBytes(response.ReadInt32()));
foreach(var player in p.Players)
p.Deserialize(response);
foreach (var player in p.Players)
{
PlayerList.SetPlayer(player.ID,player.Username);
PlayerList.SetPlayer(player.ID, player.Username);
}
Main.QueueAction(() =>
{
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
if (Main.Settings.Voice && !Voice.WasInitialized())
{
Voice.Init();
}
GTA.UI.Notification.Show("~g~Connected!");
});
Main.Logger.Info(">> Connected <<");
Main.Connected();
}
else
{
// Self-initiated connection
if (message.SenderConnection.RemoteHailMessage==null) { return; }
if (message.SenderConnection.RemoteHailMessage == null) { return; }
var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>();
if (PlayerList.Players.TryGetValue(p.ID,out var player))
if (PlayerList.Players.TryGetValue(p.ID, out var player))
{
player.Connection=message.SenderConnection;
player.Connection = message.SenderConnection;
Main.Logger.Debug($"Direct connection to {player.Username} established");
}
else
@ -105,28 +94,16 @@ namespace RageCoop.Client
}
break;
case NetConnectionStatus.Disconnected:
if (message.SenderConnection==ServerConnection)
if (message.SenderConnection == ServerConnection)
{
Memory.RestorePatches();
DownloadManager.Cleanup();
if (Main.MainChat.Focused)
{
Main.MainChat.Focused = false;
}
Main.QueueAction(() => Main.CleanUp());
CoopMenu.DisconnectedMenuSetting();
Main.Logger.Info($">> Disconnected << reason: {reason}");
Main.QueueAction(() => GTA.UI.Notification.Show("~r~Disconnected: " + reason));
Main.Resources.Unload();
Main.Disconnected(reason);
}
break;
}
break;
case NetIncomingMessageType.Data:
{
if (message.LengthBytes==0) { break; }
if (message.LengthBytes == 0) { break; }
var packetType = PacketType.Unknown;
try
{
@ -139,7 +116,7 @@ namespace RageCoop.Client
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id);
}
break;
@ -148,57 +125,60 @@ namespace RageCoop.Client
{
int id = message.ReadInt32();
var realType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
if (RequestHandlers.TryGetValue(realType, out var handler))
{
var response = Peer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(len)).Pack(response);
Peer.SendMessage(response,ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel);
handler(message).Pack(response);
Peer.SendMessage(response, ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel);
Peer.FlushSendQueue();
}
else
{
Main.Logger.Debug("Did not find a request handler of type: " + realType);
}
break;
}
default:
{
byte[] data = message.ReadBytes(message.ReadInt32());
HandlePacket(packetType, data,message.SenderConnection);
HandlePacket(packetType, message, message.SenderConnection, ref _recycle);
break;
}
}
}
catch (Exception ex)
{
#if DEBUG
Main.QueueAction(() =>
{
GTA.UI.Notification.Show("~r~~h~Packet Error");
GTA.UI.Notification.Show($"~r~~h~Packet Error {ex.Message}");
return true;
});
Main.Logger.Error($"[{packetType}] {ex.Message}");
Main.Logger.Error(ex);
Peer.Shutdown($"Packet Error [{packetType}]");
#endif
_recycle = false;
}
break;
}
case NetIncomingMessageType.UnconnectedData:
{
var packetType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
switch (packetType)
{
case PacketType.HolePunch:
{
HolePunch.Punched(data.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
HolePunch.Punched(message.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
break;
}
case PacketType.PublicKeyResponse:
{
if(message.SenderEndPoint.ToString()!=_targetServerEP.ToString() ||!IsConnecting){break;}
var packet = data.GetPacket<Packets.PublicKeyResponse>();
if (message.SenderEndPoint.ToString() != _targetServerEP.ToString() || !IsConnecting) { break; }
var packet = message.GetPacket<Packets.PublicKeyResponse>();
Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
_publicKeyReceived.Set();
break;
@ -215,41 +195,42 @@ namespace RageCoop.Client
default:
break;
}
Peer.Recycle(message);
if (_recycle)
{
Peer.Recycle(message);
}
}
private static void HandlePacket(PacketType packetType, byte[] data, NetConnection senderConnection)
private static void HandlePacket(PacketType packetType, NetIncomingMessage msg, NetConnection senderConnection, ref bool recycle)
{
switch (packetType)
{
case PacketType.HolePunchInit:
HolePunch.Add(data.GetPacket<Packets.HolePunchInit>());
HolePunch.Add(msg.GetPacket<Packets.HolePunchInit>());
break;
case PacketType.PlayerConnect:
PlayerConnect(data.GetPacket<Packets.PlayerConnect>());
PlayerConnect(msg.GetPacket<Packets.PlayerConnect>());
break;
case PacketType.PlayerDisconnect:
PlayerDisconnect(data.GetPacket<Packets.PlayerDisconnect>());
PlayerDisconnect(msg.GetPacket<Packets.PlayerDisconnect>());
break;
case PacketType.PlayerInfoUpdate:
PlayerList.UpdatePlayer(data.GetPacket<Packets.PlayerInfoUpdate>());
PlayerList.UpdatePlayer(msg.GetPacket<Packets.PlayerInfoUpdate>());
break;
case PacketType.VehicleSync:
ReceivedPackets.VehicelPacket.Deserialize(data);
ReceivedPackets.VehicelPacket.Deserialize(msg);
VehicleSync(ReceivedPackets.VehicelPacket);
break;
case PacketType.PedSync:
ReceivedPackets.PedPacket.Deserialize(data);
ReceivedPackets.PedPacket.Deserialize(msg);
PedSync(ReceivedPackets.PedPacket);
break;
case PacketType.ProjectileSync:
ReceivedPackets.ProjectilePacket.Deserialize(data);
ReceivedPackets.ProjectilePacket.Deserialize(msg);
ProjectileSync(ReceivedPackets.ProjectilePacket);
break;
@ -257,7 +238,7 @@ namespace RageCoop.Client
{
Packets.ChatMessage packet = new Packets.ChatMessage((b) => Security.Decrypt(b));
packet.Deserialize(data);
packet.Deserialize(msg);
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
}
@ -268,7 +249,7 @@ namespace RageCoop.Client
if (Main.Settings.Voice)
{
Packets.Voice packet = new Packets.Voice();
packet.Deserialize(data);
packet.Deserialize(msg);
SyncedPed player = EntityPool.GetPedByID(packet.ID);
@ -283,18 +264,20 @@ namespace RageCoop.Client
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
packet.Deserialize(data);
packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet);
}
break;
case PacketType.CustomEventQueued:
{
recycle = false;
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
Main.QueueAction(() =>
{
packet.Deserialize(data);
packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet);
Peer.Recycle(msg);
});
}
break;
@ -302,7 +285,7 @@ namespace RageCoop.Client
case PacketType.FileTransferChunk:
{
Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
packet.Deserialize(data);
packet.Deserialize(msg);
DownloadManager.Write(packet.ID, packet.FileChunk);
}
break;
@ -310,8 +293,9 @@ namespace RageCoop.Client
default:
if (packetType.IsSyncEvent())
{
recycle = false;
// Dispatch to script thread
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; });
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, msg); return true; });
}
break;
}
@ -320,33 +304,38 @@ namespace RageCoop.Client
private static void PedSync(Packets.PedSync packet)
{
SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null)
if (c == null)
{
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID));
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}");
EntityPool.ThreadSafe.Add(c = new SyncedPed(packet.ID));
}
else return;
}
PedDataFlags flags = packet.Flags;
c.ID=packet.ID;
c.OwnerID=packet.OwnerID;
c.ID = packet.ID;
c.OwnerID = packet.OwnerID;
c.Health = packet.Health;
c.Rotation = packet.Rotation;
c.Velocity = packet.Velocity;
c.Speed = packet.Speed;
c.Flags=packet.Flags;
c.Heading=packet.Heading;
c.Flags = packet.Flags;
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;
c.HeadPosition = packet.HeadPosition;
c.RightFootPosition = packet.RightFootPosition;
c.LeftFootPosition = packet.LeftFootPosition;
}
else if (c.Speed>=4)
else if (c.Speed >= 4)
{
c.VehicleID=packet.VehicleID;
c.Seat=packet.Seat;
c.VehicleID = packet.VehicleID;
c.Seat = packet.Seat;
}
c.LastSynced = Main.Ticked;
c.LastSynced = Main.Ticked;
if (c.IsAiming)
{
c.AimCoords = packet.AimCoords;
@ -354,13 +343,13 @@ namespace RageCoop.Client
if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync))
{
c.CurrentWeaponHash = packet.CurrentWeaponHash;
c.Clothes=packet.Clothes;
c.WeaponComponents=packet.WeaponComponents;
c.WeaponTint=packet.WeaponTint;
c.Model=packet.ModelHash;
c.BlipColor=packet.BlipColor;
c.BlipSprite=packet.BlipSprite;
c.BlipScale=packet.BlipScale;
c.Clothes = packet.Clothes;
c.WeaponComponents = packet.WeaponComponents;
c.WeaponTint = packet.WeaponTint;
c.Model = packet.ModelHash;
c.BlipColor = packet.BlipColor;
c.BlipSprite = packet.BlipSprite;
c.BlipScale = packet.BlipScale;
c.LastFullSynced = Main.Ticked;
}
@ -368,58 +357,64 @@ namespace RageCoop.Client
private static void VehicleSync(Packets.VehicleSync packet)
{
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null)
if (v == null)
{
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID));
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));
}
else return;
}
if (v.IsLocal) { return; }
v.ID= packet.ID;
v.OwnerID= packet.OwnerID;
v.Flags=packet.Flags;
v.Position=packet.Position;
v.Quaternion=packet.Quaternion;
v.SteeringAngle=packet.SteeringAngle;
v.ThrottlePower=packet.ThrottlePower;
v.BrakePower=packet.BrakePower;
v.Velocity=packet.Velocity;
v.RotationVelocity=packet.RotationVelocity;
v.DeluxoWingRatio=packet.DeluxoWingRatio;
v.LastSynced=Main.Ticked;
v.ID = packet.ID;
v.OwnerID = packet.OwnerID;
v.Flags = packet.Flags;
v.Position = packet.Position;
v.Quaternion = packet.Quaternion;
v.SteeringAngle = packet.SteeringAngle;
v.ThrottlePower = packet.ThrottlePower;
v.BrakePower = packet.BrakePower;
v.Velocity = packet.Velocity;
v.RotationVelocity = packet.RotationVelocity;
v.DeluxoWingRatio = packet.DeluxoWingRatio;
v.LastSynced = Main.Ticked;
v.LastSyncedStopWatch.Restart();
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{
v.DamageModel=packet.DamageModel;
v.EngineHealth=packet.EngineHealth;
v.Mods=packet.Mods;
v.Model=packet.ModelHash;
v.Colors=packet.Colors;
v.LandingGear=packet.LandingGear;
v.RoofState=(VehicleRoofState)packet.RoofState;
v.LockStatus=packet.LockStatus;
v.RadioStation=packet.RadioStation;
v.LicensePlate=packet.LicensePlate;
v.Livery=packet.Livery;
v.LastFullSynced= Main.Ticked;
v.DamageModel = packet.DamageModel;
v.EngineHealth = packet.EngineHealth;
v.Mods = packet.Mods;
v.Model = packet.ModelHash;
v.Colors = packet.Colors;
v.LandingGear = packet.LandingGear;
v.RoofState = (VehicleRoofState)packet.RoofState;
v.LockStatus = packet.LockStatus;
v.RadioStation = packet.RadioStation;
v.LicensePlate = packet.LicensePlate;
v.Livery = packet.Livery;
v.LastFullSynced = Main.Ticked;
}
}
private static void ProjectileSync(Packets.ProjectileSync packet)
{
var p = EntityPool.GetProjectileByID(packet.ID);
if (p==null)
if (p == null)
{
if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; }
// 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.Rotation=packet.Rotation;
p.Velocity=packet.Velocity;
p.WeaponHash=(WeaponHash)packet.WeaponHash;
p.Shooter= packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ?
p.Flags = packet.Flags;
p.Position = packet.Position;
p.Rotation = packet.Rotation;
p.Velocity = packet.Velocity;
p.WeaponHash = (WeaponHash)packet.WeaponHash;
p.Shooter = packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ?
(SyncedEntity)EntityPool.GetVehicleByID(packet.ShooterID) : EntityPool.GetPedByID(packet.ShooterID);
p.LastSynced=Main.Ticked;
p.LastSynced = Main.Ticked;
p.LastSyncedStopWatch.Restart();
}
}
}

View File

@ -13,7 +13,7 @@ namespace RageCoop.Client
/// <summary>
/// Reduce GC pressure by reusing frequently used packets
/// </summary>
static class SendPackets
private static class SendPackets
{
public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -28,50 +28,50 @@ namespace RageCoop.Client
public static void SendPed(SyncedPed sp, bool full)
{
if (sp.LastSentStopWatch.ElapsedMilliseconds<SyncInterval)
if (sp.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{
return;
}
Ped ped = sp.MainPed;
var p = SendPackets.PedPacket;
p.ID =sp.ID;
p.OwnerID=sp.OwnerID;
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;
p.Heading = ped.Heading;
if (p.Flags.HasPedFlag(PedDataFlags.IsAiming))
{
p.AimCoords = ped.GetAimCoord();
}
if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{
p.HeadPosition=ped.Bones[Bone.SkelHead].Position;
p.RightFootPosition=ped.Bones[Bone.SkelRightFoot].Position;
p.LeftFootPosition=ped.Bones[Bone.SkelLeftFoot].Position;
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)
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)
if (p.VehicleID == 0) { Main.Logger.Error("Invalid vehicle"); }
if (p.Speed == 5)
{
p.Seat=ped.GetSeatTryingToEnter();
p.Seat = ped.GetSeatTryingToEnter();
}
else
{
p.Seat=ped.SeatIndex;
p.Seat = ped.SeatIndex;
}
if (!veh.IsLocal && p.Speed==4 && p.Seat==VehicleSeat.Driver)
if (!veh.IsLocal && p.Speed == 4 && p.Seat == VehicleSeat.Driver)
{
veh.OwnerID=Main.LocalPlayerID;
SyncEvents.TriggerChangeOwner(veh.ID,Main.LocalPlayerID);
veh.OwnerID = Main.LocalPlayerID;
SyncEvents.TriggerChangeOwner(veh.ID, Main.LocalPlayerID);
}
}
p.Position = ped.ReadPosition();
@ -80,57 +80,57 @@ namespace RageCoop.Client
if (full)
{
var w = ped.VehicleWeapon;
p.CurrentWeaponHash = (w!=VehicleWeaponHash.Invalid)? (uint)w:(uint)ped.Weapons.Current.Hash;
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);
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);
Blip b;
if (sp.IsPlayer)
{
p.BlipColor=Scripting.API.Config.BlipColor;
p.BlipSprite=Scripting.API.Config.BlipSprite;
p.BlipScale=Scripting.API.Config.BlipScale;
p.BlipColor = Scripting.API.Config.BlipColor;
p.BlipSprite = Scripting.API.Config.BlipSprite;
p.BlipScale = Scripting.API.Config.BlipScale;
}
else if ((b = ped.AttachedBlip) !=null)
else if ((b = ped.AttachedBlip) != null)
{
p.BlipColor=b.Color;
p.BlipSprite=b.Sprite;
p.BlipColor = b.Color;
p.BlipSprite = b.Sprite;
if (p.BlipSprite==BlipSprite.PoliceOfficer || p.BlipSprite==BlipSprite.PoliceOfficer2)
if (p.BlipSprite == BlipSprite.PoliceOfficer || p.BlipSprite == BlipSprite.PoliceOfficer2)
{
p.BlipScale=0.5f;
p.BlipScale = 0.5f;
}
}
else
{
p.BlipColor=(BlipColor)255;
p.BlipColor = (BlipColor)255;
}
}
SendSync(p, ConnectionChannel.PedSync);
}
public static void SendVehicle(SyncedVehicle v, bool full)
{
if (v.LastSentStopWatch.ElapsedMilliseconds<SyncInterval)
if (v.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{
return;
}
Vehicle veh = v.MainVehicle;
var packet = SendPackets.VehicelPacket;
packet.ID =v.ID;
packet.OwnerID=v.OwnerID;
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.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 (packet.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) { packet.DeluxoWingRatio = v.MainVehicle.GetDeluxoWingRatio(); }
if (full)
{
byte primaryColor = 0;
@ -141,24 +141,24 @@ namespace RageCoop.Client
}
packet.Flags |= VehicleDataFlags.IsFullSync;
packet.Colors = new byte[] { primaryColor, secondaryColor };
packet.DamageModel=veh.GetVehicleDamageModel();
packet.DamageModel = veh.GetVehicleDamageModel();
packet.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0;
packet.RoofState=(byte)veh.RoofState;
packet.RoofState = (byte)veh.RoofState;
packet.Mods = veh.Mods.GetVehicleMods();
packet.ModelHash=veh.Model.Hash;
packet.EngineHealth=veh.EngineHealth;
packet.LockStatus=veh.LockStatus;
packet.LicensePlate=Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
packet.Livery=Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
if (v.MainVehicle==Game.Player.LastVehicle)
packet.ModelHash = veh.Model.Hash;
packet.EngineHealth = veh.EngineHealth;
packet.LockStatus = veh.LockStatus;
packet.LicensePlate = Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
packet.Livery = Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
if (v.MainVehicle == Game.Player.LastVehicle)
{
packet.RadioStation=Util.GetPlayerRadioIndex();
packet.RadioStation = Util.GetPlayerRadioIndex();
}
if (packet.EngineHealth>v.LastEngineHealth)
if (packet.EngineHealth > v.LastEngineHealth)
{
packet.Flags |= VehicleDataFlags.Repaired;
}
v.LastEngineHealth=packet.EngineHealth;
v.LastEngineHealth = packet.EngineHealth;
}
SendSync(packet, ConnectionChannel.VehicleSync);
}
@ -178,25 +178,25 @@ namespace RageCoop.Client
StartPosition = start,
EndPosition = end,
OwnerID = ownerID,
WeaponHash=weapon,
WeaponHash = weapon,
}, ConnectionChannel.SyncEvents);
}
public static void SendVehicleBullet(uint hash,SyncedPed owner,EntityBone b)
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
EndPosition = b.Position + b.ForwardVector,
OwnerID = owner.ID,
Bone = (ushort)b.Index,
WeaponHash = hash
});
}
#endregion
public static void SendChatMessage(string message)
{
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);
{ Username = Main.Settings.Username, Message = message }, ServerConnection, ConnectionChannel.Chat, NetDeliveryMethod.ReliableOrdered);
Peer.FlushSendQueue();
}
public static void SendVoiceMessage(byte[] buffer, int recorded)

View File

@ -16,8 +16,8 @@ namespace RageCoop.Client
var bu = Networking.Peer.Statistics.SentBytes;
var bd = Networking.Peer.Statistics.ReceivedBytes;
Thread.Sleep(1000);
BytesUpPerSecond=Networking.Peer.Statistics.SentBytes-bu;
BytesDownPerSecond=Networking.Peer.Statistics.ReceivedBytes-bd;
BytesUpPerSecond = Networking.Peer.Statistics.SentBytes - bu;
BytesDownPerSecond = Networking.Peer.Statistics.ReceivedBytes - bd;
}
});
}

View File

@ -1,11 +1,11 @@
using GTA;
using GTA.Math;
using GTA.Native;
using Lidgren.Network;
using RageCoop.Core;
using System.Collections.Generic;
using Lidgren.Network;
using System.Net;
using System.Linq;
using System.Net;
namespace RageCoop.Client
{
@ -50,11 +50,11 @@ namespace RageCoop.Client
_mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0);
int i=0;
int i = 0;
foreach (var player in Players.Values)
{
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Ping * 1000:N0}ms", player.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} players");
@ -63,44 +63,43 @@ namespace RageCoop.Client
public static void SetPlayer(int id, string username, float latency = 0)
{
Main.Logger.Debug($"{id},{username},{latency}");
Player p;
if (Players.TryGetValue(id, out p))
if (Players.TryGetValue(id, out Player p))
{
p.Username=username;
p.PedID=id;
p._latencyToServer=latency;
p.Username = username;
p.ID = id;
p._latencyToServer = latency;
}
else
{
p = new Player { PedID=id, Username=username, _latencyToServer=latency };
p = new Player { ID = id, Username = username, _latencyToServer = latency };
Players.Add(id, p);
}
}
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
{
var p = GetPlayer(packet.PedID);
if (p!=null)
if (p != null)
{
p._latencyToServer = packet.Latency;
p.Position = packet.Position;
p.IsHost = packet.IsHost;
Main.QueueAction(() =>
{
if (p.FakeBlip?.Exists()!=true)
if (p.FakeBlip?.Exists() != true)
{
p.FakeBlip=World.CreateBlip(p.Position);
p.FakeBlip = World.CreateBlip(p.Position);
}
if (p.Position.DistanceTo(Main.PlayerPosition)>500)
if (EntityPool.PedExists(p.ID))
{
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;
p.FakeBlip.DisplayType = BlipDisplayType.NoDisplay;
}
else
{
p.FakeBlip.DisplayType=BlipDisplayType.NoDisplay;
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;
}
});
@ -108,22 +107,21 @@ namespace RageCoop.Client
}
public static Player GetPlayer(int id)
{
Player p;
Players.TryGetValue(id, out p);
Players.TryGetValue(id, out Player p);
return p;
}
public static Player GetPlayer(SyncedPed p)
{
var player = GetPlayer(p.ID);
if (player!=null)
if (player != null)
{
player.Character=p;
player.Character = p;
}
return player;
}
public static void RemovePlayer(int id)
{
if (Players.TryGetValue(id,out var player))
if (Players.TryGetValue(id, out var player))
{
Players.Remove(id);
Main.QueueAction(() => player.FakeBlip?.Delete());
@ -131,39 +129,40 @@ namespace RageCoop.Client
}
public static void Cleanup()
{
foreach(var p in Players.Values.ToArray())
foreach (var p in Players.Values.ToArray())
{
p.FakeBlip?.Delete();
}
Players=new Dictionary<int, Player> { };
Players = new Dictionary<int, Player> { };
}
}
internal class Player
public class Player
{
public byte HolePunchStatus { get; set; } = 1;
public byte HolePunchStatus { get; internal set; } = 1;
public bool IsHost { get; internal set; }
public string Username { get; internal set; }
/// <summary>
/// Universal character ID.
/// Universal ped ID.
/// </summary>
public int PedID
public int ID
{
get; internal set;
}
public IPEndPoint InternalEndPoint { get; set; }
public IPEndPoint ExternalEndPoint { get; set; }
public bool ConnectWhenPunched { get; set; }
public Blip FakeBlip { get; set; }
public Vector3 Position { get; 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>
/// Player round-trip time in seconds, will be the latency to server if not using P2P connection.
/// Player round-trip time in seconds, will be the rtt to server if not using P2P connection.
/// </summary>
public float Ping => Main.LocalPlayerID==PedID ? Networking.Latency*2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer*2);
public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime/2 : Networking.Latency+_latencyToServer;
public float _latencyToServer = 0;
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 NetConnection Connection { get; set; }
public bool HasDirectConnection => Connection?.Status==NetConnectionStatus.Connected;
public NetConnection Connection { get; internal set; }
public bool HasDirectConnection => Connection?.Status == NetConnectionStatus.Connected;
}
}

View File

@ -15,8 +15,8 @@ using System.Resources;
[assembly: AssemblyCulture("")]
// Version informationr(
[assembly: AssemblyVersion("1.5.1.13")]
[assembly: AssemblyFileVersion("1.5.1.13")]
// Version information
[assembly: AssemblyVersion("1.5.4.7")]
[assembly: AssemblyFileVersion("1.5.4.7")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -21,6 +21,7 @@
<OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutPutPath>..\bin\Release\Client</OutPutPath>
@ -35,7 +36,6 @@
<Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" />
@ -55,16 +55,18 @@
<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\SyncedVehicle.cs" />
<Compile Include="Sync\Entities\SyncedVehicle.Members.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" />
@ -88,7 +90,7 @@
<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.7.0.0, Culture=neutral, processorArchitecture=AMD64">
<Reference Include="LemonUI.SHVDN3, Version=1.10.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference>

View File

@ -1,5 +1,6 @@
#undef DEBUG
using GTA;
using Newtonsoft.Json;
using RageCoop.Core;
using System;
using System.Collections.Generic;
@ -39,7 +40,7 @@ namespace RageCoop.Client.Scripting
/// </summary>
public static string Username
{
get { return Main.Settings.Username; }
get => Main.Settings.Username;
set
{
if (Networking.IsOnServer || string.IsNullOrEmpty(value))
@ -90,7 +91,7 @@ namespace RageCoop.Client.Scripting
/// <summary>
/// The local player is dead
/// </summary>
public static event EmptyEvent OnPlayerDied;
public static event EventHandler<string> OnPlayerDied;
/// <summary>
/// A local vehicle is spawned
@ -132,7 +133,7 @@ namespace RageCoop.Client.Scripting
internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.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 InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); }
@ -141,12 +142,11 @@ namespace RageCoop.Client.Scripting
internal static void InvokeCustomEventReceived(Packets.CustomEvent p)
{
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args };
var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args };
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
List<Action<CustomEventReceivedArgs>> handlers;
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
{
handlers.ForEach((x) => { x.Invoke(args); });
}
@ -160,65 +160,48 @@ namespace RageCoop.Client.Scripting
/// Get the local player's ID
/// </summary>
/// <returns>PlayerID</returns>
public static int LocalPlayerID
{
get { return Main.LocalPlayerID; }
}
public static int LocalPlayerID => Main.LocalPlayerID;
/// <summary>
/// Check if player is connected to a server
/// </summary>
public static bool IsOnServer { get { return Networking.IsOnServer; } }
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 { get { return Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null; } }
public static System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
/// <summary>
/// Check if a RAGECOOP menu is visible
/// </summary>
public static bool IsMenuVisible
{
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
}
public static bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
/// <summary>
/// Check if the RAGECOOP chat is visible
/// </summary>
public static bool IsChatFocused
{
get { return Main.MainChat.Focused; }
}
public static bool IsChatFocused => Main.MainChat.Focused;
/// <summary>
/// Check if the RAGECOOP list of players is visible
/// </summary>
public static bool IsPlayerListVisible
{
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
}
public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
/// <summary>
/// Get the version of RAGECOOP
/// </summary>
public static Version CurrentVersion
{
get { return Main.Version; }
}
public static Version CurrentVersion => Main.Version;
/// <summary>
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
/// </summary>
/// <returns></returns>
public static Logger Logger
{
get
{
return Main.Logger;
}
}
public static Logger Logger => Main.Logger;
/// <summary>
/// Get all players indexed by their ID
/// </summary>
public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
#endregion
#region FUNCTIONS
@ -245,6 +228,16 @@ namespace RageCoop.Client.Scripting
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>
/// Send a local chat message to this player
/// </summary>
@ -255,6 +248,15 @@ namespace RageCoop.Client.Scripting
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>
/// Queue an action to be executed on next tick.
/// </summary>
@ -283,9 +285,9 @@ namespace RageCoop.Client.Scripting
Networking.Peer.SendTo(new Packets.CustomEvent()
{
Args=args,
Hash=eventHash
},Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>
@ -318,17 +320,17 @@ namespace RageCoop.Client.Scripting
callback(e);
}
};
DownloadManager.DownloadCompleted+=handler;
DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{
Name=name,
Name = name,
},
(p) =>
{
if (p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted-=handler;
throw new ArgumentException("Requested file was not found on the server: "+name);
DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: " + name);
}
});
}

View File

@ -14,9 +14,9 @@ namespace RageCoop.Client.Scripting
private bool _isHost = false;
public override void OnStart()
{
API.Events.OnPedDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); };
API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); };
API.Events.OnPlayerDied+=() => { API.SendCustomEvent(CustomEvents.OnPlayerDied); };
API.Events.OnPedDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, 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.SetDisplayNameTag, SetDisplayNameTag);
@ -29,9 +29,9 @@ namespace RageCoop.Client.Scripting
API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip);
API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle);
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.OnPlayerDied, (e) => { GTA.UI.Notification.Show($"~h~{e.Args[0]}~h~ died."); });
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show((string)e.Args[0]); });
Task.Run(() =>
{
while (true)
@ -46,7 +46,7 @@ namespace RageCoop.Client.Scripting
int weather1 = default(int);
int weather2 = default(int);
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);
}
});
@ -59,33 +59,33 @@ namespace RageCoop.Client.Scripting
private void WeatherTimeSync(CustomEventReceivedArgs e)
{
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]);
World.CurrentTimeOfDay = new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]);
Function.Call(Hash.SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
}
private void SetDisplayNameTag(CustomEventReceivedArgs e)
{
var p = PlayerList.GetPlayer((int)e.Args[0]);
if (p != null) { p.DisplayNameTag=(bool)e.Args[1]; }
if (p != null) { p.DisplayNameTag = (bool)e.Args[1]; }
}
private void UpdatePedBlip(CustomEventReceivedArgs e)
{
var p = Entity.FromHandle((int)e.Args[0]);
if (p == null) { return; }
if (p.Handle==Game.Player.Character.Handle)
if (p.Handle == Game.Player.Character.Handle)
{
API.Config.BlipColor=(BlipColor)(byte)e.Args[1];
API.Config.BlipSprite=(BlipSprite)(ushort)e.Args[2];
API.Config.BlipScale=(float)e.Args[3];
API.Config.BlipColor = (BlipColor)(byte)e.Args[1];
API.Config.BlipSprite = (BlipSprite)(ushort)e.Args[2];
API.Config.BlipScale = (float)e.Args[3];
}
else
{
var b = p.AttachedBlip;
if (b == null) { b=p.AddBlip(); }
b.Color=(BlipColor)(byte)e.Args[1];
b.Sprite=(BlipSprite)(ushort)e.Args[2];
b.Scale=(float)e.Args[3];
if (b == null) { b = p.AddBlip(); }
b.Color = (BlipColor)(byte)e.Args[1];
b.Sprite = (BlipSprite)(ushort)e.Args[2];
b.Scale = (float)e.Args[3];
}
}
@ -94,17 +94,17 @@ namespace RageCoop.Client.Scripting
var vehicleModel = (Model)e.Args[1];
vehicleModel.Request(1000);
Vehicle veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
while (veh==null)
while (veh == null)
{
veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]);
Thread.Sleep(10);
}
veh.CanPretendOccupants=false;
veh.CanPretendOccupants = false;
var v = new SyncedVehicle()
{
ID=(int)e.Args[0],
MainVehicle=veh,
OwnerID=Main.LocalPlayerID,
ID = (int)e.Args[0],
MainVehicle = veh,
OwnerID = Main.LocalPlayerID,
};
EntityPool.Add(v);
}
@ -127,10 +127,9 @@ namespace RageCoop.Client.Scripting
var pos = (Vector3)obj.Args[4];
int rot = (int)obj.Args[5];
var name = (string)obj.Args[6];
Blip blip;
if (!EntityPool.ServerBlips.TryGetValue(id, out blip))
if (!EntityPool.ServerBlips.TryGetValue(id, out Blip blip))
{
EntityPool.ServerBlips.Add(id, blip=World.CreateBlip(pos));
EntityPool.ServerBlips.Add(id, blip = World.CreateBlip(pos));
}
blip.Sprite = sprite;
blip.Color = color;
@ -152,14 +151,14 @@ namespace RageCoop.Client.Scripting
private void SetNameTag(CustomEventReceivedArgs e)
{
var p = PlayerList.GetPlayer((int)e.Args[0]);
if (p!= null)
if (p != null)
{
p.DisplayNameTag=(bool)e.Args[1];
p.DisplayNameTag = (bool)e.Args[1];
}
}
private void SetAutoRespawn(CustomEventReceivedArgs args)
{
API.Config.EnableAutoRespawn=(bool)args.Args[0];
API.Config.EnableAutoRespawn = (bool)args.Args[0];
}
private void DeleteServerProp(CustomEventReceivedArgs e)
{
@ -179,13 +178,13 @@ namespace RageCoop.Client.Scripting
{
if (!EntityPool.ServerProps.TryGetValue(id, out prop))
{
EntityPool.ServerProps.Add(id, prop=new SyncedProp(id));
EntityPool.ServerProps.Add(id, prop = new SyncedProp(id));
}
}
prop.LastSynced=Main.Ticked+1;
prop.Model= (Model)e.Args[1];
prop.Position=(Vector3)e.Args[2];
prop.Rotation=(Vector3)e.Args[3];
prop.LastSynced = Main.Ticked + 1;
prop.Model = (Model)e.Args[1];
prop.Position = (Vector3)e.Args[2];
prop.Rotation = (Vector3)e.Args[3];
prop.Update();
}
private void NativeCall(CustomEventReceivedArgs e)
@ -194,14 +193,14 @@ namespace RageCoop.Client.Scripting
int i;
var ty = (byte)e.Args[0];
TypeCode returnType = (TypeCode)ty;
i = returnType==TypeCode.Empty ? 1 : 2;
i = returnType == TypeCode.Empty ? 1 : 2;
var hash = (Hash)e.Args[i++];
for (; i<e.Args.Length; i++)
for (; i < e.Args.Length; i++)
{
arguments.Add(GetInputArgument(e.Args[i]));
}
if (returnType==TypeCode.Empty)
if (returnType == TypeCode.Empty)
{
Function.Call(hash, arguments.ToArray());
return;

View File

@ -30,7 +30,7 @@ namespace RageCoop.Client.Scripting
/// <summary>
/// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/>
/// </summary>
public Core.Logger Logger { get { return CurrentResource.Logger; } }
public Core.Logger Logger => CurrentResource.Logger;
}
}

View File

@ -38,9 +38,11 @@ namespace RageCoop.Client.Scripting
}
internal class Resources
{
private readonly List<ClientResource> LoadedResources = new List<ClientResource>();
private const string BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
private Logger Logger { get; set; }
public Resources()
{
BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
Logger = Main.Logger;
}
private void StartAll()
@ -53,12 +55,12 @@ namespace RageCoop.Client.Scripting
{
try
{
s.CurrentResource=d;
s.CurrentResource = d;
s.OnStart();
}
catch (Exception ex)
{
Logger.Error("Error occurred when starting script:"+s.GetType().FullName);
Logger.Error("Error occurred when starting script:" + s.GetType().FullName);
Logger?.Error(ex);
}
}
@ -79,7 +81,7 @@ namespace RageCoop.Client.Scripting
}
catch (Exception ex)
{
Logger.Error("Error occurred when stopping script:"+s.GetType().FullName);
Logger.Error("Error occurred when stopping script:" + s.GetType().FullName);
Logger?.Error(ex);
}
}
@ -106,16 +108,6 @@ namespace RageCoop.Client.Scripting
}
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)
{
@ -124,22 +116,22 @@ namespace RageCoop.Client.Scripting
{
Logger = Main.Logger,
Scripts = new List<ClientScript>(),
Name=Path.GetFileNameWithoutExtension(file.Name),
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
Name = Path.GetFileNameWithoutExtension(file.Name),
DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
};
Directory.CreateDirectory(r.DataFolder);
foreach (ZipEntry entry in file)
{
ResourceFile rFile;
r.Files.Add(entry.Name, rFile=new ResourceFile()
r.Files.Add(entry.Name, rFile = new ResourceFile()
{
Name=entry.Name,
IsDirectory=entry.IsDirectory,
Name = entry.Name,
IsDirectory = entry.IsDirectory,
});
if (!entry.IsDirectory)
{
rFile.GetStream=() => { return file.GetInputStream(entry); };
rFile.GetStream = () => { return file.GetInputStream(entry); };
if (entry.Name.EndsWith(".dll") && !entry.Name.Contains("/"))
{
// Don't load API assembly
@ -152,12 +144,12 @@ namespace RageCoop.Client.Scripting
{
continue;
}
var asm=Assembly.LoadFrom(tmp);
toLoad.Add(() => LoadScriptsFromAssembly(rFile,asm, entry.Name,r));
var asm = Assembly.LoadFrom(tmp);
toLoad.Add(() => LoadScriptsFromAssembly(rFile, asm, entry.Name, r));
}
}
}
foreach(var a in toLoad)
foreach (var a in toLoad)
{
a();
}
@ -181,8 +173,8 @@ namespace RageCoop.Client.Scripting
// Invoke script constructor
var script = constructor.Invoke(null) as ClientScript;
// script.CurrentResource = toload;
script.CurrentFile=rfile;
script.CurrentResource=toload;
script.CurrentFile = rfile;
script.CurrentResource = toload;
toload.Scripts.Add(script);
count++;
}

View File

@ -7,7 +7,7 @@ namespace RageCoop.Client
{
public RSA ServerRSA { get; set; }
public Aes ClientAes { get; set; } = Aes.Create();
private Logger Logger;
private readonly Logger Logger;
public Security(Logger logger)
{
Logger = logger;
@ -17,8 +17,8 @@ namespace RageCoop.Client
public void GetSymmetricKeysCrypted(out byte[] cryptedKey, out byte[] cryptedIV)
{
// Logger?.Debug($"Aes.Key:{ClientAes.Key.Dump()}, Aes.IV:{ClientAes.IV.Dump()}");
cryptedKey =ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1);
cryptedIV =ServerRSA.Encrypt(ClientAes.IV, RSAEncryptionPadding.Pkcs1);
cryptedKey = ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1);
cryptedIV = ServerRSA.Encrypt(ClientAes.IV, RSAEncryptionPadding.Pkcs1);
}
public byte[] Encrypt(byte[] data)
{
@ -33,11 +33,11 @@ namespace RageCoop.Client
var para = new RSAParameters();
para.Modulus = modulus;
para.Exponent = exponent;
ServerRSA=RSA.Create(para);
ServerRSA = RSA.Create(para);
}
public void Regen()
{
ClientAes=Aes.Create();
ClientAes = Aes.Create();
ClientAes.GenerateKey();
ClientAes.GenerateIV();
}

View File

@ -22,7 +22,7 @@ namespace RageCoop.Client
/// <summary>
/// Don't use it!
/// </summary>
public string MasterServer { get; set; } = "https://masterserver.ragecoop.online/";
public string MasterServer { get; set; } = "https://masterserver.ragecoop.com/";
/// <summary>
/// Don't use it!
/// </summary>
@ -36,7 +36,7 @@ namespace RageCoop.Client
/// LogLevel for RageCoop.
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
public int LogLevel = 2;
public int LogLevel = 00;
/// <summary>
/// The key to open menu
@ -51,7 +51,7 @@ namespace RageCoop.Client
/// <summary>
/// Disable world NPC traffic, mission entities won't be affected
/// </summary>
public bool DisableTraffic { get; set; } = true;
public bool DisableTraffic { get; set; } = false;
/// <summary>
/// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed.
@ -67,9 +67,25 @@ namespace RageCoop.Client
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary>
public int WorldPedSoftLimit { get; set; } = 30;
/// <summary>
/// The directory where log and resources downloaded from server will be placed.
/// </summary>
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

@ -116,10 +116,10 @@ namespace RageCoop.Client
case unchecked((uint)-1357824103):
case unchecked((uint)-1074790547):
case unchecked((uint)2132975508):
case unchecked(2132975508):
case unchecked((uint)-2084633992):
case unchecked((uint)-952879014):
case unchecked((uint)100416529):
case unchecked(100416529):
case unchecked((uint)WeaponHash.Gusenberg):
case unchecked((uint)WeaponHash.MG):
case unchecked((uint)WeaponHash.CombatMG):

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

@ -15,7 +15,6 @@ namespace RageCoop.Client
/// </summary>
public partial class SyncedPed : SyncedEntity
{
#region CONSTRUCTORS
/// <summary>
/// Create a local entity (outgoing sync)
@ -23,13 +22,13 @@ namespace RageCoop.Client
/// <param name="p"></param>
internal SyncedPed(Ped p)
{
ID=EntityPool.RequestNewID();
p.CanWrithe=false;
p.IsOnlyDamagedByPlayer=false;
MainPed=p;
OwnerID=Main.LocalPlayerID;
ID = EntityPool.RequestNewID();
p.CanWrithe = false;
p.IsOnlyDamagedByPlayer = false;
MainPed = p;
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_DisableMelee, true);
@ -40,82 +39,13 @@ namespace RageCoop.Client
/// </summary>
internal SyncedPed(int id)
{
ID=id;
LastSynced=Main.Ticked;
ID = id;
LastSynced = Main.Ticked;
}
#endregion
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; }
internal Vehicle _lastVehicle { get; set; }
internal int _lastVehicleID { 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;
#region -- VARIABLES --
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 int _lastWeaponObj = 0;
#endregion
internal Vector3 AimCoords { get; set; }
private WeaponAsset WeaponAsset { get; set; }
internal override void Update()
{
if (Owner==null) { return; }
if (Owner == null) { OwnerID = OwnerID; return; }
if (IsPlayer)
{
RenderNameTag();
@ -136,9 +66,9 @@ namespace RageCoop.Client
}
// Need to update state
if (LastFullSynced>=LastUpdated)
if (LastFullSynced >= LastUpdated)
{
if (MainPed!=null&& (Model != MainPed.Model.Hash))
if (MainPed != null && (Model != MainPed.Model.Hash))
{
if (!CreateCharacter())
{
@ -146,33 +76,34 @@ namespace RageCoop.Client
}
}
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=null;
PedBlip = null;
}
else if (((byte)BlipColor != 255) && PedBlip==null)
else if ((byte)BlipColor != 255 && PedBlip == null)
{
PedBlip=MainPed.AddBlip();
PedBlip = MainPed.AddBlip();
PedBlip.Color = BlipColor;
PedBlip.Sprite = BlipSprite;
PedBlip.Scale = BlipScale;
}
if (PedBlip != null)
{
if (PedBlip.Color != BlipColor)
{
PedBlip.Color = BlipColor;
}
if (PedBlip.Sprite != BlipSprite)
{
PedBlip.Sprite = BlipSprite;
}
if (IsPlayer)
{
// Main.Logger.Debug("blip:"+Player.Username);
Main.QueueAction(() => { PedBlip.Name=Owner.Username; });
}
PedBlip.Color=BlipColor;
PedBlip.Sprite=BlipSprite;
PedBlip.Scale=BlipScale;
}
if (PedBlip!=null)
{
if (PedBlip.Color!=BlipColor)
{
PedBlip.Color=BlipColor;
}
if (PedBlip.Sprite!=BlipSprite)
{
PedBlip.Sprite=BlipSprite;
PedBlip.Name = Owner.Username;
}
}
@ -180,11 +111,13 @@ namespace RageCoop.Client
{
SetClothes();
}
CheckCurrentWeapon();
}
if (MainPed.IsDead)
{
if (Health>0)
if (Health > 0)
{
if (IsPlayer)
{
@ -196,7 +129,7 @@ namespace RageCoop.Client
}
}
}
else if (IsPlayer&&(MainPed.Health != Health))
else if (IsPlayer && (MainPed.Health != Health))
{
MainPed.Health = Health;
@ -207,13 +140,20 @@ namespace RageCoop.Client
return;
}
}
if (Speed>=4)
if (!IsPlayer && Health <= 0 && !MainPed.IsDead)
{
MainPed.Kill();
return;
}
if (Speed >= 4)
{
DisplayInVehicle();
}
else
{
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); }
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); return; }
DisplayOnFoot();
}
@ -232,12 +172,12 @@ namespace RageCoop.Client
}
}
LastUpdated=Main.Ticked;
LastUpdated = Main.Ticked;
}
private void RenderNameTag()
{
if (!Owner.DisplayNameTag || (MainPed==null) || !MainPed.IsVisible || !MainPed.IsInRange(Main.PlayerPosition, 40f))
if (!Owner.DisplayNameTag || !Main.Settings.ShowPlayerNameTag || MainPed == null || !MainPed.IsVisible || !MainPed.IsInRange(Main.PlayerPosition, 40f))
{
return;
}
@ -246,12 +186,12 @@ namespace RageCoop.Client
Point toDraw = default;
if (Util.WorldToScreen(targetPos, ref toDraw))
{
toDraw.Y-=100;
toDraw.Y -= 100;
new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon)
{
Outline = true,
Alignment = GTA.UI.Alignment.Center,
Color=Owner.HasDirectConnection? Color.FromArgb(179, 229, 252) : Color.White,
Color = Owner.HasDirectConnection ? Color.FromArgb(179, 229, 252) : Color.White,
}.Draw();
}
}
@ -271,7 +211,7 @@ namespace RageCoop.Client
MainPed = null;
}
if (PedBlip != null && PedBlip.Exists())
if (PedBlip != null)
{
PedBlip.Delete();
PedBlip = null;
@ -290,19 +230,19 @@ namespace RageCoop.Client
Model.MarkAsNoLongerNeeded();
MainPed.BlockPermanentEvents = true;
MainPed.CanWrithe=false;
MainPed.CanWrithe = false;
MainPed.CanBeDraggedOutOfVehicle = true;
MainPed.IsOnlyDamagedByPlayer = false;
MainPed.RelationshipGroup=Main.SyncedPedsGroup;
MainPed.IsFireProof=false;
MainPed.IsExplosionProof=false;
MainPed.RelationshipGroup = Main.SyncedPedsGroup;
MainPed.IsFireProof = false;
MainPed.IsExplosionProof = false;
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, false);
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED, MainPed.Handle, 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_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);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false);
@ -318,8 +258,8 @@ namespace RageCoop.Client
SetClothes();
if (IsPlayer) { MainPed.IsInvincible=true; }
if (IsInvincible) { MainPed.IsInvincible=true; }
if (IsPlayer) { MainPed.IsInvincible = true; }
if (IsInvincible) { MainPed.IsInvincible = true; }
lock (EntityPool.PedsLock)
{
@ -334,19 +274,15 @@ namespace RageCoop.Client
{
for (byte i = 0; i < 12; i++)
{
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i], (int)Clothes[i+12], (int)Clothes[i+24]);
Function.Call(Hash.SET_PED_COMPONENT_VARIATION, MainPed.Handle, i, (int)Clothes[i], (int)Clothes[i + 12], (int)Clothes[i + 24]);
}
_lastClothes = Clothes;
}
#region ONFOOT
private string[] _currentAnimation = new string[2] { "", "" };
private void DisplayOnFoot()
{
CheckCurrentWeapon();
if (IsInParachuteFreeFall)
{
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
@ -490,7 +426,7 @@ namespace RageCoop.Client
}
_lastIsJumping = false;
if (IsRagdoll || Health==0)
if (IsRagdoll || (IsPlayer && Health == 0))
{
if (!MainPed.IsRagdoll)
{
@ -500,26 +436,22 @@ namespace RageCoop.Client
if (!_lastRagdoll)
{
_lastRagdoll = true;
_lastRagdollTime=Main.Ticked;
_lastRagdollTime = Main.Ticked;
}
return;
}
else
if (MainPed.IsRagdoll)
{
if (MainPed.IsRagdoll)
if (Speed == 0)
{
if (Speed==0)
{
MainPed.CancelRagdoll();
}
else
{
MainPed.Task.ClearAllImmediately();
}
return;
MainPed.CancelRagdoll();
}
else
{
MainPed.Task.ClearAllImmediately();
}
_lastRagdoll = false;
return;
}
if (IsReloading)
@ -549,11 +481,11 @@ namespace RageCoop.Client
Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle);
}
_lastInCover=true;
_lastInCover = true;
if (IsAiming)
{
DisplayAiming();
_lastInCover=false;
_lastInCover = false;
}
else if (MainPed.IsInCover)
{
@ -563,7 +495,7 @@ namespace RageCoop.Client
else if (_lastInCover)
{
MainPed.Task.ClearAllImmediately();
_lastInCover=false;
_lastInCover = false;
}
else if (IsAiming)
{
@ -579,17 +511,15 @@ namespace RageCoop.Client
}
}
#region WEAPON
private void CheckCurrentWeapon()
{
if (!WeaponAsset.IsLoaded) { WeaponAsset.Request(); }
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(); }
WeaponAsset=new WeaponAsset(CurrentWeaponHash);
MainPed.Weapons.RemoveAll();
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
new WeaponAsset(CurrentWeaponHash).Request();
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 (WeaponComponents != null && WeaponComponents.Count != 0)
@ -598,15 +528,15 @@ namespace RageCoop.Client
{
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;
}
if (Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash)!=WeaponTint)
if (Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash) != WeaponTint)
{
Function.Call<int>(Hash.SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash, WeaponTint);
}
@ -614,27 +544,25 @@ namespace RageCoop.Client
private void DisplayAiming()
{
if (Velocity==default)
if (Velocity == default)
{
MainPed.Task.AimAt(AimCoords, 1000);
}
else
{
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle,
Position.X+Velocity.X, Position.Y+Velocity.Y, Position.Z+Velocity.Z,
Position.X + Velocity.X, Position.Y + Velocity.Y, Position.Z + Velocity.Z,
AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false, 0);
}
SmoothTransition();
}
#endregion
private bool LastMoving;
private void WalkTo()
{
MainPed.Task.ClearAll();
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0);
Vector3 predictPosition = Position + (Position - MainPed.ReadPosition()) + Velocity * 0.5f;
Vector3 predictPosition = Predict(Position) + Velocity;
float range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
switch (Speed)
@ -676,6 +604,8 @@ namespace RageCoop.Client
MainPed.Task.StandStill(2000);
LastMoving = false;
}
if (MainPed.IsTaskActive(TaskType.CTaskDiveToGround)) MainPed.Task.ClearAll();
break;
}
SmoothTransition();
@ -684,21 +614,27 @@ namespace RageCoop.Client
private void SmoothTransition()
{
var localRagdoll = MainPed.IsRagdoll;
var dist = Position.DistanceTo(MainPed.ReadPosition());
if (dist>3)
var predicted = Predict(Position);
var dist = predicted.DistanceTo(MainPed.ReadPosition());
if (IsOff(dist))
{
MainPed.PositionNoOffset=Position;
MainPed.PositionNoOffset = predicted;
return;
}
if (!(localRagdoll || MainPed.IsDead))
{
if (!IsAiming && !MainPed.IsGettingUp)
{
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*(Position-MainPed.ReadPosition());
MainPed.Velocity = Velocity + 5 * dist * (predicted - MainPed.ReadPosition());
}
else if (Main.Ticked-_lastRagdollTime<10)
else if (Main.Ticked - _lastRagdollTime < 10)
{
return;
}
@ -708,44 +644,49 @@ namespace RageCoop.Client
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,
amount = 20 * (Predict(HeadPosition) - head.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex=20;
helper.Impulse=20*(HeadPosition-head.Position);
helper.PartIndex = 20;
helper.Impulse = amount;
helper.Start();
helper.Stop();
amount = 20 * (Predict(RightFootPosition) - rightFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1;
helper.PartIndex=6;
helper.Impulse=20*(RightFootPosition-rightFoot.Position);
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=20*(LeftFootPosition-leftFoot.Position);
helper.PartIndex = 3;
helper.Impulse = amount;
helper.Start();
helper.Stop();
}
else
{
MainPed.Velocity=Velocity+5*dist*(Position-MainPed.ReadPosition());
// 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()
{
if (CurrentVehicle==null || CurrentVehicle.MainVehicle==null) { Main.Logger.Error("Veh not found"); return; }
if (CurrentVehicle?.MainVehicle == null) { return; }
switch (Speed)
{
case 4:
if (MainPed.CurrentVehicle!=CurrentVehicle.MainVehicle)
if (MainPed.CurrentVehicle != CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
{
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat);
}
@ -754,41 +695,40 @@ namespace RageCoop.Client
// 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);
}
if (MainPed.VehicleWeapon==VehicleWeaponHash.Invalid)
if (MainPed.VehicleWeapon == VehicleWeaponHash.Invalid)
{
// World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue);
CheckCurrentWeapon();
if (IsAiming)
{
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z);
if (!_lastDriveBy)
{
_lastDriveBy=true;
_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;
_lastDriveBy = false;
}
}
else if (MainPed.VehicleWeapon!=(VehicleWeaponHash)CurrentWeaponHash)
else if (MainPed.VehicleWeapon != (VehicleWeaponHash)CurrentWeaponHash)
{
MainPed.VehicleWeapon=(VehicleWeaponHash)CurrentWeaponHash;
MainPed.VehicleWeapon = (VehicleWeaponHash)CurrentWeaponHash;
}
break;
case 5:
if (MainPed.VehicleTryingToEnter!=CurrentVehicle.MainVehicle || MainPed.GetSeatTryingToEnter()!=Seat)
if (MainPed.VehicleTryingToEnter != CurrentVehicle.MainVehicle || MainPed.GetSeatTryingToEnter() != Seat)
{
MainPed.Task.EnterVehicle(CurrentVehicle.MainVehicle,Seat,-1,5,EnterVehicleFlags.AllowJacking);
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);
MainPed.Task.LeaveVehicle(CurrentVehicle.Velocity.Length() > 5f ? LeaveVehicleFlags.BailOut : LeaveVehicleFlags.None);
}
break;
}

View File

@ -33,9 +33,13 @@ namespace RageCoop.Client
get => _ownerID;
internal set
{
if (value==_ownerID && Owner!=null) { return; }
if (value == _ownerID && Owner != null) { return; }
_ownerID = value;
Owner=PlayerList.GetPlayer(value);
Owner = PlayerList.GetPlayer(value);
if (this is SyncedPed && Owner != null)
{
Owner.Character = ((SyncedPed)this);
}
}
}
@ -80,16 +84,33 @@ namespace RageCoop.Client
/// <summary>
///
/// </summary>
internal protected bool _lastFrozen = false;
protected internal bool _lastFrozen = false;
internal Model Model { get; set; }
internal Vector3 Position { get; set; }
internal Vector3 Rotation { get; set; }
internal Quaternion Quaternion { get; set; }
internal Vector3 Velocity { get; set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
internal abstract void Update();
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,13 +1,13 @@
using GTA;
using GTA.Math;
using RageCoop.Core;
using GTA.Native;
using RageCoop.Core;
namespace RageCoop.Client
{
internal class SyncedProjectile : SyncedEntity
{
public ProjectileDataFlags Flags { private get; set; }=ProjectileDataFlags.None;
public ProjectileDataFlags Flags { private get; set; } = ProjectileDataFlags.None;
public readonly Vector3 Origin;
private bool _firstSend = false;
@ -19,28 +19,27 @@ namespace RageCoop.Client
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 { } }
internal override Player Owner => Shooter.Owner;
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;
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)
if (MainProjectile.AttachedEntity != null)
{
p.Flags |= ProjectileDataFlags.IsAttached;
}
@ -51,44 +50,44 @@ namespace RageCoop.Client
if (_firstSend)
{
p.Flags |= ProjectileDataFlags.IsAttached;
_firstSend=false;
_firstSend = false;
}
}
public SyncedProjectile(Projectile p)
{
var owner = p.OwnerEntity;
if (owner==null) { IsValid=false;return; }
ID=EntityPool.RequestNewID();
if (owner == null) { IsValid = false; return; }
ID = EntityPool.RequestNewID();
MainProjectile = p;
Origin=p.Position;
if(EntityPool.PedsByHandle.TryGetValue(owner.Handle,out var shooter))
Origin = p.Position;
if (EntityPool.PedsByHandle.TryGetValue(owner.Handle, out var shooter))
{
if (shooter.MainPed!=null
&& (p.AttachedEntity==shooter.MainPed.Weapons.CurrentWeaponObject
|| p.AttachedEntity== shooter.MainPed))
if (shooter.MainPed != null
&& (p.AttachedEntity == shooter.MainPed.Weapons.CurrentWeaponObject
|| p.AttachedEntity == shooter.MainPed))
{
// Reloading
IsValid=false;
IsValid = false;
return;
}
Shooter=shooter;
IsLocal=shooter.IsLocal;
Shooter = shooter;
IsLocal = shooter.IsLocal;
}
else if(EntityPool.VehiclesByHandle.TryGetValue(owner.Handle,out var shooterVeh))
else if (EntityPool.VehiclesByHandle.TryGetValue(owner.Handle, out var shooterVeh))
{
Shooter=shooterVeh;
IsLocal=shooterVeh.IsLocal;
Shooter = shooterVeh;
IsLocal = shooterVeh.IsLocal;
}
else
{
IsValid=false;
IsValid = false;
}
}
public SyncedProjectile(int id)
{
ID= id;
IsLocal=false;
ID = id;
IsLocal = false;
}
internal override void Update()
{
@ -101,27 +100,26 @@ namespace RageCoop.Client
CreateProjectile();
return;
}
MainProjectile.Velocity=Velocity+(Position+Shooter.Owner.PacketTravelTime*Velocity-MainProjectile.Position);
MainProjectile.Rotation=Rotation;
LastUpdated=Main.Ticked;
MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
MainProjectile.Rotation = Rotation;
LastUpdated = Main.Ticked;
}
private void CreateProjectile()
{
Asset=new WeaponAsset(WeaponHash);
Asset = new WeaponAsset(WeaponHash);
if (!Asset.IsLoaded) { Asset.Request(); return; }
if(Shooter == null) { return; }
if (Shooter == null) { return; }
Entity owner;
owner=(Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle;
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,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();
MainProjectile=ps[ps.Length-1];
MainProjectile.IsCollisionEnabled=false;
MainProjectile.Position=Position;
MainProjectile.Rotation =Rotation;
MainProjectile.Velocity=Velocity;
Main.Delay(()=>MainProjectile.IsCollisionEnabled=true, 100);
MainProjectile = ps[ps.Length - 1];
MainProjectile.Position = Position;
MainProjectile.Rotation = Rotation;
MainProjectile.Velocity = Velocity;
EntityPool.Add(this);
}
}

View File

@ -9,7 +9,7 @@ namespace RageCoop.Client
{
internal SyncedProp(int id)
{
ID= id;
ID = id;
}
/// <summary>
/// The real entity
@ -27,15 +27,15 @@ namespace RageCoop.Client
{
if (!NeedUpdate) { return; }
if (MainProp== null || !MainProp.Exists())
if (MainProp == null || !MainProp.Exists())
{
MainProp=World.CreateProp(Model, Position, Rotation, false, false);
MainProp.IsInvincible=true;
MainProp = World.CreateProp(Model, Position, Rotation, false, false);
MainProp.IsInvincible = true;
}
MainProp.Position=Position;
MainProp.Rotation=Rotation;
MainProp.Position = Position;
MainProp.Rotation = Rotation;
MainProp.SetFrozen(true);
LastUpdated=Main.Ticked;
LastUpdated = Main.Ticked;
}
}
}

View File

@ -1,15 +1,13 @@
using System;
using RageCoop.Core;
using GTA;
using System.Diagnostics;
using System.Collections.Generic;
using GTA.Math;
using GTA.Native;
using RageCoop.Core;
using System.Collections.Generic;
namespace RageCoop.Client{
public partial class SyncedVehicle{
namespace RageCoop.Client
{
public partial class SyncedVehicle
{
public Vehicle MainVehicle { get; internal set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
#region -- SYNC DATA --
@ -69,11 +67,9 @@ namespace RageCoop.Client{
private bool _lastHornActive = false;
private bool _lastTransformed = false;
internal int _lastLivery = -1;
List<Vector3> _predictedTrace = new List<Vector3>();
List<Vector3> _orgTrace = new List<Vector3>();
private readonly List<Vector3> _predictedTrace = new List<Vector3>();
private readonly List<Vector3> _orgTrace = new List<Vector3>();
private Vector3 _predictedPosition;
float _elapsed;
#endregion
#region OUTGOING

View File

@ -4,8 +4,7 @@ using GTA.Native;
using RageCoop.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
namespace RageCoop.Client
{
@ -24,22 +23,24 @@ namespace RageCoop.Client
internal SyncedVehicle(Vehicle v)
{
ID=EntityPool.RequestNewID();
MainVehicle=v;
MainVehicle.CanPretendOccupants=false;
OwnerID=Main.LocalPlayerID;
ID = EntityPool.RequestNewID();
MainVehicle = v;
MainVehicle.CanPretendOccupants = false;
OwnerID = Main.LocalPlayerID;
SetUpFixedData();
}
private void 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;
IsSubmarineCar = MainVehicle.IsSubmarineCar;
IsDeluxo = MainVehicle.Model == 1483171323;
}
/// <summary>
@ -51,8 +52,8 @@ namespace RageCoop.Client
}
internal SyncedVehicle(int id)
{
ID=id;
LastSynced=Main.Ticked;
ID = id;
LastSynced = Main.Ticked;
}
#endregion
/// <summary>
@ -85,6 +86,8 @@ namespace RageCoop.Client
}
}
DisplayVehicle();
// Skip update if no new sync message has arrived.
if (!NeedUpdate)
{
@ -95,10 +98,8 @@ namespace RageCoop.Client
{
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
}
MainVehicle.ThrottlePower=ThrottlePower;
MainVehicle.BrakePower=BrakePower;
var v = Main.P.CurrentVehicle;
DisplayVehicle(v != null && MainVehicle.IsTouching(v));
MainVehicle.ThrottlePower = ThrottlePower;
MainVehicle.BrakePower = BrakePower;
if (IsDead)
{
@ -119,7 +120,7 @@ namespace RageCoop.Client
{
MainVehicle.Repair();
}
},1000);
}, 1000);
}
}
if (MainVehicle.IsOnFire)
@ -149,8 +150,6 @@ namespace RageCoop.Client
MainVehicle.AreHighBeamsOn = HighBeamsOn;
}
if (IsAircraft)
{
if (LandingGear != (byte)MainVehicle.LandingGearState)
@ -179,15 +178,17 @@ namespace RageCoop.Client
MainVehicle.SoundHorn(1);
}
if (HasRoof && MainVehicle.RoofState!=RoofState)
if (HasRoof && MainVehicle.RoofState != RoofState)
{
MainVehicle.RoofState=RoofState;
MainVehicle.RoofState = RoofState;
}
if(HasRocketBoost && Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) != MainVehicle.IsRocketBoostActive()){
if (HasRocketBoost && Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive) != MainVehicle.IsRocketBoostActive())
{
MainVehicle.SetRocketBoostActive(Flags.HasFlag(VehicleDataFlags.IsRocketBoostActive));
}
if(HasParachute && Flags.HasFlag(VehicleDataFlags.IsParachuteActive) != MainVehicle.IsParachuteActive()){
if (HasParachute && Flags.HasFlag(VehicleDataFlags.IsParachuteActive) != MainVehicle.IsParachuteActive())
{
MainVehicle.SetParachuteActive(Flags.HasFlag(VehicleDataFlags.IsParachuteActive));
}
if (IsSubmarineCar)
@ -197,31 +198,32 @@ namespace RageCoop.Client
if (!_lastTransformed)
{
_lastTransformed = true;
Function.Call(Hash._TRANSFORM_VEHICLE_TO_SUBMARINE, MainVehicle.Handle, false);
Function.Call(Hash.TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
}
}
else if (_lastTransformed)
{
_lastTransformed = false;
Function.Call(Hash._TRANSFORM_SUBMARINE_TO_VEHICLE, MainVehicle.Handle, false);
Function.Call(Hash.TRANSFORM_TO_CAR, MainVehicle.Handle, false);
}
}
else if(IsDeluxo && IsDeluxoHovering!=MainVehicle.IsDeluxoHovering()){
else if (IsDeluxo)
{
MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
if(IsDeluxoHovering){
if (IsDeluxoHovering)
{
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
}
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn);
MainVehicle.SetDamageModel(DamageModel);
}
MainVehicle.LockStatus=LockStatus;
MainVehicle.LockStatus = LockStatus;
if (LastFullSynced>=LastUpdated)
if (LastFullSynced >= LastUpdated)
{
#region -- SYNC STATE --
if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{
MainVehicle.Repair();
@ -232,7 +234,7 @@ namespace RageCoop.Client
_lastVehicleColors = Colors;
}
MainVehicle.EngineHealth=EngineHealth;
MainVehicle.EngineHealth = EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods))
{
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
@ -245,24 +247,24 @@ namespace RageCoop.Client
_lastVehicleMods = Mods;
}
if (Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle)!=LicensePlate)
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)
if (_lastLivery != Livery)
{
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, Livery);
_lastLivery=Livery;
_lastLivery = Livery;
}
#endregion
MainVehicle.SetDamageModel(DamageModel);
}
LastUpdated=Main.Ticked;
LastUpdated = Main.Ticked;
}
void DisplayVehicle(bool touching)
private void DisplayVehicle()
{
_elapsed = Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds;
_predictedPosition = Position + _elapsed * Velocity;
_predictedPosition = Predict(Position);
var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(_predictedPosition);
var cali = dist * (_predictedPosition - current);
@ -274,59 +276,48 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion;
return;
}
if (dist > 0.03)
{
MainVehicle.Velocity = Velocity + cali;
}
MainVehicle.Velocity = Velocity+cali;
if (IsFlipped)
Vector3 calirot;
if (IsFlipped || (calirot = GetCalibrationRotation()).Length() > 50)
{
MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
MainVehicle.RotationVelocity = RotationVelocity;
return;
}
Vector3 calirot = GetCalibrationRotation();
if (calirot.Length() < 50)
{
MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
}
else
{
MainVehicle.Quaternion = Quaternion;
MainVehicle.RotationVelocity = RotationVelocity;
}
#if DEBUG_VEH
if (_orgTrace.Count >= 30)
{
_orgTrace.RemoveAt(0);
}
if (_predictedTrace.Count >= 30)
{
_predictedTrace.RemoveAt(0);
}
_orgTrace.Add(Position);
_predictedTrace.Add(_predictedPos);
#endif
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 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; }
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.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; }
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)
@ -334,7 +325,7 @@ namespace RageCoop.Client
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return false;
}
else if (MainVehicle==null)
if (MainVehicle == null)
{
Model.Request();
return false;
@ -346,54 +337,16 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof)
{
MainVehicle.RoofState=RoofState;
MainVehicle.RoofState = RoofState;
}
foreach(var w in MainVehicle.Wheels)
foreach (var w in MainVehicle.Wheels)
{
w.Fix();
}
if (IsInvincible) { MainVehicle.IsInvincible=true; }
if (IsInvincible) { MainVehicle.IsInvincible = true; }
SetUpFixedData();
Model.MarkAsNoLongerNeeded();
return true;
}
#region -- PEDALING --
/*
* Thanks to @oldnapalm.
*/
private string PedalingAnimDict()
{
switch ((VehicleHash)Model)
{
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
}
}

View File

@ -1,18 +1,17 @@
using GTA;
using GTA.Native;
using Lidgren.Network;
using RageCoop.Client.Scripting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Lidgren.Network;
namespace RageCoop.Client
{
internal class EntityPool
{
public static object PedsLock = new object();
public static int CharactersCount { get { return PedsByID.Count; } }
#if BENCHMARK
private static Stopwatch PerfCounter=new Stopwatch();
private static Stopwatch PerfCounter2=Stopwatch.StartNew();
@ -44,17 +43,17 @@ namespace RageCoop.Client
#endregion
public static void Cleanup(bool keepPlayer = true, bool keepMine = true)
{
foreach (int id in new List<int>(PedsByID.Keys))
foreach (var ped in PedsByID.Values.ToArray())
{
if (keepPlayer && (id==Main.LocalPlayerID)|| keepMine && (PedsByID[id].OwnerID == Main.LocalPlayerID)) { continue; }
RemovePed(id);
if ((keepPlayer && (ped.ID == Main.LocalPlayerID)) || (keepMine && (ped.OwnerID == Main.LocalPlayerID))) { continue; }
RemovePed(ped.ID);
}
PedsByID.Clear();
PedsByHandle.Clear();
foreach (int id in new List<int>(VehiclesByID.Keys))
foreach (int id in VehiclesByID.Keys.ToArray())
{
if (keepMine&&(VehiclesByID[id].OwnerID==Main.LocalPlayerID)) { continue; }
if (keepMine && (VehiclesByID[id].OwnerID == Main.LocalPlayerID)) { continue; }
RemoveVehicle(id);
}
VehiclesByID.Clear();
@ -62,7 +61,7 @@ namespace RageCoop.Client
foreach (var p in ProjectilesByID.Values)
{
if (p.Shooter.ID!=Main.LocalPlayerID && p.MainProjectile!=null && p.MainProjectile.Exists())
if (p.Shooter.ID != Main.LocalPlayerID && p.MainProjectile != null && p.MainProjectile.Exists())
{
p.MainProjectile.Delete();
}
@ -93,8 +92,10 @@ namespace RageCoop.Client
public static bool AddPlayer()
{
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);
if (player==null)
if (player == null)
{
Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
SyncedPed c = new SyncedPed(p);
@ -131,16 +132,16 @@ namespace RageCoop.Client
{
if (PedsByID.ContainsKey(c.ID))
{
PedsByID[c.ID]=c;
PedsByID[c.ID] = c;
}
else
{
PedsByID.Add(c.ID, c);
}
if (c.MainPed==null) { return; }
if (c.MainPed == null) { return; }
if (PedsByHandle.ContainsKey(c.MainPed.Handle))
{
PedsByHandle[c.MainPed.Handle]=c;
PedsByHandle[c.MainPed.Handle] = c;
}
else
{
@ -157,7 +158,7 @@ namespace RageCoop.Client
{
SyncedPed c = PedsByID[id];
var p = c.MainPed;
if (p!=null)
if (p != null)
{
if (PedsByHandle.ContainsKey(p.Handle))
{
@ -182,23 +183,23 @@ namespace RageCoop.Client
#endregion
#region VEHICLES
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;
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;
public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys);
public static void Add(SyncedVehicle v)
{
if (VehiclesByID.ContainsKey(v.ID))
{
VehiclesByID[v.ID]=v;
VehiclesByID[v.ID] = v;
}
else
{
VehiclesByID.Add(v.ID, v);
}
if (v.MainVehicle==null) { return; }
if (v.MainVehicle == null) { return; }
if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle))
{
VehiclesByHandle[v.MainVehicle.Handle]=v;
VehiclesByHandle[v.MainVehicle.Handle] = v;
}
else
{
@ -215,7 +216,7 @@ namespace RageCoop.Client
{
SyncedVehicle v = VehiclesByID[id];
var veh = v.MainVehicle;
if (veh!=null)
if (veh != null)
{
if (VehiclesByHandle.ContainsKey(veh.Handle))
{
@ -237,27 +238,27 @@ namespace RageCoop.Client
#region PROJECTILES
public static SyncedProjectile GetProjectileByID(int id)
{
return ProjectilesByID.TryGetValue(id,out var p) ? p : null;
return ProjectilesByID.TryGetValue(id, out var p) ? p : null;
}
public static void Add(SyncedProjectile p)
{
if (!p.IsValid) { return; }
if (p.WeaponHash==(WeaponHash)VehicleWeaponHash.Tank)
if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank)
{
Networking.SendBullet(p.Position, p.Position+p.Velocity, (uint)VehicleWeaponHash.Tank, ((SyncedVehicle)p.Shooter).MainVehicle.Driver.GetSyncEntity().ID);
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;
ProjectilesByID[p.ID] = p;
}
else
{
ProjectilesByID.Add(p.ID, p);
}
if (p.MainProjectile==null) { return; }
if (p.MainProjectile == null) { return; }
if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle))
{
ProjectilesByHandle[p.MainProjectile.Handle]=p;
ProjectilesByHandle[p.MainProjectile.Handle] = p;
}
else
{
@ -270,14 +271,14 @@ namespace RageCoop.Client
{
SyncedProjectile sp = ProjectilesByID[id];
var p = sp.MainProjectile;
if (p!=null)
if (p != null)
{
if (ProjectilesByHandle.ContainsKey(p.Handle))
{
ProjectilesByHandle.Remove(p.Handle);
}
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
p.Explode();
//Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
if (sp.Exploded) p.Explode();
}
ProjectilesByID.Remove(id);
}
@ -287,11 +288,11 @@ namespace RageCoop.Client
public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id);
public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id);
#endregion
static int vehStateIndex;
static int pedStateIndex;
static int vehStatesPerFrame;
static int pedStatesPerFrame;
static int i;
private static int vehStateIndex;
private static int pedStateIndex;
private static int vehStatesPerFrame;
private static int pedStatesPerFrame;
private static int i;
public static Ped[] allPeds = new Ped[0];
public static Vehicle[] allVehicles = new Vehicle[0];
public static Projectile[] allProjectiles = new Projectile[0];
@ -304,21 +305,10 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
#endif
allPeds = World.GetAllPeds();
allVehicles=World.GetAllVehicles();
allProjectiles=World.GetAllProjectiles();
vehStatesPerFrame=allVehicles.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; }
}
*/
allVehicles = World.GetAllVehicles();
allProjectiles = World.GetAllProjectiles();
vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1;
pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1;
#if BENCHMARK
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
@ -340,10 +330,10 @@ namespace RageCoop.Client
// Outgoing sync
if (p.IsLocal)
{
if (p.MainProjectile.AttachedEntity==null)
if (p.MainProjectile.AttachedEntity == null)
{
// Prevent projectiles from exploding next to vehicle
if (p.WeaponHash==(WeaponHash)VehicleWeaponHash.Tank || p.MainProjectile.Position.DistanceTo(p.Origin)<2)
if (p.WeaponHash == (WeaponHash)VehicleWeaponHash.Tank || (p.MainProjectile.OwnerEntity?.EntityType == EntityType.Vehicle && p.MainProjectile.Position.DistanceTo(p.Origin) < 2))
{
continue;
}
@ -364,26 +354,29 @@ namespace RageCoop.Client
}
}
i=-1;
i = -1;
lock (PedsLock)
{
EntityPool.AddPlayer();
AddPlayer();
var mainCharacters = new List<PedHash> { PedHash.Michael, PedHash.Franklin, PedHash.Franklin02, PedHash.Trevor };
foreach (Ped p in allPeds)
{
SyncedPed c = EntityPool.GetPedByHandle(p.Handle);
if (c==null && (p!=Game.Player.Character))
if (!PedsByHandle.ContainsKey(p.Handle) && p != Game.Player.Character && !mainCharacters.Contains((PedHash)p.Model.Hash))
{
if (allPeds.Length>Main.Settings.WorldPedSoftLimit && p.PopulationType != EntityPopulationType.RandomAmbient)
if (PedsByID.Count(x => x.Value.IsLocal) > Main.Settings.WorldPedSoftLimit)
{
p.Delete();
continue;
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}");
c=new SyncedPed(p);
EntityPool.Add(c);
Add(new SyncedPed(p));
}
}
#if BENCHMARK
@ -391,18 +384,18 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks;
#endif
var ps = PedsByID.Values.ToArray();
pedStateIndex+=pedStatesPerFrame;
if (pedStateIndex>=ps.Length)
pedStateIndex += pedStatesPerFrame;
if (pedStateIndex >= ps.Length)
{
pedStateIndex=0;
pedStateIndex = 0;
}
foreach (SyncedPed c in ps)
{
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;
}
@ -415,7 +408,7 @@ namespace RageCoop.Client
// event check
SyncEvents.Check(c);
Networking.SendPed(c, (i-pedStateIndex)<pedStatesPerFrame);
Networking.SendPed(c, (i - pedStateIndex) < pedStatesPerFrame);
#if BENCHMARK
Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start;
#endif
@ -439,27 +432,25 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
#endif
}
i=-1;
var check = Main.Ticked % 100 == 0;
i = -1;
lock (VehiclesLock)
{
foreach (Vehicle veh in allVehicles)
{
if (!VehiclesByHandle.ContainsKey(veh.Handle))
{
if (allVehicles.Length>Main.Settings.WorldVehicleSoftLimit)
if (VehiclesByID.Count(x => x.Value.IsLocal) > Main.Settings.WorldVehicleSoftLimit)
{
var type = veh.PopulationType;
if (type==EntityPopulationType.RandomAmbient || type==EntityPopulationType.RandomParked)
if (veh.PopulationType == EntityPopulationType.RandomAmbient || veh.PopulationType == EntityPopulationType.RandomParked)
{
foreach (var p in veh.Occupants)
{
p.Delete();
var c = EntityPool.GetPedByHandle(p.Handle);
if (c!=null)
var c = GetPedByHandle(p.Handle);
if (c != null)
{
EntityPool.RemovePed(c.ID, "ThrottleTraffic");
RemovePed(c.ID, "ThrottleTraffic");
}
}
veh.Delete();
@ -475,28 +466,31 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks;
#endif
var vs = VehiclesByID.Values.ToArray();
vehStateIndex+=vehStatesPerFrame;
if (vehStateIndex>=vs.Length)
vehStateIndex += vehStatesPerFrame;
if (vehStateIndex >= vs.Length)
{
vehStateIndex=0;
vehStateIndex = 0;
}
foreach (SyncedVehicle v in vs)
{
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;
}
if (check)
{
v.SetUpFixedData();
}
// Outgoing sync
if (v.IsLocal)
{
if (!v.MainVehicle.IsVisible) { continue; }
SyncEvents.Check(v);
Networking.SendVehicle(v, (i-vehStateIndex)<vehStatesPerFrame);
Networking.SendVehicle(v, (i - vehStateIndex) < vehStatesPerFrame);
}
else // Incoming sync
{
@ -514,12 +508,13 @@ namespace RageCoop.Client
}
Networking.Peer.FlushSendQueue();
}
static void UpdateTargets()
private static void UpdateTargets()
{
Networking.Targets=new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection };
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)
if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition) < 500)
{
Networking.Targets.Add(p.Connection);
}
@ -530,7 +525,7 @@ namespace RageCoop.Client
{
foreach (SyncedPed p in PedsByID.Values.ToArray())
{
if (p.OwnerID==playerPedId)
if (p.OwnerID == playerPedId)
{
RemovePed(p.ID);
}
@ -538,7 +533,7 @@ namespace RageCoop.Client
foreach (SyncedVehicle v in VehiclesByID.Values.ToArray())
{
if (v.OwnerID==playerPedId)
if (v.OwnerID == playerPedId)
{
RemoveVehicle(v.ID);
}
@ -548,7 +543,7 @@ namespace RageCoop.Client
public static int RequestNewID()
{
int ID = 0;
while ((ID==0) || PedsByID.ContainsKey(ID) || VehiclesByID.ContainsKey(ID) || ProjectilesByID.ContainsKey(ID))
while ((ID == 0) || PedsByID.ContainsKey(ID) || VehiclesByID.ContainsKey(ID) || ProjectilesByID.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
@ -579,21 +574,21 @@ namespace RageCoop.Client
{
public static void Add(SyncedVehicle v)
{
lock (EntityPool.VehiclesLock)
lock (VehiclesLock)
{
EntityPool.Add(v);
}
}
public static void Add(SyncedPed p)
{
lock (EntityPool.PedsLock)
lock (PedsLock)
{
EntityPool.Add(p);
}
}
public static void Add(SyncedProjectile sp)
{
lock (EntityPool.ProjectilesLock)
lock (ProjectilesLock)
{
EntityPool.Add(sp);
}

View File

@ -1,10 +1,8 @@
using GTA;
using GTA.Math;
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Threading;
using System.Threading.Tasks;
using Lidgren.Network;
namespace RageCoop.Client
{
@ -13,7 +11,7 @@ namespace RageCoop.Client
#region TRIGGER
public static void TriggerPedKilled(SyncedPed victim)
{
Networking.SendSync(new Packets.PedKilled() { VictimID=victim.ID }, ConnectionChannel.SyncEvents);
Networking.SendSync(new Packets.PedKilled() { VictimID = victim.ID }, ConnectionChannel.SyncEvents);
}
public static void TriggerChangeOwner(int vehicleID, int newOwnerID)
@ -21,9 +19,9 @@ namespace RageCoop.Client
Networking.SendSync(new Packets.OwnerChanged()
{
ID= vehicleID,
NewOwnerID= newOwnerID,
}, ConnectionChannel.SyncEvents,NetDeliveryMethod.ReliableOrdered);
ID = vehicleID,
NewOwnerID = newOwnerID,
}, ConnectionChannel.SyncEvents, NetDeliveryMethod.ReliableOrdered);
}
@ -33,11 +31,11 @@ namespace RageCoop.Client
var start = owner.MainPed.GetMuzzlePosition();
if (owner.MainPed.IsOnTurretSeat()) { start=owner.MainPed.Bones[Bone.SkelHead].Position; }
if (start.DistanceTo(impactPosition)>10)
if (owner.MainPed.IsOnTurretSeat()) { start = owner.MainPed.Bones[Bone.SkelHead].Position; }
if (start.DistanceTo(impactPosition) > 10)
{
// Reduce latency
start=impactPosition-(impactPosition-start).Normalized*10;
start = impactPosition - (impactPosition - start).Normalized * 10;
}
Networking.SendBullet(start, impactPosition, hash, owner.ID);
}
@ -47,14 +45,14 @@ namespace RageCoop.Client
int i;
// ANNIHL
if (veh.Model.Hash==837858166)
if (veh.Model.Hash == 837858166)
{
Networking.SendVehicleBullet(hash, owner, veh.Bones[35]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[36]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[37]);
Networking.SendVehicleBullet(hash, owner, veh.Bones[38]);
}
else if((i = veh.GetMuzzleIndex())!=-1)
else if ((i = veh.GetMuzzleIndex()) != -1)
{
Networking.SendVehicleBullet(hash, owner, veh.Bones[i]);
}
@ -65,7 +63,7 @@ namespace RageCoop.Client
}
public static void TriggerNozzleTransform(int vehID, bool hover)
{
Networking.SendSync(new Packets.NozzleTransform() { VehicleID=vehID, Hover=hover }, ConnectionChannel.SyncEvents);
Networking.SendSync(new Packets.NozzleTransform() { VehicleID = vehID, Hover = hover }, ConnectionChannel.SyncEvents);
}
#endregion
@ -73,9 +71,8 @@ namespace RageCoop.Client
#region HANDLE
public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core");
static WeaponAsset _weaponAsset = default;
static uint _lastWeaponHash;
private static WeaponAsset _weaponAsset = default;
private static uint _lastWeaponHash;
private static void HandlePedKilled(Packets.PedKilled p)
{
@ -84,11 +81,11 @@ namespace RageCoop.Client
private static void HandleOwnerChanged(Packets.OwnerChanged p)
{
var v = EntityPool.GetVehicleByID(p.ID);
if (v==null) { return; }
v.OwnerID=p.NewOwnerID;
v.LastSynced=Main.Ticked;
v.Position=v.MainVehicle.Position;
v.Quaternion=v.MainVehicle.Quaternion;
if (v == null) { return; }
v.OwnerID = p.NewOwnerID;
v.LastSynced = Main.Ticked;
v.Position = v.MainVehicle.Position;
v.Quaternion = v.MainVehicle.Quaternion;
}
private static void HandleNozzleTransform(Packets.NozzleTransform p)
{
@ -100,47 +97,47 @@ namespace RageCoop.Client
{
// Minigun, not working for some reason
case (uint)WeaponHash.Minigun:
weaponHash=1176362416;
weaponHash = 1176362416;
break;
// Valkyire, not working for some reason
case 2756787765:
weaponHash=1176362416;
weaponHash = 1176362416;
break;
// Tampa3, not working for some reason
case 3670375085:
weaponHash=1176362416;
weaponHash = 1176362416;
break;
// Ruiner2, not working for some reason
case 50118905:
weaponHash=1176362416;
weaponHash = 1176362416;
break;
// SAVAGE
case 1638077257:
weaponHash=(uint)VehicleWeaponHash.PlayerLazer;
weaponHash = (uint)VehicleWeaponHash.PlayerLazer;
break;
case (uint)VehicleWeaponHash.PlayerBuzzard:
weaponHash=1176362416;
weaponHash = 1176362416;
break;
}
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 (_lastWeaponHash!=weaponHash)
if (_lastWeaponHash != weaponHash)
{
_weaponAsset.MarkAsNoLongerNeeded();
_weaponAsset=new WeaponAsset(weaponHash);
_lastWeaponHash=weaponHash;
_weaponAsset = new WeaponAsset(weaponHash);
_lastWeaponHash = weaponHash;
}
if (!_weaponAsset.IsLoaded) { _weaponAsset.Request(); }
World.ShootBullet(start, end, p, _weaponAsset, (int)p.GetWeaponDamage(weaponHash));
Prop w;
if (((w = p.Weapons.CurrentWeaponObject) != null)&&(p.VehicleWeapon==VehicleWeaponHash.Invalid))
if (((w = p.Weapons.CurrentWeaponObject) != null) && (p.VehicleWeapon == VehicleWeaponHash.Invalid))
{
if (p.Weapons.Current.Components.GetSuppressorComponent().Active)
{
@ -154,52 +151,47 @@ namespace RageCoop.Client
}
public static void HandleVehicleBulletShot(Packets.VehicleBulletShot p)
{
HandleBulletShot(p.StartPosition,p.EndPosition,p.WeaponHash,p.OwnerID);
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
var v = EntityPool.GetPedByID(p.OwnerID)?.MainPed.CurrentVehicle;
if(v == null) { return; }
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);
b.Position, b.ForwardVector.ToEulerRotation(v.Bones[35].UpVector), 1);
}
public static void HandleEvent(PacketType type, byte[] data)
public static void HandleEvent(PacketType type, NetIncomingMessage msg)
{
switch (type)
{
case PacketType.BulletShot:
{
Packets.BulletShot p = new Packets.BulletShot();
p.Deserialize(data);
p.Deserialize(msg);
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
break;
}
case PacketType.VehicleBulletShot:
{
HandleVehicleBulletShot(data.GetPacket<Packets.VehicleBulletShot>());
HandleVehicleBulletShot(msg.GetPacket<Packets.VehicleBulletShot>());
break;
}
case PacketType.OwnerChanged:
{
Packets.OwnerChanged packet = new Packets.OwnerChanged();
packet.Deserialize(data);
HandleOwnerChanged(packet);
HandleOwnerChanged(msg.GetPacket<Packets.OwnerChanged>());
}
break;
case PacketType.PedKilled:
{
var packet = new Packets.PedKilled();
packet.Deserialize(data);
HandlePedKilled(packet);
HandlePedKilled(msg.GetPacket<Packets.PedKilled>());
}
break;
case PacketType.NozzleTransform:
{
var packet = new Packets.NozzleTransform();
packet.Deserialize(data);
HandleNozzleTransform(packet);
HandleNozzleTransform(msg.GetPacket<Packets.NozzleTransform>());
break;
}
}
Networking.Peer.Recycle(msg);
}
#endregion
@ -220,9 +212,9 @@ namespace RageCoop.Client
Func<bool> getBulletImpact = (() =>
{
Vector3 endPos = subject.LastWeaponImpactPosition;
if (endPos==default)
if (endPos == default)
{
if (++i<=5) { return false; }
if (++i <= 5) { return false; }
endPos = subject.GetAimCoord();
if (subject.IsInVehicle() && subject.VehicleWeapon != VehicleWeaponHash.Invalid)
@ -267,7 +259,7 @@ namespace RageCoop.Client
Main.QueueAction(getBulletImpact);
}
}
else if (subject.VehicleWeapon==VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition!=default)
else if (subject.VehicleWeapon == VehicleWeaponHash.Tank && subject.LastWeaponImpactPosition != default)
{
TriggerBulletShot((uint)VehicleWeaponHash.Tank, c, subject.LastWeaponImpactPosition);
}
@ -276,7 +268,7 @@ namespace RageCoop.Client
public static void Check(SyncedVehicle v)
{
if (v.MainVehicle==null||!v.MainVehicle.HasNozzle())
if (v.MainVehicle == null || !v.MainVehicle.HasNozzle())
{
return;
}

View File

@ -1,6 +1,5 @@
using System.Threading;
using NAudio.Wave;
using NAudio.Wave;
using System.Threading;
namespace RageCoop.Client
{

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

@ -1,18 +1,19 @@

using GTA;
using GTA.Math;
using RageCoop.Core;
using SHVDN;
using System;
using System.Collections.Generic;
using GTA.Math;
using GTA;
using SHVDN;
using RageCoop.Core;
using System.Runtime.InteropServices;
namespace RageCoop.Client
{
internal unsafe class MemPatch{
private byte[] _data;
private byte[] _orginal;
private IntPtr _address;
internal unsafe class MemPatch
{
private readonly byte[] _data;
private readonly byte[] _orginal;
private readonly IntPtr _address;
public MemPatch(byte* address, byte[] data)
{
_data = data;
@ -30,7 +31,7 @@ namespace RageCoop.Client
}
}
internal static unsafe class Memory
internal static unsafe class Memory
{
public static MemPatch VignettingPatch;
public static MemPatch VignettingCallPatch;
@ -40,12 +41,12 @@ internal static unsafe class 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;
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 });
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()
@ -68,30 +69,30 @@ internal static unsafe class Memory
public const int MatrixOffset = 96;
#endregion
#region OPCODE
const byte XOR_32_64 = 0x31;
const byte RET = 0xC3;
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 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]
X = *ptr,
Y = ptr[1],
Z = ptr[2]
};
}
public static List<int> FindOffset(float toSearch,IntPtr start, int range=1000, float tolerance = 0.01f)
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)
var val = NativeMemory.ReadFloat(start + i);
if (Math.Abs(val - toSearch) < tolerance)
{
foundOffsets.Add(i);
}

View File

@ -1,152 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Math;
using GTA.Native;
using GTA.Math;
using System;
using System.Runtime.InteropServices;
using LemonUI.Elements;
using System.Drawing;
namespace RageCoop.Client
{
[StructLayout(LayoutKind.Explicit, Size = 80)]
public struct HeadBlendData
{
[FieldOffset(0)]
public int ShapeFirst;
[StructLayout(LayoutKind.Explicit, Size = 80)]
public struct HeadBlendData
{
[FieldOffset(0)]
public int ShapeFirst;
[FieldOffset(8)]
public int ShapeSecond;
[FieldOffset(8)]
public int ShapeSecond;
[FieldOffset(16)]
public int ShapeThird;
[FieldOffset(16)]
public int ShapeThird;
[FieldOffset(24)]
public int SkinFirst;
[FieldOffset(24)]
public int SkinFirst;
[FieldOffset(32)]
public int SkinSecond;
[FieldOffset(32)]
public int SkinSecond;
[FieldOffset(40)]
public int SkinThird;
[FieldOffset(40)]
public int SkinThird;
[FieldOffset(48)]
public float ShapeMix;
[FieldOffset(48)]
public float ShapeMix;
[FieldOffset(56)]
public float SkinMix;
[FieldOffset(56)]
public float SkinMix;
[FieldOffset(64)]
public float ThirdMix;
}
[FieldOffset(64)]
public float ThirdMix;
}
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct NativeVector3
{
[FieldOffset(0)]
public float X;
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct NativeVector3
{
[FieldOffset(0)]
public float X;
[FieldOffset(8)]
public float Y;
[FieldOffset(8)]
public float Y;
[FieldOffset(16)]
public float Z;
[FieldOffset(16)]
public float Z;
public static implicit operator Vector3(NativeVector3 vec)
public static implicit operator Vector3(NativeVector3 vec)
{
return new Vector3() { X=vec.X, Y=vec.Y, Z=vec.Z };
return new Vector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
}
public static implicit operator NativeVector3(Vector3 vec)
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")]
static extern void NativeInit(ulong hash);
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")]
static extern void NativePush64(ulong val);
[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")]
static extern unsafe ulong* NativeCall();
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")]
private static extern unsafe ulong* NativeCall();
// These are from ScriptHookV's nativeCaller.h
static unsafe void NativePush<T>(T val) where T : unmanaged
{
ulong val64 = 0;
*(T*)(&val64) = val;
NativePush64(val64);
}
// 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);
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());
}
return *(R*)(NativeCall());
}
/// <summary>
/// Helper function that converts an array of primitive values to a native stack.
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
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] = (ulong)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;
}
/// <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));
}
throw new ArgumentException("Unknown primitive type in native argument list", nameof(args));
}
return result;
}
}
return result;
}
}
}

View File

@ -2,8 +2,8 @@
using GTA.Math;
using GTA.Native;
using RageCoop.Core;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace RageCoop.Client
{
@ -80,9 +80,9 @@ namespace RageCoop.Client
var result = new byte[36];
for (byte i = 0; i < 12; i++)
{
result[i]=(byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
result[i+12]=(byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i);
result[i+24]=(byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
result[i] = (byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
result[i + 12] = (byte)Function.Call<short>(Hash.GET_PED_TEXTURE_VARIATION, ped.Handle, i);
result[i + 24] = (byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
}
return result;
}
@ -108,7 +108,7 @@ namespace RageCoop.Client
}
// Fake death
if (ped.IsRagdoll || (ped.Health==1 && ped.IsPlayer))
if (ped.IsRagdoll || (ped.Health == 1 && ped.IsPlayer))
{
flags |= PedDataFlags.IsRagdoll;
}
@ -144,17 +144,23 @@ namespace RageCoop.Client
flags |= PedDataFlags.IsInCover;
if (ped.IsInCoverFacingLeft)
{
flags |=PedDataFlags.IsInCover;
flags |= PedDataFlags.IsInCover;
}
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped)){
flags|=PedDataFlags.IsInLowCover;
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped))
{
flags |= PedDataFlags.IsInLowCover;
}
if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire))
{
flags|=PedDataFlags.IsBlindFiring;
flags |= PedDataFlags.IsBlindFiring;
}
}
if (ped.IsInvincible)
{
flags |= PedDataFlags.IsInvincible;
}
if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
{
flags |= PedDataFlags.IsInStealthMode;
@ -271,7 +277,7 @@ namespace RageCoop.Client
veh,
veh.Bones[text].Index
}));
bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num)&& IsSeatUsableByPed(ped, veh, dictionary[text]);
bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num) && IsSeatUsableByPed(ped, veh, dictionary[text]);
if (flag2)
{
num = num2;
@ -290,7 +296,7 @@ namespace RageCoop.Client
{
result = true;
}
else
else if (veh.GetPedOnSeat(seat) != null)
{
bool isDead = veh.GetPedOnSeat(seat).IsDead;
@ -325,29 +331,29 @@ namespace RageCoop.Client
var v = p.CurrentVehicle;
// Rhino
if (v!=null && v.Model.Hash==782665360)
if (v != null && v.Model.Hash == 782665360)
{
return v.Bones[35].Position+v.Bones[35].ForwardVector*100;
return v.Bones[35].Position + v.Bones[35].ForwardVector * 100;
}
if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); }
if (weapon!=null)
if (weapon != null)
{
// Not very accurate, but doesn't matter
Vector3 dir = weapon.RightVector;
return weapon.Position+dir*20;
return weapon.Position + dir * 20;
}
return GetLookingCoord(p);
}
public static Vector3 GetLookingCoord(this Ped p)
{
if (p==Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE)==4)
if (p == Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
{
return RaycastEverything(default);
}
EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized;
return b.Position+200*v;
return b.Position + 200 * v;
}
public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{
@ -470,7 +476,7 @@ namespace RageCoop.Client
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
case 0:
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc
|| (VehicleHash)veh.Model.Hash==VehicleHash.Dune3;
|| (VehicleHash)veh.Model.Hash == VehicleHash.Dune3;
case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2

View File

@ -6,13 +6,12 @@ using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Collections.Generic;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client
{
internal static class Util
@ -40,7 +39,7 @@ namespace RageCoop.Client
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));
screenPos = new Point((int)(res.Width * x), (int)(y * 1080));
return true;
}
}
@ -112,11 +111,11 @@ namespace RageCoop.Client
#endregion
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));
string path = SettingsPath;
Directory.CreateDirectory(Directory.GetParent(path).FullName);
Settings settings = null;
@ -124,12 +123,7 @@ namespace RageCoop.Client
{
using (FileStream stream = File.OpenRead(path))
{
settings = (RageCoop.Client.Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
settings = (Settings)ser.Deserialize(stream);
}
}
else
@ -142,22 +136,25 @@ namespace RageCoop.Client
return settings;
}
public static void SaveSettings()
public static bool SaveSettings(string path = null, Settings settings = null)
{
try
{
string path = SettingsPath;
path = path ?? SettingsPath;
settings = settings ?? Main.Settings;
Directory.CreateDirectory(Directory.GetParent(path).FullName);
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings);
ser.Serialize(stream, settings);
}
return true;
}
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);
}
}
@ -192,7 +189,7 @@ namespace RageCoop.Client
{
if (p == null) { return null; }
var c = EntityPool.GetPedByHandle(p.Handle);
if (c==null) { EntityPool.Add(c=new SyncedPed(p)); }
if (c == null) { EntityPool.Add(c = new SyncedPed(p)); }
return c;
}
@ -200,7 +197,7 @@ namespace RageCoop.Client
{
if (veh == null) { return null; }
var v = EntityPool.GetVehicleByHandle(veh.Handle);
if (v==null) { EntityPool.Add(v=new SyncedVehicle(veh)); }
if (v == null) { EntityPool.Add(v = new SyncedVehicle(veh)); }
return v;
}
@ -219,7 +216,7 @@ namespace RageCoop.Client
#region WIN32
const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYDOWN = 0x0100;
public static void Reload()
{
string reloadKey = "None";
@ -227,18 +224,19 @@ namespace RageCoop.Client
foreach (var l in lines)
{
var ss = l.Split('=');
if (ss.Length > 0 && ss[0]=="ReloadKey")
if (ss.Length > 0 && ss[0] == "ReloadKey")
{
reloadKey = ss[1];
}
}
var lineList = lines.ToList();
if (reloadKey=="None")
if (reloadKey == "None")
{
foreach (var l in lines)
{
var ss = l.Split('=');
if (ss.Length > 0 && ss[0]=="ReloadKey")
ss.ForEach(s => s.Replace(" ", ""));
if (ss.Length > 0 && ss[0] == "ReloadKey")
{
reloadKey = ss[1];
lineList.Remove(l);
@ -253,7 +251,7 @@ namespace RageCoop.Client
// Move log file so it doesn't get deleted
Main.Logger.Dispose();
var path = Main.Logger.LogPath+".last.log";
var path = Main.Logger.LogPath + ".last.log";
try
{
if (File.Exists(path)) { File.Delete(path); }
@ -268,7 +266,7 @@ namespace RageCoop.Client
}
[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")]

View File

@ -12,7 +12,7 @@ namespace RageCoop.Client
public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v)
{
var veh=v.MainVehicle;
var veh = v.MainVehicle;
VehicleDataFlags flags = 0;
if (veh.IsEngineRunning)
@ -50,51 +50,58 @@ namespace RageCoop.Client
flags |= VehicleDataFlags.IsHornActive;
}
if (v.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;
}
if (v.IsAircraft)
{
flags |= VehicleDataFlags.IsAircraft;
}
if (v.IsDeluxo && veh.IsDeluxoHovering())
{
flags|= VehicleDataFlags.IsDeluxoHovering;
}
if (v.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;
flags |= VehicleDataFlags.IsDeluxoHovering;
}
if (v.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;
}
public static bool IsRocketBoostActive(this Vehicle veh)
{
return Function.Call<bool>(Hash._IS_VEHICLE_ROCKET_BOOST_ACTIVE,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)
public static bool IsParachuteActive(this Vehicle veh)
{
Function.Call(Hash._SET_VEHICLE_ROCKET_BOOST_ACTIVE,veh,toggle);
return Function.Call<bool>((Hash)0x3DE51E9C80B116CF, veh);
}
public static void SetParachuteActive(this Vehicle veh,bool toggle)
public static void SetRocketBoostActive(this Vehicle veh, bool toggle)
{
Function.Call((Hash)0x0BFFB028B3DD0A97,veh,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)
{
@ -133,7 +140,6 @@ namespace RageCoop.Client
}
}
// Bursted tires
short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
@ -176,7 +182,7 @@ namespace RageCoop.Client
}
if ((model.OpenedDoors & (byte)(1 << i)) != 0)
{
if ((!door.IsOpen)&&(!door.IsBroken))
if ((!door.IsOpen) && (!door.IsBroken))
{
door.Open();
}
@ -220,7 +226,7 @@ namespace RageCoop.Client
{
Dictionary<int, int> ps = new Dictionary<int, int>();
var d = veh.Driver;
if (d!=null&&d.IsSittingInVehicle())
if (d != null && d.IsSittingInVehicle())
{
ps.Add(-1, d.GetSyncEntity().ID);
}
@ -228,32 +234,31 @@ namespace RageCoop.Client
{
if (p.IsSittingInVehicle())
{
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID);
ps.Add((int)p.SeatIndex, p.GetSyncEntity().ID);
}
}
return ps;
}
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)
{
return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector)-1)>0.05;
return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector) - 1) > 0.05;
}
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)
{
return v.Bones[99].Position.DistanceTo(v.Bones[92].Position)-1.43f;
return v.Bones[99].Position.DistanceTo(v.Bones[92].Position) - 1.43f;
}
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)
{
@ -283,8 +288,6 @@ namespace RageCoop.Client
{
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
}
#endregion
}
}

View File

@ -2,7 +2,6 @@
using GTA.Math;
using GTA.Native;
using System.Collections.Generic;
using System.Xml;
namespace RageCoop.Client
{
@ -11,7 +10,7 @@ namespace RageCoop.Client
public MuzzleInfo(Vector3 pos, Vector3 forward)
{
Position = pos;
ForawardVector=forward;
ForawardVector = forward;
}
public Vector3 Position;
public Vector3 ForawardVector;
@ -38,7 +37,7 @@ namespace RageCoop.Client
public static Vector3 GetMuzzlePosition(this Ped p)
{
var w = p.Weapons.CurrentWeaponObject;
if (w!=null)
if (w != null)
{
var hash = p.Weapons.Current.Hash;
if (MuzzleBoneIndexes.ContainsKey(hash)) { return w.Bones[MuzzleBoneIndexes[hash]].Position; }
@ -46,7 +45,8 @@ namespace RageCoop.Client
}
return p.Bones[Bone.SkelRightHand].Position;
}
static long BulletsShot = 0;
private static long BulletsShot = 0;
public static float GetWeaponDamage(this Ped P, uint hash)
{
@ -103,49 +103,49 @@ namespace RageCoop.Client
// ISSI6
case 1239571361:
return BulletsShot%2==0 ? 12 : 14;
return BulletsShot % 2 == 0 ? 12 : 14;
// ISSI5
case 1537277726:
return BulletsShot%2==0 ? 30 : 32;
return BulletsShot % 2 == 0 ? 30 : 32;
// ISSI4
case 628003514:
return BulletsShot%2==0 ? 14 : 12;
return BulletsShot % 2 == 0 ? 14 : 12;
// DOMINATOR6
case -1293924613:
return BulletsShot%2==0 ? 51 : 55;
return BulletsShot % 2 == 0 ? 51 : 55;
// IMPALER4
case -1744505657:
return BulletsShot%2==0 ? 64 : 63;
return BulletsShot % 2 == 0 ? 64 : 63;
// IMPERATOR3
case -755532233:
return BulletsShot%2==0 ? 86 : 88;
return BulletsShot % 2 == 0 ? 86 : 88;
// SLAMVAN6
case 1742022738:
return BulletsShot%2==0 ? 78 : 76;
return BulletsShot % 2 == 0 ? 78 : 76;
// CHAMPION
case -915234475:
return BulletsShot%2==0 ? 60 : 61;
return BulletsShot % 2 == 0 ? 60 : 61;
// MONSTER4
case 840387324:
return BulletsShot%2==0 ? 63 : 65;
return BulletsShot % 2 == 0 ? 63 : 65;
// BRUTUS2
@ -154,7 +154,7 @@ namespace RageCoop.Client
// BRUISER2
case -1694081890:
return BulletsShot%2==0 ? 45 : 51;
return BulletsShot % 2 == 0 ? 45 : 51;
// TECHNICAL3
@ -171,11 +171,11 @@ namespace RageCoop.Client
// PATRIOT3
case -670086588:
return BulletsShot%2==0 ? 87 : 89;
return BulletsShot % 2 == 0 ? 87 : 89;
// NIGHTSHARK
case 433954513:
return BulletsShot%2==0 ? 1 : 2;
return BulletsShot % 2 == 0 ? 1 : 2;
/*
// NIGHTSHARK (second)
@ -185,7 +185,7 @@ namespace RageCoop.Client
// MENACER
case 2044532910:
return BulletsShot%2==0 ? 91 : 90;
return BulletsShot % 2 == 0 ? 91 : 90;
/*
// MENACER
case 2044532910:
@ -222,11 +222,11 @@ namespace RageCoop.Client
// BLAZER5
case -1590337689:
return BulletsShot%2==0 ? 17 : 18;
return BulletsShot % 2 == 0 ? 17 : 18;
// BRUISER
case 668439077:
return BulletsShot%2==0 ? 66 : 68;
return BulletsShot % 2 == 0 ? 66 : 68;
// BRUTUS
@ -236,12 +236,12 @@ namespace RageCoop.Client
// MONSTER3
case 1721676810:
return BulletsShot%2==0 ? 53 : 55;
return BulletsShot % 2 == 0 ? 53 : 55;
// BRUISER3
case -2042350822:
return BulletsShot%2==0 ? 52 : 50;
return BulletsShot % 2 == 0 ? 52 : 50;
// BRUTUS3
case 2038858402:
@ -249,34 +249,34 @@ namespace RageCoop.Client
// MONSTER5
case -715746948:
return BulletsShot%2==0 ? 63 : 65;
return BulletsShot % 2 == 0 ? 63 : 65;
// JB7002
case 394110044:
return BulletsShot%2==0 ? 54 : 53;
return BulletsShot % 2 == 0 ? 54 : 53;
// DOMINATOR5
case -1375060657:
return BulletsShot%2==0 ? 35 : 36;
return BulletsShot % 2 == 0 ? 35 : 36;
// IMPALER3
case -1924800695:
return BulletsShot%2==0 ? 75 : 76;
return BulletsShot % 2 == 0 ? 75 : 76;
// IMPERATOR2
case 1637620610:
return BulletsShot%2==0 ? 97 : 99;
return BulletsShot % 2 == 0 ? 97 : 99;
// SLAMVAN5
case 373261600:
return BulletsShot%2==0 ? 51 : 53;
return BulletsShot % 2 == 0 ? 51 : 53;
// RUINER2
case 941494461:
return BulletsShot%2==0 ? 65 : 66;
return BulletsShot % 2 == 0 ? 65 : 66;
// TAMPA3
@ -285,52 +285,52 @@ namespace RageCoop.Client
// SCRAMJET
case -638562243:
return BulletsShot%2==0 ? 44 : 45;
return BulletsShot % 2 == 0 ? 44 : 45;
// VIGILANTE
case -1242608589:
return BulletsShot%2==0 ? 42 : 43;
return BulletsShot % 2 == 0 ? 42 : 43;
// ZR380
case 540101442:
return BulletsShot%2==0 ? 57 : 63;
return BulletsShot % 2 == 0 ? 57 : 63;
// ZR3802
case -1106120762:
return BulletsShot%2==0 ? 57 : 63;
return BulletsShot % 2 == 0 ? 57 : 63;
// ZR3803
case -1478704292:
return BulletsShot%2==0 ? 53 : 59;
return BulletsShot % 2 == 0 ? 53 : 59;
// STROMBERG
case 886810209:
return BulletsShot%2==0 ? 85 : 84;
return BulletsShot % 2 == 0 ? 85 : 84;
// SLAMVAN4
case -2061049099:
return BulletsShot%2==0 ? 76 : 78;
return BulletsShot % 2 == 0 ? 76 : 78;
// IMPERATOR
case 444994115:
return BulletsShot%2==0 ? 88 : 86;
return BulletsShot % 2 == 0 ? 88 : 86;
// IMPALER2
case 1009171724:
return BulletsShot%2==0 ? 63 : 64;
return BulletsShot % 2 == 0 ? 63 : 64;
// DOMINATOR4
case -688189648:
return BulletsShot%2==0 ? 59 : 60;
return BulletsShot % 2 == 0 ? 59 : 60;
// SAVAGE
@ -339,22 +339,22 @@ namespace RageCoop.Client
// BUZZARD
case 788747387:
return BulletsShot%2==0 ? 28 : 23;
return BulletsShot % 2 == 0 ? 28 : 23;
// ANNIHL
case 837858166:
return (int)BulletsShot%4+35;
return (int)BulletsShot % 4 + 35;
// HYDRA
case 970385471:
return BulletsShot%2==0 ? 29 : 28;
return BulletsShot % 2 == 0 ? 29 : 28;
// STARLING
case -1700874274:
return BulletsShot%2==0 ? 24 : 12;
return BulletsShot % 2 == 0 ? 24 : 12;
// RHINO
@ -362,26 +362,20 @@ namespace RageCoop.Client
return 30;
default:
return -1;
return AddOnDataProvider.GetMuzzleIndex(v.Model.Hash);
}
}
public static bool IsUsingProjectileWeapon(this Ped p)
{
var vp = p.VehicleWeapon;
var type = Function.Call<int>(Hash.GET_WEAPON_DAMAGE_TYPE, vp);
if (vp!=VehicleWeaponHash.Invalid)
if (vp != VehicleWeaponHash.Invalid)
{
if (type==3)
{
return false;
}
return VehicleProjectileWeapons.Contains(vp) || (type==5 && !ExplosiveBullets.Contains((uint)vp));
}
else
{
var w = p.Weapons.Current;
return w.Group==WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
return type == 3 ? false : VehicleProjectileWeapons.Contains(vp) || (type == 5 && !ExplosiveBullets.Contains((uint)vp));
}
var w = p.Weapons.Current;
return w.Group == WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
}
public static readonly HashSet<uint> ExplosiveBullets = new HashSet<uint>
@ -460,7 +454,6 @@ namespace RageCoop.Client
};
public static readonly HashSet<WeaponHash> ProjectileWeapons = new HashSet<WeaponHash> {
WeaponHash.HomingLauncher,
WeaponHash.RPG,

View File

@ -9,7 +9,6 @@ namespace RageCoop.Client
/// </summary>
public class WorldThread : Script
{
private static bool _lastDisableTraffic = false;
/// <summary>
/// Don't use it!
@ -19,13 +18,11 @@ namespace RageCoop.Client
Tick += OnTick;
Aborted += (sender, e) =>
{
if (_lastDisableTraffic)
{
Traffic(true);
}
ChangeTraffic(true);
};
}
private static bool _trafficEnabled;
private void OnTick(object sender, EventArgs e)
{
if (Game.IsLoading || !Networking.IsOnServer)
@ -34,28 +31,42 @@ namespace RageCoop.Client
}
Game.DisableControlThisFrame(Control.FrontendPause);
Game.DisableControlThisFrame(Control.VehicleExit);
Game.DisableControlThisFrame(Control.Enter);
if (Main.Settings.DisableAlternatePause)
{
Game.DisableControlThisFrame(Control.FrontendPauseAlternate);
}
var P = Game.Player.Character;
// 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.
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==null) { return; }
if (Main.Settings.DisableTraffic)
if (Main.Settings.ShowEntityOwnerName)
{
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_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_DENSITY_MULTIPLIER_THIS_FRAME, 0f);
@ -64,15 +75,13 @@ namespace RageCoop.Client
Function.Call(Hash.SUPPRESS_SHOCKING_EVENTS_NEXT_FRAME);
Function.Call(Hash.SUPPRESS_AGITATION_EVENTS_NEXT_FRAME);
}
else if (_lastDisableTraffic)
{
Traffic(true);
}
_lastDisableTraffic = Main.Settings.DisableTraffic;
}
public static void Traffic(bool enable)
{
ChangeTraffic(enable);
_trafficEnabled = enable;
}
private static void ChangeTraffic(bool enable)
{
if (enable)
{
@ -81,15 +90,15 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RANDOM_TRAINS, true);
Function.Call(Hash.SET_RANDOM_BOATS, true);
Function.Call(Hash.SET_GARBAGE_TRUCKS, true);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 1); // 0 - 3
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 1); // 0 - 3
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3); // 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_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, true);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, -1);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, true);
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.SET_CREATE_RANDOM_COPS, false);
@ -97,39 +106,36 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RANDOM_BOATS, false);
Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
Function.Call(Hash.DELETE_ALL_TRAINS);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
Function.Call(Hash.SET_DISTANT_CARS_ENABLED, false);
Function.Call(Hash.DISABLE_VEHICLE_DISTANTLIGHTS, true);
foreach (Ped ped in World.GetAllPeds())
{
if (ped == Game.Player.Character) { continue; }
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");
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
}
}
foreach (Vehicle veh in World.GetAllVehicles())
{
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
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");

View File

@ -1,15 +1,13 @@
using System;
using System.Text;
using System.Linq;
using GTA.Math;
using GTA.Math;
using System.IO;
using System.Text;
namespace RageCoop.Core
{
internal class BitReader:BinaryReader
internal class BitReader : BinaryReader
{
public BitReader(byte[] array):base(new MemoryStream(array))
public BitReader(byte[] array) : base(new MemoryStream(array))
{
}

View File

@ -1,20 +1,23 @@
using System;
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.Net.Http;
using System.Net.Sockets;
using System.IO;
using System.Runtime.CompilerServices;
using GTA.Math;
using Lidgren.Network;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
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.InteropServices;
using System.Security.Cryptography;
using System.Text;
[assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")]
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
namespace RageCoop.Core
{
internal static class CoreUtils
@ -31,43 +34,44 @@ namespace RageCoop.Core
{
return ToIgnore.Contains(name);
}
public static (byte, byte[]) GetBytesFromObject(object obj)
public static void GetBytesFromObject(object obj, NetOutgoingMessage m)
{
switch (obj)
{
case byte _:
return (0x01, new byte[] { (byte)obj });
case short _:
return (0x02, BitConverter.GetBytes((short)obj));
case ushort _:
return (0x03, BitConverter.GetBytes((ushort)obj));
case int _:
return (0x04, BitConverter.GetBytes((int)obj));
case uint _:
return (0x05, BitConverter.GetBytes((uint)obj));
case long _:
return (0x06, BitConverter.GetBytes((long)obj));
case ulong _:
return (0x07, BitConverter.GetBytes((ulong)obj));
case float _:
return (0x08, BitConverter.GetBytes((float)obj));
case bool _:
return (0x09, BitConverter.GetBytes((bool)obj));
case string _:
return (0x10, ((string)obj).GetBytesWithLength());
case Vector3 _:
return (0x11,((Vector3)obj).GetBytes());
case Quaternion _:
return (0x12, ((Quaternion)obj).GetBytes());
case GTA.Model _:
return (0x13, BitConverter.GetBytes((GTA.Model)obj));
case Vector2 _:
return (0x14, ((Vector2)obj).GetBytes());
case Tuple<byte, byte[]> _:
var tup = (Tuple<byte, byte[]>)obj;
return (tup.Item1, tup.Item2);
case byte value:
m.Write((byte)0x01); m.Write(value); break;
case short value:
m.Write((byte)0x02); m.Write(value); break;
case ushort value:
m.Write((byte)0x03); m.Write(value); break;
case int value:
m.Write((byte)0x04); m.Write(value); break;
case uint value:
m.Write((byte)0x05); m.Write(value); break;
case long value:
m.Write((byte)0x06); m.Write(value); break;
case ulong value:
m.Write((byte)0x07); m.Write(value); break;
case float value:
m.Write((byte)0x08); m.Write(value); break;
case bool value:
m.Write((byte)0x09); m.Write(value); break;
case string value:
m.Write((byte)0x10); m.Write(value); break;
case Vector3 value:
m.Write((byte)0x11); m.Write(value); break;
case Quaternion value:
m.Write((byte)0x12); m.Write(value); break;
case GTA.Model value:
m.Write((byte)0x13); m.Write(value); break;
case Vector2 value:
m.Write((byte)0x14); m.Write(value); break;
case byte[] value:
m.Write((byte)0x15); m.WriteByteArray(value); break;
case Tuple<byte, byte[]> value:
m.Write(value.Item1); m.Write(value.Item2); break;
default:
return (0x0, null);
throw new Exception("Unsupported object type: " + obj.GetType());
}
}
public static IPEndPoint StringToEndPoint(string endpointstring)
@ -134,9 +138,8 @@ namespace RageCoop.Core
private static int getPort(string p)
{
int port;
if (!int.TryParse(p, out port)
if (!int.TryParse(p, out int port)
|| port < IPEndPoint.MinPort
|| port > IPEndPoint.MaxPort)
{
@ -145,7 +148,7 @@ namespace RageCoop.Core
return port;
}
public static IPAddress GetLocalAddress(string target= "8.8.8.8")
public static IPAddress GetLocalAddress(string target = "8.8.8.8")
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
@ -181,10 +184,49 @@ namespace RageCoop.Core
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 struct IpInfo
internal class IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }
@ -194,74 +236,10 @@ namespace RageCoop.Core
}
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 AddBool(this List<byte> bytes, bool b)
{
bytes.Add(b? (byte)1 :(byte)0);
}
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 byte[] GetBytes(this string 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)
{
return Encoding.UTF8.GetString(data);
@ -288,35 +266,28 @@ namespace RageCoop.Core
// 16 bytes
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, T existingPacket = null) where T : Packet, new()
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
{
msg.ReadByte();
return GetPacket<T>(msg.ReadBytes(msg.ReadInt32()),existingPacket);
}
public static T GetPacket<T>(this byte[] data, T existingPacket=null) where T : Packet, new()
{
var p = existingPacket??new T();
p.Deserialize(data);
var p = new T();
p.Deserialize(msg);
return p;
}
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;
return (flagToCheck & flag) != 0;
}
public static bool HasVehFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
{
return (flagToCheck & flag)!=0;
return (flagToCheck & flag) != 0;
}
public static bool HasConfigFlag(this PlayerConfigFlags flagToCheck, PlayerConfigFlags flag)
{
return (flagToCheck & flag)!=0;
return (flagToCheck & flag) != 0;
}
public static Type GetActualType(this TypeCode code)
{
@ -383,19 +354,19 @@ namespace RageCoop.Core
public static string DumpWithType(this IEnumerable<object> objects)
{
StringBuilder sb = new StringBuilder();
foreach(var obj in objects)
foreach (var obj in objects)
{
sb.Append(obj.GetType()+":"+obj.ToString()+"\n");
sb.Append(obj.GetType() + ":" + obj.ToString() + "\n");
}
return sb.ToString();
}
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)
{
foreach(var obj in objects)
foreach (var obj in objects)
{
action(obj);
}
@ -411,10 +382,16 @@ namespace RageCoop.Core
return memoryStream.ToArray();
}
}
public static byte[] Join(this List<byte[]> arrays,int lengthPerArray=-1)
public static MemoryStream ToMemStream(this Stream stream)
{
if (arrays.Count==1) { return arrays[0]; }
var output = lengthPerArray== -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count*lengthPerArray];
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream;
}
public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1)
{
if (arrays.Count == 1) { return arrays[0]; }
var output = lengthPerArray == -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count * lengthPerArray];
int writeIdx = 0;
foreach (var byteArr in arrays)
{
@ -468,5 +445,6 @@ namespace RageCoop.Core
{
return IPAddress.Parse(ip);
}
}
}

View File

@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
namespace RageCoop.Core
@ -32,34 +31,34 @@ namespace RageCoop.Core
private StreamWriter logWriter;
private string Buffer = "";
private Thread LoggerThread;
private readonly Thread LoggerThread;
private bool Stopping = false;
private bool FlushImmediately;
private readonly bool FlushImmediately;
internal Logger(bool flushImmediately = false, bool overwrite = true)
{
FlushImmediately = flushImmediately;
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
Name=Process.GetCurrentProcess().Id.ToString();
if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
Name = Process.GetCurrentProcess().Id.ToString();
if (!flushImmediately)
{
LoggerThread=new Thread(() =>
{
if (!UseConsole)
{
while (LogPath==default)
{
Thread.Sleep(100);
}
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
}
while (!Stopping)
{
Flush();
Thread.Sleep(1000);
}
Flush();
});
LoggerThread = new Thread(() =>
{
if (!UseConsole)
{
while (LogPath == default)
{
Thread.Sleep(100);
}
if (File.Exists(LogPath) && overwrite) { File.Delete(LogPath); }
}
while (!Stopping)
{
Flush();
Thread.Sleep(1000);
}
Flush();
});
LoggerThread.Start();
}
}
@ -69,12 +68,12 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Info(string message)
{
if (LogLevel>2) { return; }
if (LogLevel > 2) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [INF] {1}", Date(), message, Name);
Buffer+=msg+"\r\n";
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
@ -87,12 +86,12 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Warning(string message)
{
if (LogLevel>3) { return; }
if (LogLevel > 3) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [WRN] {1}", Date(), message, Name);
Buffer+=msg+"\r\n";
Buffer += msg + "\r\n";
}
if (FlushImmediately)
@ -106,12 +105,12 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Error(string message)
{
if (LogLevel>4) { return; }
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), message, Name);
Buffer+=msg+"\r\n";
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
@ -125,11 +124,11 @@ namespace RageCoop.Core
/// <param name="error"></param>
public void Error(string message, Exception error)
{
if (LogLevel>4) { return; }
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name,error.Message);
Buffer+=msg+"\r\n";
string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name, error.Message);
Buffer += msg + "\r\n";
Trace(error.ToString());
}
@ -144,11 +143,11 @@ namespace RageCoop.Core
/// <param name="ex"></param>
public void Error(Exception ex)
{
if (LogLevel>4) { return; }
if (LogLevel > 4) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.Message, Name);
Buffer+=msg+"\r\n";
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n" + ex.Message, Name);
Buffer += msg + "\r\n";
Trace(ex.ToString());
}
if (FlushImmediately)
@ -163,12 +162,12 @@ namespace RageCoop.Core
public void Debug(string message)
{
if (LogLevel>1) { return; }
if (LogLevel > 1) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [DBG] {1}", Date(), message, Name);
Buffer+=msg+"\r\n";
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
@ -181,12 +180,12 @@ namespace RageCoop.Core
/// <param name="message"></param>
public void Trace(string message)
{
if (LogLevel>0) { return; }
if (LogLevel > 0) { return; }
lock (Buffer)
{
string msg = string.Format("[{0}][{2}] [TRC] {1}", Date(), message, Name);
Buffer+=msg+"\r\n";
Buffer += msg + "\r\n";
}
if (FlushImmediately)
{
@ -205,21 +204,21 @@ namespace RageCoop.Core
{
lock (Buffer)
{
if (Buffer!="")
if (Buffer != "")
{
if (UseConsole)
{
Console.Write(Buffer);
Buffer="";
Buffer = "";
}
else
{
try
{
logWriter=new StreamWriter(LogPath, true, Encoding.UTF8);
logWriter = new StreamWriter(LogPath, true, Encoding.UTF8);
logWriter.Write(Buffer);
logWriter.Close();
Buffer="";
Buffer = "";
}
catch { }
}
@ -232,7 +231,7 @@ namespace RageCoop.Core
/// </summary>
public void Dispose()
{
Stopping=true;
Stopping = true;
LoggerThread?.Join();
}
}

View File

@ -1,12 +1,11 @@
using System;
using GTA.Math;
using GTA.Math;
using System;
namespace RageCoop.Core
{
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 static Vector3 ToDirection(this Vector3 rotation)
@ -82,14 +81,14 @@ namespace RageCoop.Core
vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2);
float sinRollOver2 = (float)Math.Sin(rollOver2);
float cosRollOver2 = (float)Math.Cos(rollOver2);
float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2);
float sinPitchOver2 = (float)Math.Sin(pitchOver2);
float cosPitchOver2 = (float)Math.Cos(pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin((double)yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2);
float sinYawOver2 = (float)Math.Sin(yawOver2);
float cosYawOver2 = (float)Math.Cos(yawOver2);
Quaternion result = new Quaternion()
{
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
@ -111,7 +110,7 @@ namespace RageCoop.Core
}
public static Vector3 ToDegree(this Vector3 radian)
{
return radian*(float)(180/Math.PI);
return radian * (float)(180 / Math.PI);
}
public static Vector3 ToEulerDegrees(this Quaternion q)
{
@ -146,16 +145,12 @@ namespace RageCoop.Core
}
private static float CopySign(double x, double y)
{
bool isPositive = y>=0;
if (isPositive)
if (y >= 0)
{
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)
{
@ -163,8 +158,7 @@ namespace RageCoop.Core
}
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

@ -1,7 +1,6 @@
using System;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading;
namespace RageCoop.Core
@ -10,22 +9,22 @@ namespace RageCoop.Core
{
public EventHandler<NetIncomingMessage> OnMessageReceived;
private readonly Thread ListenerThread;
private bool _stopping=false;
public CoopPeer(NetPeerConfiguration config):base(config)
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 = new Thread(() =>
{
while (!_stopping)
{
msg = WaitMessage(200);
if (msg != null)
{
OnMessageReceived?.Invoke(this, msg);
}
}
});
ListenerThread.Start();
}
@ -34,7 +33,7 @@ namespace RageCoop.Core
/// </summary>
public void Dispose()
{
_stopping=true;
_stopping = true;
Shutdown("Bye!");
ListenerThread.Join();
}
@ -51,12 +50,11 @@ namespace RageCoop.Core
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)
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

@ -1,19 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading;
namespace RageCoop.Core
{
internal class PublicKey{
public PublicKey(){
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 static PublicKey FromServerInfo(ServerInfo info)
{
return new PublicKey
{
Modulus = Convert.FromBase64String(info.publicKeyModulus),
Exponent = Convert.FromBase64String(info.publicKeyExponent)
};
}
public byte[] Modulus;

View File

@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace RageCoop.Core
namespace RageCoop.Core
{
internal class ServerInfo
/// <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; }
@ -27,8 +25,8 @@ namespace RageCoop.Core
public string ztID { get; set; }
public string ztAddress { get; set; }
public string publicKeyModulus{get;set;}
public string publicKeyExponent{get;set;}
public string publicKeyModulus { get; set; }
public string publicKeyExponent { get; set; }
}
}

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.Net;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
namespace RageCoop.Core
@ -15,13 +13,13 @@ namespace RageCoop.Core
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];
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]);
@ -33,43 +31,43 @@ namespace RageCoop.Core
public string Status;
public string Type;
public string Device;
public List<string> Addresses=new List<string>();
public List<string> Addresses = new List<string>();
}
internal static class ZeroTierHelper
{
private static readonly string _path="zerotier-cli";
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 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);
throw new Exception("ZeroTier not ready: " + status);
}
}
public static ZeroTierNetwork Join(string networkId, int timeout=10000)
public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
{
var p = Run("join "+networkId);
var p = Run("join " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK"))
{
throw new Exception(o+p.StandardError.ReadToEnd());
throw new Exception(o + p.StandardError.ReadToEnd());
}
if (timeout==0) { return Networks[networkId]; }
if (timeout == 0) { return Networks[networkId]; }
int i = 0;
while (i<=timeout)
while (i <= timeout)
{
i+=100;
if(Networks.TryGetValue(networkId,out var n))
i += 100;
if (Networks.TryGetValue(networkId, out var n))
{
if (n.Addresses.Count!=0 && (!n.Addresses.Where(x=>x=="-").Any()))
if (n.Addresses.Count != 0 && (!n.Addresses.Where(x => x == "-").Any()))
{
return n;
}
@ -84,16 +82,17 @@ namespace RageCoop.Core
}
public static void Leave(string networkId)
{
var p = Run("leave "+networkId);
var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK"))
{
throw new Exception(o+p.StandardError.ReadToEnd());
throw new Exception(o + p.StandardError.ReadToEnd());
}
}
public static Dictionary<string, ZeroTierNetwork> Networks
{
get {
get
{
Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>();
var p = Run("listnetworks");
var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
@ -113,14 +112,14 @@ namespace RageCoop.Core
private static Process Run(string args)
{
var p = new Process();
p.StartInfo=new ProcessStartInfo()
p.StartInfo = new ProcessStartInfo()
{
FileName = _path,
Arguments =_arg+args,
Arguments = _arg + args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow=true,
CreateNoWindow = true,
};
p.Start();
p.WaitForExit();
@ -129,7 +128,11 @@ namespace RageCoop.Core
private static string RunCommand(string command)
{
var p = Run(command);
return p.StandardOutput.ReadToEnd()+p.StandardError.ReadToEnd();
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

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Lidgren.Network;
using Lidgren.Network;
using System;
namespace RageCoop.Core
{
@ -10,9 +9,9 @@ namespace RageCoop.Core
internal class ChatMessage : Packet
{
public override PacketType Type => PacketType.ChatMessage;
private Func<string, byte[]> crypt;
private Func<byte[], byte[]> decrypt;
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;
@ -25,33 +24,31 @@ namespace RageCoop.Core
public string Message { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// Write Username
byteArray.AddString(Username);
m.Write(Username);
// Write Message
byteArray.AddArray(crypt(Message));
return byteArray.ToArray();
m.WriteByteArray(crypt(Message));
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read username
Username = reader.ReadString();
Username = m.ReadString();
Message = decrypt(reader.ReadByteArray()).GetString();
Message = decrypt(m.ReadByteArray()).GetString();
#endregion
}
}

View File

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

View File

@ -1,18 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;

using Lidgren.Network;
namespace RageCoop.Core
{
internal enum FileResponse:byte
internal enum FileResponse : byte
{
NeedToDownload=0,
AlreadyExists=1,
Completed=2,
Loaded=3,
LoadFailed=4,
NeedToDownload = 0,
AlreadyExists = 1,
Completed = 2,
Loaded = 3,
LoadFailed = 4,
}
internal partial class Packets
{
@ -25,34 +22,30 @@ namespace RageCoop.Core
public long FileLength { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.AddInt(ID);
m.Write(ID);
// The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
m.Write(Name);
// The length of the file
byteArray.AddRange(BitConverter.GetBytes(FileLength));
return byteArray.ToArray();
m.Write(FileLength);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
Name = reader.ReadString();
FileLength = reader.ReadInt64();
ID = m.ReadInt32();
Name = m.ReadString();
FileLength = m.ReadInt64();
}
}
@ -61,86 +54,74 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.FileTransferResponse;
public int ID { get; set; }
public FileResponse Response { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.AddInt(ID);
m.Write(ID);
byteArray.Add((byte)Response);
m.Write((byte)Response);
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
Response = (FileResponse)reader.ReadByte();
ID = m.ReadInt32();
Response = (FileResponse)m.ReadByte();
}
}
internal class FileTransferChunk : Packet
{
public override PacketType Type => PacketType.FileTransferChunk;
public override PacketType Type => PacketType.FileTransferChunk;
public int ID { get; set; }
public byte[] FileChunk { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.AddInt(ID);
// The chunk of the file
byteArray.AddInt(FileChunk.Length);
byteArray.AddRange(FileChunk);
return byteArray.ToArray();
m.Write(ID);
m.WriteByteArray(FileChunk);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
FileChunk = reader.ReadByteArray();
ID = m.ReadInt32();
FileChunk = m.ReadByteArray();
}
}
internal class FileTransferComplete : Packet
{
public override PacketType Type => PacketType.FileTransferComplete;
public override PacketType Type => PacketType.FileTransferComplete;
public int ID { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// The ID for the download
byteArray.AddInt(ID);
return byteArray.ToArray();
m.Write(ID);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
ID = m.ReadInt32();
}
}
internal class AllResourcesSent : Packet
{
public override PacketType Type => PacketType.AllResourcesSent;
public override PacketType Type => PacketType.AllResourcesSent;
}
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using Lidgren.Network;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -14,26 +12,26 @@ namespace RageCoop.Core
public string TargetInternal { get; set; }
public string TargetExternal { get; set; }
public bool Connect { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(TargetID);
byteArray.AddString(TargetInternal);
byteArray.AddString(TargetExternal);
byteArray.AddBool(Connect);
return byteArray.ToArray();
m.Write(TargetID);
m.Write(TargetInternal);
m.Write(TargetExternal);
m.Write(Connect);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
TargetID = reader.ReadInt32();
TargetInternal = reader.ReadString();
TargetExternal = reader.ReadString();
Connect=reader.ReadBoolean();
TargetID = m.ReadInt32();
TargetInternal = m.ReadString();
TargetExternal = m.ReadString();
Connect = m.ReadBoolean();
#endregion
}
}
@ -45,23 +43,23 @@ namespace RageCoop.Core
/// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed
/// </summary>
public byte Status { get;set;}
public override byte[] Serialize()
public byte Status { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(Puncher);
byteArray.Add(Status);
return byteArray.ToArray();
m.Write(Puncher);
m.Write(Status);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
Puncher = reader.ReadInt32();
Status = reader.ReadByte();
Puncher = m.ReadInt32();
Status = m.ReadByte();
#endregion
}
}

View File

@ -1,4 +1,4 @@
using System;
using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core
@ -12,16 +12,15 @@ namespace RageCoop.Core
{
public int TargetID { get; set; }
public override PacketType Type => PacketType.ConnectionRequest;
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
var data=new List<byte>(10);
data.AddInt(TargetID);
return data.ToArray();
var data = new List<byte>(10);
m.Write(TargetID);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
var reader=new BitReader(array);
TargetID = reader.ReadInt32();
TargetID = m.ReadInt32();
}
}
@ -33,16 +32,16 @@ namespace RageCoop.Core
{
public int ID { get; set; }
public override PacketType Type => PacketType.P2PConnect;
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>(10);
data.AddInt(ID);
return data.ToArray();
m.Write(ID);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
var reader = new BitReader(array);
ID = reader.ReadInt32();
ID = m.ReadInt32();
}
}
}

View File

@ -1,68 +1,56 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using Newtonsoft.Json;
using GTA.Math;
using Lidgren.Network;
using System;
namespace RageCoop.Core
{
internal enum PacketType:byte
internal enum PacketType : byte
{
Handshake=0,
PlayerConnect=1,
PlayerDisconnect=2,
PlayerInfoUpdate=3,
PublicKeyRequest=4,
PublicKeyResponse=5,
Request=6,
Response=7,
Handshake = 0,
PlayerConnect = 1,
PlayerDisconnect = 2,
PlayerInfoUpdate = 3,
PublicKeyRequest = 4,
PublicKeyResponse = 5,
Request = 6,
Response = 7,
PingPong = 8,
HandshakeSuccess = 9,
ChatMessage =10,
ChatMessage = 10,
FileTransferChunk=11,
FileTransferRequest=12,
FileTransferChunk = 11,
FileTransferRequest = 12,
FileTransferResponse = 13,
FileTransferComplete =14,
AllResourcesSent=15,
FileTransferComplete = 14,
AllResourcesSent = 15,
CustomEvent = 16,
CustomEventQueued = 17,
ConnectionRequest=18,
ConnectionRequest = 18,
P2PConnect = 19,
HolePunchInit=20,
HolePunch=21,
HolePunchInit = 20,
HolePunch = 21,
Voice = 22,
#region Sync
PedSync = 23,
VehicleSync = 24,
ProjectileSync =25,
ProjectileSync = 25,
#endregion
#region EVENT
PedKilled=30,
BulletShot=31,
PedKilled = 30,
BulletShot = 31,
VehicleBulletShot = 32,
OwnerChanged =35,
NozzleTransform=37,
OwnerChanged = 35,
NozzleTransform = 37,
#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
{
Default = 0,
@ -72,18 +60,18 @@ namespace RageCoop.Core
Mod = 4,
File = 5,
Event = 6,
RequestResponse=7,
RequestResponse = 7,
PingPong = 8,
VehicleSync = 9,
PedSync= 10,
PedSync = 10,
ProjectileSync = 11,
SyncEvents = 12,
}
[Flags]
internal enum PedDataFlags:ushort
internal enum PedDataFlags : ushort
{
None=0,
None = 0,
IsAiming = 1 << 0,
IsInStealthMode = 1 << 1,
IsReloading = 1 << 2,
@ -98,10 +86,11 @@ namespace RageCoop.Core
IsInLowCover = 1 << 11,
IsInCoverFacingLeft = 1 << 12,
IsBlindFiring = 1 << 13,
IsFullSync = 1<<15 ,
IsInvincible = 1 << 14,
IsFullSync = 1 << 15,
}
internal enum ProjectileDataFlags:byte
internal enum ProjectileDataFlags : byte
{
None = 0,
Exploded = 1 << 0,
@ -110,9 +99,9 @@ namespace RageCoop.Core
IsShotByVehicle = 1 << 3,
}
#region ===== VEHICLE DATA =====
internal enum VehicleDataFlags:ushort
internal enum VehicleDataFlags : ushort
{
None=0,
None = 0,
IsEngineRunning = 1 << 0,
AreLightsOn = 1 << 1,
AreBrakeLightsOn = 1 << 2,
@ -124,18 +113,18 @@ namespace RageCoop.Core
IsParachuteActive = 1 << 8,
IsRocketBoostActive = 1 << 9,
IsAircraft = 1 << 10,
IsDeluxoHovering=1 << 11,
HasRoof=1 << 12,
IsFullSync = 1<<13,
IsOnFire = 1<<14,
Repaired = 1<<15,
IsDeluxoHovering = 1 << 11,
HasRoof = 1 << 12,
IsFullSync = 1 << 13,
IsOnFire = 1 << 14,
Repaired = 1 << 15,
}
internal enum PlayerConfigFlags : byte
{
None = 0,
ShowBlip= 1 << 0,
ShowNameTag= 1 << 1
ShowBlip = 1 << 0,
ShowNameTag = 1 << 1
}
internal struct VehicleDamageModel
@ -152,25 +141,19 @@ namespace RageCoop.Core
internal interface IPacket
{
PacketType Type { get; }
byte[] Serialize();
void Deserialize(byte[] data);
void Deserialize(NetIncomingMessage m);
}
internal abstract class Packet : IPacket
{
public abstract PacketType Type { get; }
public virtual byte[] Serialize()
public void Pack(NetOutgoingMessage m)
{
return new byte[0];
}
public virtual void Deserialize(byte[] array) { }
public void Pack(NetOutgoingMessage message)
{
var d=Serialize();
message.Write((byte)Type);
message.Write(d.Length);
message.Write(d);
m.Write((byte)Type);
Serialize(m);
}
protected virtual void Serialize(NetOutgoingMessage m) { }
public virtual void Deserialize(NetIncomingMessage m) { }
}
}

View File

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

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using GTA.Math;
using Lidgren.Network;
using System.Net;
namespace RageCoop.Core
@ -15,7 +13,7 @@ namespace RageCoop.Core
}
public class Handshake : Packet
{
public override PacketType Type => PacketType.Handshake;
public override PacketType Type => PacketType.Handshake;
public int PedID { get; set; }
public string Username { get; set; }
@ -38,62 +36,55 @@ namespace RageCoop.Core
public byte[] PasswordEncrypted { get; set; }
public IPEndPoint InternalEndPoint { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// Write Player Ped ID
byteArray.AddRange(BitConverter.GetBytes(PedID));
m.Write(PedID);
// Write Username
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
byteArray.AddRange(usernameBytes);
m.Write(Username);
// Write ModVersion
byte[] modVersionBytes = Encoding.UTF8.GetBytes(ModVersion);
byteArray.AddRange(BitConverter.GetBytes(modVersionBytes.Length));
byteArray.AddRange(modVersionBytes);
m.Write(ModVersion);
byteArray.AddString(InternalEndPoint.ToString());
m.Write(InternalEndPoint.ToString());
// Write AesKeyCrypted
byteArray.AddArray(AesKeyCrypted);
m.WriteByteArray(AesKeyCrypted);
// Write AesIVCrypted
byteArray.AddArray(AesIVCrypted);
m.WriteByteArray(AesIVCrypted);
// Write PassHash
byteArray.AddArray(PasswordEncrypted);
m.WriteByteArray(PasswordEncrypted);
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle
PedID = reader.ReadInt32();
PedID = m.ReadInt32();
// Read Username
Username = reader.ReadString();
Username = m.ReadString();
// Read ModVersion
ModVersion = reader.ReadString();
ModVersion = m.ReadString();
InternalEndPoint=CoreUtils.StringToEndPoint(reader.ReadString());
InternalEndPoint = CoreUtils.StringToEndPoint(m.ReadString());
AesKeyCrypted=reader.ReadByteArray();
AesKeyCrypted = m.ReadByteArray();
AesIVCrypted=reader.ReadByteArray();
AesIVCrypted = m.ReadByteArray();
PasswordEncrypted=reader.ReadByteArray();
PasswordEncrypted = m.ReadByteArray();
#endregion
}
}
@ -101,99 +92,81 @@ namespace RageCoop.Core
{
public PlayerData[] Players { get; set; }
public override PacketType Type => PacketType.HandshakeSuccess;
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>();
data.AddInt(Players.Length);
foreach(var p in Players)
m.Write(Players.Length);
foreach (var p in Players)
{
data.AddInt(p.ID);
data.AddString(p.Username);
m.Write(p.ID);
m.Write(p.Username);
}
return data.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
var reader = new BitReader(array);
Players=new PlayerData[reader.ReadInt32()];
for(int i = 0; i<Players.Length; i++)
Players = new PlayerData[m.ReadInt32()];
for (int i = 0; i < Players.Length; i++)
{
Players[i]=new PlayerData()
Players[i] = new PlayerData()
{
ID=reader.ReadInt32(),
Username=reader.ReadString(),
ID = m.ReadInt32(),
Username = m.ReadString(),
};
}
}
}
public class PlayerConnect : Packet
{
public override PacketType Type => PacketType.PlayerConnect;
public override PacketType Type => PacketType.PlayerConnect;
public int PedID { get; set; }
public string Username { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// Write NetHandle
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
byteArray.AddRange(usernameBytes);
return byteArray.ToArray();
m.Write(Username);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read player netHandle
PedID = reader.ReadInt32();
PedID = m.ReadInt32();
// Read Username
Username = reader.ReadString();
Username = m.ReadString();
#endregion
}
}
public class PlayerDisconnect : Packet
{
public override PacketType Type => PacketType.PlayerDisconnect;
public override PacketType Type => PacketType.PlayerDisconnect;
public int PedID { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
m.Write(PedID);
byteArray.AddRange(BitConverter.GetBytes(PedID));
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
PedID = reader.ReadInt32();
PedID = m.ReadInt32();
#endregion
}
}
public class PlayerInfoUpdate : Packet
{
public override PacketType Type => PacketType.PlayerInfoUpdate;
public override PacketType Type => PacketType.PlayerInfoUpdate;
/// <summary>
/// Ped ID for this Player
@ -202,66 +175,67 @@ namespace RageCoop.Core
public string Username { get; set; }
public float Latency { get; set; }
public Vector3 Position { get; set; }
public override byte[] Serialize()
public bool IsHost;
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// Write ID
byteArray.AddRange(BitConverter.GetBytes(PedID));
m.Write(PedID);
// Write Username
byteArray.AddString(Username);
m.Write(Username);
// Write Latency
byteArray.AddFloat(Latency);
m.Write(Latency);
byteArray.AddVector3(Position);
m.Write(Position);
return byteArray.ToArray();
m.Write(IsHost);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
BitReader reader = new BitReader(array);
// Read player ID
PedID = reader.ReadInt32();
PedID = m.ReadInt32();
// Read Username
Username = reader.ReadString();
Username = m.ReadString();
Latency=reader.ReadSingle();
Latency = m.ReadFloat();
Position=reader.ReadVector3();
Position = m.ReadVector3();
IsHost = m.ReadBoolean();
}
}
public class PublicKeyResponse : Packet
{
public override PacketType Type => PacketType.PublicKeyResponse;
public override PacketType Type => PacketType.PublicKeyResponse;
public byte[] Modulus;
public byte[] Exponent;
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddArray(Modulus);
byteArray.AddArray(Exponent);
return byteArray.ToArray();
m.WriteByteArray(Modulus);
m.WriteByteArray(Exponent);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
var reader=new BitReader(array);
Modulus=reader.ReadByteArray();
Exponent=reader.ReadByteArray();
Modulus = m.ReadByteArray();
Exponent = m.ReadByteArray();
#endregion
}
@ -269,7 +243,7 @@ namespace RageCoop.Core
public class PublicKeyRequest : Packet
{
public override PacketType Type => PacketType.PublicKeyRequest;
public override PacketType Type => PacketType.PublicKeyRequest;
}
}
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
@ -10,7 +7,7 @@ namespace RageCoop.Core
{
internal class ProjectileSync : Packet
{
public override PacketType Type => PacketType.ProjectileSync;
public override PacketType Type => PacketType.ProjectileSync;
public int ID { get; set; }
public int ShooterID { get; set; }
@ -26,57 +23,57 @@ namespace RageCoop.Core
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
// Write id
byteArray.AddInt(ID);
m.Write(ID);
// Write ShooterID
byteArray.AddInt(ShooterID);
m.Write(ShooterID);
byteArray.AddUint(WeaponHash);
m.Write(WeaponHash);
// Write position
byteArray.AddVector3(Position);
m.Write(Position);
// Write rotation
byteArray.AddVector3(Rotation);
m.Write(Rotation);
// Write velocity
byteArray.AddVector3(Velocity);
byteArray.Add((byte)Flags);
m.Write(Velocity);
m.Write((byte)Flags);
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read id
ID = reader.ReadInt32();
ID = m.ReadInt32();
// Read ShooterID
ShooterID= reader.ReadInt32();
ShooterID = m.ReadInt32();
WeaponHash= reader.ReadUInt32();
WeaponHash = m.ReadUInt32();
// Read position
Position = reader.ReadVector3();
Position = m.ReadVector3();
// Read rotation
Rotation = reader.ReadVector3();
Rotation = m.ReadVector3();
// Read velocity
Velocity =reader.ReadVector3();
Velocity = m.ReadVector3();
Flags=(ProjectileDataFlags)reader.ReadByte();
Flags = (ProjectileDataFlags)m.ReadByte();
#endregion
}

View File

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

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;

using Lidgren.Network;
namespace RageCoop.Core
@ -10,29 +7,29 @@ namespace RageCoop.Core
{
internal class NozzleTransform : Packet
{
public override PacketType Type => PacketType.NozzleTransform;
public override PacketType Type => PacketType.NozzleTransform;
public int VehicleID { get; set; }
public bool Hover { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VehicleID);
byteArray.AddBool(Hover);
return byteArray.ToArray();
m.Write(VehicleID);
m.Write(Hover);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VehicleID=reader.ReadInt32();
Hover=reader.ReadBoolean();
VehicleID = m.ReadInt32();
Hover = m.ReadBoolean();
#endregion
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;

using Lidgren.Network;
namespace RageCoop.Core
@ -11,29 +8,24 @@ namespace RageCoop.Core
internal class OwnerChanged : Packet
{
public override PacketType Type => PacketType.OwnerChanged;
public override PacketType Type => PacketType.OwnerChanged;
public int ID { get; set; }
public int NewOwnerID { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(ID);
byteArray.AddInt(NewOwnerID);
return byteArray.ToArray();
m.Write(ID);
m.Write(NewOwnerID);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID=reader.ReadInt32();
NewOwnerID=reader.ReadInt32();
ID = m.ReadInt32();
NewOwnerID = m.ReadInt32();
#endregion
}

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;

using Lidgren.Network;
namespace RageCoop.Core
@ -11,25 +8,25 @@ namespace RageCoop.Core
internal class PedKilled : Packet
{
public override PacketType Type => PacketType.PedKilled;
public override PacketType Type => PacketType.PedKilled;
public int VictimID { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VictimID);
return byteArray.ToArray();
m.Write(VictimID);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VictimID=reader.ReadInt32();
VictimID = m.ReadInt32();
#endregion
}

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -18,31 +16,31 @@ namespace RageCoop.Core
public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; }
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(OwnerID);
byteArray.AddUshort(Bone);
byteArray.AddUint(WeaponHash);
byteArray.AddVector3(StartPosition);
byteArray.AddVector3(EndPosition);
return byteArray.ToArray();
m.Write(OwnerID);
m.Write(Bone);
m.Write(WeaponHash);
m.Write(StartPosition);
m.Write(EndPosition);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
OwnerID=reader.ReadInt32();
Bone=reader.ReadUInt16();
WeaponHash=reader.ReadUInt32();
StartPosition=reader.ReadVector3();
EndPosition=reader.ReadVector3();
OwnerID = m.ReadInt32();
Bone = m.ReadUInt16();
WeaponHash = m.ReadUInt32();
StartPosition = m.ReadVector3();
EndPosition = m.ReadVector3();
#endregion
}
}

View File

@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA;
using GTA;
using GTA.Math;
using Lidgren.Network;
using System.Linq;
using System.Collections.Generic;
namespace RageCoop.Core
{
@ -13,7 +10,7 @@ namespace RageCoop.Core
public class VehicleSync : Packet
{
public override PacketType Type => PacketType.VehicleSync;
public override PacketType Type => PacketType.VehicleSync;
public int ID { get; set; }
public int OwnerID { get; set; }
@ -58,200 +55,170 @@ namespace RageCoop.Core
public string LicensePlate { get; set; }
#endregion
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
List<byte> byteArray = new List<byte>(100);
byteArray.AddInt(ID);
byteArray.AddInt(OwnerID);
byteArray.AddUshort((ushort)Flags);
byteArray.AddVector3(Position);
byteArray.AddQuaternion(Quaternion);
byteArray.AddVector3(Velocity);
byteArray.AddVector3(RotationVelocity);
byteArray.AddFloat(ThrottlePower);
byteArray.AddFloat(BrakePower);
byteArray.AddFloat(SteeringAngle);
m.Write(ID);
m.Write(OwnerID);
m.Write((ushort)Flags);
m.Write(Position);
m.Write(Quaternion);
m.Write(Velocity);
m.Write(RotationVelocity);
m.Write(ThrottlePower);
m.Write(BrakePower);
m.Write(SteeringAngle);
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{
byteArray.AddFloat(DeluxoWingRatio);
m.Write(DeluxoWingRatio);
}
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{
byteArray.AddInt(ModelHash);
byteArray.AddFloat(EngineHealth);
m.Write(ModelHash);
m.Write(EngineHealth);
// Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{
// Write the vehicle landing gear
byteArray.Add(LandingGear);
m.Write(LandingGear);
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{
byteArray.Add(RoofState);
m.Write(RoofState);
}
// Write vehicle colors
byteArray.Add(Colors[0]);
byteArray.Add(Colors[1]);
m.Write(Colors[0]);
m.Write(Colors[1]);
// Write vehicle mods
// Write the count of mods
byteArray.AddRange(BitConverter.GetBytes((short)Mods.Count));
m.Write((short)Mods.Count);
// Loop the dictionary and add the values
foreach (KeyValuePair<int, int> mod in Mods)
{
// Write the mod value
byteArray.AddRange(BitConverter.GetBytes(mod.Key));
byteArray.AddRange(BitConverter.GetBytes(mod.Value));
m.Write(mod.Key);
m.Write(mod.Value);
}
if (!DamageModel.Equals(default(VehicleDamageModel)))
{
// Write boolean = true
byteArray.Add(0x01);
m.Write(true);
// Write vehicle damage model
byteArray.Add(DamageModel.BrokenDoors);
byteArray.Add(DamageModel.OpenedDoors);
byteArray.Add(DamageModel.BrokenWindows);
byteArray.AddRange(BitConverter.GetBytes(DamageModel.BurstedTires));
byteArray.Add(DamageModel.LeftHeadLightBroken);
byteArray.Add(DamageModel.RightHeadLightBroken);
m.Write(DamageModel.BrokenDoors);
m.Write(DamageModel.OpenedDoors);
m.Write(DamageModel.BrokenWindows);
m.Write(DamageModel.BurstedTires);
m.Write(DamageModel.LeftHeadLightBroken);
m.Write(DamageModel.RightHeadLightBroken);
}
else
{
// Write boolean = false
byteArray.Add(0x00);
m.Write(false);
}
// Write LockStatus
byteArray.Add((byte)LockStatus);
m.Write((byte)LockStatus);
// Write RadioStation
byteArray.Add(RadioStation);
m.Write(RadioStation);
// Write LicensePlate
while (LicensePlate.Length<8)
{
LicensePlate+=" ";
}
if (LicensePlate.Length>8)
{
LicensePlate=new string(LicensePlate.Take(8).ToArray());
}
byteArray.AddRange(Encoding.ASCII.GetBytes(LicensePlate));
m.Write(LicensePlate);
byteArray.Add((byte)(Livery+1));
m.Write((byte)(Livery + 1));
}
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read vehicle id
ID = reader.ReadInt32();
OwnerID = reader.ReadInt32();
Flags=(VehicleDataFlags)reader.ReadUInt16();
// Read position
Position = reader.ReadVector3();
// Read quaternion
Quaternion=reader.ReadQuaternion();
// Read velocity
Velocity =reader.ReadVector3();
// Read rotation velocity
RotationVelocity=reader.ReadVector3();
// Read throttle power
ThrottlePower=reader.ReadSingle();
// Read brake power
BrakePower=reader.ReadSingle();
// Read steering angle
SteeringAngle = reader.ReadSingle();
ID = m.ReadInt32();
OwnerID = m.ReadInt32();
Flags = (VehicleDataFlags)m.ReadUInt16();
Position = m.ReadVector3();
Quaternion = m.ReadQuaternion();
Velocity = m.ReadVector3();
RotationVelocity = m.ReadVector3();
ThrottlePower = m.ReadFloat();
BrakePower = m.ReadFloat();
SteeringAngle = m.ReadFloat();
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{
DeluxoWingRatio = reader.ReadSingle();
DeluxoWingRatio = m.ReadFloat();
}
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{
// Read vehicle model hash
ModelHash = reader.ReadInt32();
ModelHash = m.ReadInt32();
// Read vehicle engine health
EngineHealth = reader.ReadSingle();
EngineHealth = m.ReadFloat();
// Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{
// Read vehicle landing gear
LandingGear = reader.ReadByte();
LandingGear = m.ReadByte();
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{
RoofState=reader.ReadByte();
RoofState = m.ReadByte();
}
// Read vehicle colors
byte vehColor1 = reader.ReadByte();
byte vehColor2 = reader.ReadByte();
byte vehColor1 = m.ReadByte();
byte vehColor2 = m.ReadByte();
Colors = new byte[] { vehColor1, vehColor2 };
// Read vehicle mods
// Create new Dictionary
Mods = new Dictionary<int, int>();
// Read count of mods
short vehModCount = reader.ReadInt16();
short vehModCount = m.ReadInt16();
// Loop
for (int i = 0; i < vehModCount; i++)
{
// Read the mod value
Mods.Add(reader.ReadInt32(), reader.ReadInt32());
Mods.Add(m.ReadInt32(), m.ReadInt32());
}
if (reader.ReadBoolean())
if (m.ReadBoolean())
{
// Read vehicle damage model
DamageModel = new VehicleDamageModel()
{
BrokenDoors = reader.ReadByte(),
OpenedDoors=reader.ReadByte(),
BrokenWindows = reader.ReadByte(),
BurstedTires = reader.ReadInt16(),
LeftHeadLightBroken = reader.ReadByte(),
RightHeadLightBroken = reader.ReadByte()
BrokenDoors = m.ReadByte(),
OpenedDoors = m.ReadByte(),
BrokenWindows = m.ReadByte(),
BurstedTires = m.ReadInt16(),
LeftHeadLightBroken = m.ReadByte(),
RightHeadLightBroken = m.ReadByte()
};
}
// Read LockStatus
LockStatus=(VehicleLockStatus)reader.ReadByte();
LockStatus = (VehicleLockStatus)m.ReadByte();
// Read RadioStation
RadioStation=reader.ReadByte();
RadioStation = m.ReadByte();
LicensePlate=Encoding.ASCII.GetString(reader.ReadBytes(8));
LicensePlate = m.ReadString();
Livery=(int)(reader.ReadByte()-1);
Livery = m.ReadByte() - 1;
}
#endregion
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -10,20 +10,19 @@ namespace RageCoop.Core
public byte[] Buffer { get; set; }
public int Recorded { get; set; }
public override PacketType Type => PacketType.Voice;
public override byte[] Serialize()
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>();
data.AddInt(ID);
data.AddArray(Buffer);
data.AddInt(Recorded);
return data.ToArray();
m.Write(ID);
m.Write(Buffer);
m.Write(Recorded);
}
public override void Deserialize(byte[] array)
public override void Deserialize(NetIncomingMessage m)
{
var reader = new BitReader(array);
ID = reader.ReadInt32();
Buffer = reader.ReadByteArray();
Recorded = reader.ReadInt32();
ID = m.ReadInt32();
Buffer = m.ReadByteArray();
Recorded = m.ReadInt32();
}
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Text;
namespace RageCoop.Core.Scripting
{
@ -10,8 +10,8 @@ namespace RageCoop.Core.Scripting
/// </summary>
public static class CustomEvents
{
static MD5 Hasher = MD5.Create();
static Dictionary<int,string> Hashed=new Dictionary<int,string>();
private static readonly MD5 Hasher = MD5.Create();
private static readonly Dictionary<int, string> Hashed = new Dictionary<int, string>();
internal static readonly int OnPlayerDied = Hash("RageCoop.OnPlayerDied");
internal static readonly int SetWeather = Hash("RageCoop.SetWeather");
internal static readonly int OnPedDeleted = Hash("RageCoop.OnPedDeleted");
@ -40,25 +40,20 @@ namespace RageCoop.Core.Scripting
public static int Hash(string s)
{
var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
string name;
lock (Hashed)
{
if (Hashed.TryGetValue(hash, out name))
if (Hashed.TryGetValue(hash, out string name))
{
if (name!=s)
if (name != s)
{
throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}");
}
else
{
return hash;
}
}
else
{
Hashed.Add(hash, s);
return hash;
}
Hashed.Add(hash, s);
return hash;
}
}
}

View File

@ -1,25 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace RageCoop.Core.Scripting
{
/// <summary>
///
/// </summary>
public class ResourceFile
/// <summary>
///
/// </summary>
public class ResourceFile
{
/// <summary>
/// Full name with relative path of this file
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Whether this is a directory
/// </summary>
public bool IsDirectory { get; internal set; }
/// <summary>
/// Get a stream that can be used to read file content.
/// </summary>
public Func<Stream> GetStream { get; internal set; }
/// <summary>
/// Full name with relative path of this file
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Whether this is a directory
/// </summary>
public bool IsDirectory { get; internal set; }
/// <summary>
/// Get a stream that can be used to read file content.
/// </summary>
public Func<Stream> GetStream { get; internal set; }
}
}

View File

@ -1,17 +1,17 @@
using System;
using System.Threading;
using System.Collections.Concurrent;
using System.Threading;
namespace RageCoop.Core
{
/// <summary>
/// A worker that constantly execute jobs in a background thread.
/// </summary>
public class Worker:IDisposable
public class Worker : IDisposable
{
private SemaphoreSlim _semaphoreSlim;
private Thread _workerThread;
private bool _stopping=false;
private readonly SemaphoreSlim _semaphoreSlim;
private readonly Thread _workerThread;
private bool _stopping = false;
/// <summary>
/// Name of the worker
/// </summary>
@ -19,37 +19,37 @@ namespace RageCoop.Core
/// <summary>
/// Whether this worker is busy executing job(s).
/// </summary>
public bool IsBusy { get;private set; }
internal Worker(string name,Logger logger,int maxJobs = Int32.MaxValue)
public bool IsBusy { get; private set; }
internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue)
{
Name = name;
_semaphoreSlim = new SemaphoreSlim(0,maxJobs);
_workerThread=new Thread(() =>
{
while (!_stopping)
{
IsBusy=false;
_semaphoreSlim.Wait();
if(Jobs.TryDequeue(out var job))
{
IsBusy=true;
try
{
job.Invoke();
}
catch (Exception ex)
{
logger.Error("Error occurred when executing queued job:");
logger.Error(ex);
}
}
else
{
throw new InvalidOperationException("Hmm... that's unexpected.");
}
}
IsBusy=false;
});
_semaphoreSlim = new SemaphoreSlim(0, maxJobs);
_workerThread = new Thread(() =>
{
while (!_stopping)
{
IsBusy = false;
_semaphoreSlim.Wait();
if (Jobs.TryDequeue(out var job))
{
IsBusy = true;
try
{
job.Invoke();
}
catch (Exception ex)
{
logger.Error("Error occurred when executing queued job:");
logger.Error(ex);
}
}
else
{
throw new InvalidOperationException("Hmm... that's unexpected.");
}
}
IsBusy = false;
});
_workerThread.Start();
}
/// <summary>
@ -66,7 +66,7 @@ namespace RageCoop.Core
/// </summary>
public void Stop()
{
_stopping=true;
_stopping = true;
QueueJob(() => { });
if (_workerThread.IsAlive)
{
@ -81,6 +81,6 @@ namespace RageCoop.Core
Stop();
_semaphoreSlim.Dispose();
}
private ConcurrentQueue<Action> Jobs=new ConcurrentQueue<Action>();
private readonly ConcurrentQueue<Action> Jobs = new ConcurrentQueue<Action>();
}
}

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using Lidgren.Network;
using RageCoop.Core;
using Lidgren.Network;
using System.Diagnostics;
using RageCoop.Core.Scripting;
using System.Security.Cryptography;
using RageCoop.Server.Scripting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Security.Cryptography;
namespace RageCoop.Server
{
@ -18,12 +18,17 @@ namespace RageCoop.Server
private readonly Server Server;
internal Client(Server server)
{
Server=server;
Server = server;
}
/// <summary>
/// Gets the total number of entities owned by this client
/// </summary>
public int EntitiesCount { get; internal set; }
/// <summary>
/// Th client's IP address and port.
/// </summary>
public IPEndPoint EndPoint { get { return Connection?.RemoteEndPoint; } }
public IPEndPoint EndPoint => Connection?.RemoteEndPoint;
/// <summary>
/// Internal(LAN) address of this client, used for NAT hole-punching
@ -31,7 +36,7 @@ namespace RageCoop.Server
public IPEndPoint InternalEndPoint { get; internal set; }
internal long NetHandle = 0;
internal NetConnection Connection { get;set; }
internal NetConnection Connection { get; set; }
/// <summary>
/// The <see cref="ServerPed"/> instance representing the client's main character.
/// </summary>
@ -39,45 +44,47 @@ namespace RageCoop.Server
/// <summary>
/// The client's latency in seconds.
/// </summary>
public float Latency => Connection.AverageRoundtripTime/2;
public float Latency => Connection.AverageRoundtripTime / 2;
internal readonly Dictionary<int, Action<object>> Callbacks = new();
internal byte[] PublicKey { get; set; }
/// <summary>
/// Indicates whether the client has succefully loaded all resources.
/// </summary>
public bool IsReady { get; internal set; }=false;
public bool IsReady { get; internal set; } = false;
/// <summary>
/// The client's username.
/// </summary>
public string Username { get;internal set; } = "N/A";
public string Username { get; internal set; } = "N/A";
private bool _autoRespawn=true;
private bool _autoRespawn = true;
/// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary>
public bool EnableAutoRespawn {
get { return _autoRespawn; }
set {
BaseScript.SetAutoRespawn(this,value);
_autoRespawn=value;
public bool EnableAutoRespawn
{
get => _autoRespawn;
set
{
BaseScript.SetAutoRespawn(this, value);
_autoRespawn = value;
}
}
private bool _displayNameTag=true;
private Stopwatch _latencyWatch = new Stopwatch();
private bool _displayNameTag = true;
private readonly Stopwatch _latencyWatch = new Stopwatch();
/// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary>
public bool DisplayNameTag
{
get { return _displayNameTag; }
get => _displayNameTag;
set
{
Server.BaseScript.SetNameTag(this,value);
_displayNameTag=value;
Server.BaseScript.SetNameTag(this, value);
_displayNameTag = value;
}
}
#region FUNCTIONS
@ -85,7 +92,7 @@ namespace RageCoop.Server
/// Kick this client
/// </summary>
/// <param name="reason"></param>
public void Kick(string reason="You have been kicked!")
public void Kick(string reason = "You have been kicked!")
{
Connection?.Disconnect(reason);
}
@ -124,7 +131,7 @@ namespace RageCoop.Server
/// <param name="args"></param>
public void SendNativeCall<T>(Action<object> callBack, GTA.Native.Hash hash, params object[] args)
{
var argsList= new List<object>(args);
var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID<T>(callBack), (ulong)hash });
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
@ -137,7 +144,7 @@ namespace RageCoop.Server
public void SendNativeCall(GTA.Native.Hash hash, params object[] args)
{
var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty,(ulong)hash });
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
// Server.Logger?.Debug(argsList.DumpWithType());
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
}
@ -146,7 +153,7 @@ namespace RageCoop.Server
int ID = 0;
lock (Callbacks)
{
while ((ID==0)
while ((ID == 0)
|| Callbacks.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
@ -165,7 +172,7 @@ namespace RageCoop.Server
/// </summary>
/// <param name="hash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
/// <param name="args">Arguments</param>
public void SendCustomEvent(int hash,params object[] args)
public void SendCustomEvent(int hash, params object[] args)
{
if (!IsReady)
{
@ -178,8 +185,8 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent()
{
Hash=hash,
Args=args
Hash = hash,
Args = args
}.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);
@ -206,10 +213,10 @@ namespace RageCoop.Server
{
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent(null,true)
new Packets.CustomEvent(null, true)
{
Hash=hash,
Args=args
Hash = hash,
Args = args
}.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);

View File

@ -1,16 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Server
namespace RageCoop.Server
{
internal class FileTransfer
{
public int ID { get; set; }
public float Progress { get; set; }
public string Name { get; set; }
public bool Cancel { get; set; }=false;
public bool Cancel { get; set; } = false;
}
}

View File

@ -1,12 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using RageCoop.Core;
namespace RageCoop.Server
namespace RageCoop.Server
{
internal class HolePunch
{

View File

@ -0,0 +1,197 @@
using ICSharpCode.SharpZipLib.Zip;
using Lidgren.Network;
using Newtonsoft.Json;
using RageCoop.Core;
using RageCoop.Server.Scripting;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace RageCoop.Server
{
public partial class Server
{
private const string _versionURL = "https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/main/RageCoop.Server/Properties/AssemblyInfo.cs";
private void SendPlayerUpdate()
{
foreach (var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID = c.Player.ID,
Username = c.Username,
Latency = c.Latency,
Position = c.Player.Position,
IsHost = c == _hostClient
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch (Exception ex)
{
Logger?.Error(ex);
}
}
}
private IpInfo IpInfo = null;
private bool CanAnnounce = false;
private void Announce()
{
HttpResponseMessage response = null;
HttpClient httpClient = new();
if (IpInfo == null)
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 |
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
try
{
IpInfo = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {IpInfo.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
}
if (!CanAnnounce)
{
var existing = JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Util.GetFinalRedirect(Settings.MasterServer))).Where(x => x.address == IpInfo.Address && x.port == Settings.Port.ToString()).FirstOrDefault();
if(existing != null)
{
Logger.Warning("Server info already present in master server, waiting for 10 seconds...");
return;
}
else
{
CanAnnounce = true;
}
}
try
{
Security.GetPublicKey(out var pModulus, out var pExpoenet);
var serverInfo = new ServerInfo
{
address = IpInfo.Address,
port = Settings.Port.ToString(),
country = IpInfo.Country,
name = Settings.Name,
version = Version.ToString(),
players = MainNetServer.ConnectionsCount.ToString(),
maxPlayers = Settings.MaxPlayers.ToString(),
description = Settings.Description,
website = Settings.Website,
gameMode = Settings.GameMode,
language = Settings.Language,
useP2P = Settings.UseP2P,
useZT = Settings.UseZeroTier,
ztID = Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress = Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus = Convert.ToBase64String(pModulus),
publicKeyExponent = Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
return;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{response.Content.ReadAsStringAsync().GetAwaiter().GetResult()}]");
}
}
}
private void CheckUpdate()
{
try
{
var versionLine = HttpHelper.DownloadString(_versionURL).Split('\n', StringSplitOptions.RemoveEmptyEntries).Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
var start = versionLine.IndexOf('\"') + 1;
var end = versionLine.LastIndexOf('\"');
var latest = Version.Parse(versionLine.AsSpan(start, end - start));
if (latest <= Version) { return; }
// wait ten minutes for the build to complete
API.SendChatMessage($"New server version found: {latest}, server will update in 10 minutes");
Thread.Sleep(10 * 60 * 1000);
API.SendChatMessage("downloading update...");
var downloadURL = $"https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Server-{CoreUtils.GetInvariantRID()}.zip";
if (Directory.Exists("Update")) { Directory.Delete("Update", true); }
HttpHelper.DownloadFile(downloadURL, "Update.zip", null);
Logger?.Info("Installing update");
Directory.CreateDirectory("Update");
new FastZip().ExtractZip("Update.zip", "Update", FastZip.Overwrite.Always, null, null, null, true);
MainNetServer.Shutdown("Server updating");
Logger.Info("Server shutting down!");
Logger.Flush();
Process.Start(Path.Combine("Update", RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "RageCoop.Server.exe" : "RageCoop.Server"), "update \"" + AppDomain.CurrentDomain.BaseDirectory[0..^1] + "\"");
Environment.Exit(0);
}
catch (Exception ex)
{
Logger?.Error("Update", ex);
}
}
private void KickAssholes()
{
foreach (var c in ClientsByNetHandle.Values.ToArray())
{
if (c.EntitiesCount > Settings.SpamLimit && Settings.KickSpamming)
{
c.Kick("Bye bye asshole: spamming");
API.SendChatMessage($"Asshole {c.Username} was kicked: Spamming");
}
else if (Settings.KickGodMode && c.Player.IsInvincible)
{
c.Kick("Bye bye asshole: godmode");
API.SendChatMessage($"Asshole {c.Username} was kicked: GodMode");
}
}
}
}
}

View File

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
using System;
using System.Linq;
using System.Text;
namespace RageCoop.Server
{
@ -43,16 +41,17 @@ namespace RageCoop.Server
connection.Deny("Username is already taken!");
return;
}
try
{
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted, packet.AesIVCrypted);
var args = new HandshakeEventArgs()
{
EndPoint=connection.RemoteEndPoint,
ID=packet.PedID,
Username=packet.Username,
PasswordHash=Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
EndPoint = connection.RemoteEndPoint,
ID = packet.PedID,
Username = packet.Username,
PasswordHash = Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
};
API.Events.InvokePlayerHandshake(args);
if (args.Cancel)
@ -68,20 +67,22 @@ namespace RageCoop.Server
connection.Deny("Malformed handshak packet!");
return;
}
var handshakeSuccess = MainNetServer.CreateMessage();
var currentClients = ClientsByID.Values.ToArray();
var players = new Packets.PlayerData[currentClients.Length];
for (int i = 0; i<players.Length; i++)
for (int i = 0; i < players.Length; i++)
{
players[i]=new Packets.PlayerData()
players[i] = new Packets.PlayerData()
{
ID=currentClients[i].Player.ID,
Username=currentClients[i].Username,
ID = currentClients[i].Player.ID,
Username = currentClients[i].Username,
};
}
new Packets.HandshakeSuccess()
{
Players=players
Players = players
}.Pack(handshakeSuccess);
connection.Approve(handshakeSuccess);
Client tmpClient;
@ -91,25 +92,25 @@ namespace RageCoop.Server
{
var player = new ServerPed(this)
{
ID= packet.PedID,
ID = packet.PedID,
};
Entities.Add(player);
ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this)
{
NetHandle = connection.RemoteUniqueIdentifier,
Connection=connection,
Username=packet.Username,
Connection = connection,
Username = packet.Username,
Player = player,
InternalEndPoint=packet.InternalEndPoint,
InternalEndPoint = packet.InternalEndPoint,
}
);
player.Owner=tmpClient;
player.Owner = tmpClient;
ClientsByName.Add(packet.Username.ToLower(), tmpClient);
ClientsByID.Add(player.ID, tmpClient);
if (ClientsByNetHandle.Count==1)
if (ClientsByNetHandle.Count == 1)
{
_hostClient=tmpClient;
_hostClient = tmpClient;
}
}
@ -120,19 +121,19 @@ namespace RageCoop.Server
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private void PlayerConnected(Client newClient)
{
if (newClient==_hostClient)
if (newClient == _hostClient)
{
API.SendCustomEvent(new() { newClient }, CustomEvents.IsHost, true);
}
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
if (cons.Count != 0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
PedID=newClient.Player.ID,
PedID = newClient.Player.ID,
Username = newClient.Username
}.Pack(outgoingMessage);
@ -146,15 +147,13 @@ namespace RageCoop.Server
// Send all blips to this player
BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient });
// Create P2P connection
if (Settings.UseP2P)
{
ClientsByNetHandle.Values.ForEach(target =>
{
if (target==newClient) { return; }
HolePunch(target,newClient);
if (target == newClient) { return; }
HolePunch(target, newClient);
});
}
@ -170,23 +169,23 @@ namespace RageCoop.Server
private void PlayerDisconnected(Client localClient)
{
var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0)
if (cons.Count != 0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect()
{
PedID=localClient.Player.ID,
PedID = localClient.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
Entities.CleanUp(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); }
if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); }
if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); }
if (localClient==_hostClient)
if (localClient == _hostClient)
{
_hostClient = ClientsByNetHandle.Values.FirstOrDefault();

View File

@ -1,11 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
@ -14,12 +8,12 @@ namespace RageCoop.Server
{
private void PedSync(Packets.PedSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player.ID;
bool isPlayer = packet.ID == client.Player.ID;
if (isPlayer)
{
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
if (Settings.UseP2P) { return; }
@ -27,17 +21,17 @@ namespace RageCoop.Server
{
// Don't send data back
if (c.NetHandle==client.NetHandle) { continue; }
if (c.NetHandle == client.NetHandle) { continue; }
// Check streaming distance
if (isPlayer)
{
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
{
continue;
}
}
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
{
continue;
}
@ -49,35 +43,33 @@ namespace RageCoop.Server
}
private void VehicleSync(Packets.VehicleSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID;
QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID == client.Player?.LastVehicle?.ID;
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }
if (c.NetHandle == client.NetHandle) { continue; }
if (isPlayer)
{
// Player's vehicle
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
{
continue;
}
}
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.VehicleSync);
}
}
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
{
if (Settings.UseP2P) { return; }
Forward(packet, client, ConnectionChannel.ProjectileSync);
}

View File

@ -1,12 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
@ -17,19 +10,19 @@ namespace RageCoop.Server
// Send to host
Send(new Packets.HolePunchInit
{
Connect=false,
TargetID=client.Player.ID,
TargetInternal=client.InternalEndPoint.ToString(),
TargetExternal=client.EndPoint.ToString()
Connect = false,
TargetID = client.Player.ID,
TargetInternal = client.InternalEndPoint.ToString(),
TargetExternal = client.EndPoint.ToString()
}, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
// Send to client
Send(new Packets.HolePunchInit
{
Connect=true,
TargetID=host.Player.ID,
TargetInternal=host.InternalEndPoint.ToString(),
TargetExternal=host.EndPoint.ToString()
Connect = true,
TargetID = host.Player.ID,
TargetInternal = host.InternalEndPoint.ToString(),
TargetExternal = host.EndPoint.ToString()
}, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
}
}

View File

@ -0,0 +1,247 @@
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Server.Scripting;
using System;
namespace RageCoop.Server
{
public partial class Server
{
private void Listen()
{
NetIncomingMessage msg = null;
while (!_stopping)
{
try
{
msg = MainNetServer.WaitMessage(200);
ProcessMessage(msg);
}
catch (Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
if (msg != null)
{
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
}
}
}
Logger?.Info("Server is shutting down!");
MainNetServer.Shutdown("Server is shutting down!");
BaseScript.OnStop();
Resources.UnloadAll();
}
private void ProcessMessage(NetIncomingMessage message)
{
Client sender;
if (message == null) { return; }
switch (message.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
{
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
if (message.ReadByte() != (byte)PacketType.Handshake)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
message.SenderConnection.Deny("Wrong packet!");
}
else
{
try
{
GetHandshake(message.SenderConnection, message.GetPacket<Packets.Handshake>());
}
catch (Exception e)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
Logger?.Error(e);
message.SenderConnection.Deny(e.Message);
}
}
break;
}
case NetIncomingMessageType.StatusChanged:
{
// Get sender client
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
if (status == NetConnectionStatus.Disconnected)
{
PlayerDisconnected(sender);
}
else if (status == NetConnectionStatus.Connected)
{
PlayerConnected(sender);
QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender);
}
break;
}
case NetIncomingMessageType.Data:
{
// Get sender client
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
// Get packet type
var type = (PacketType)message.ReadByte();
switch (type)
{
case PacketType.Response:
{
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id);
}
break;
}
case PacketType.Request:
{
int id = message.ReadInt32();
var reqType = (PacketType)message.ReadByte();
if (RequestHandlers.TryGetValue(reqType, out var handler))
{
var response = MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message, sender).Pack(response);
MainNetServer.SendMessage(response, message.SenderConnection, NetDeliveryMethod.ReliableOrdered);
}
else
{
Logger.Warning("Did not find a request handler of type: " + reqType);
}
break;
}
default:
{
if (type.IsSyncEvent())
{
// Sync Events
if (Settings.UseP2P) { break; }
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count != 0)
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type);
outgoingMessage.Write(message.ReadBytes(message.LengthBytes - 1));
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
HandlePacket(type, message, sender);
}
break;
}
}
}
break;
}
case NetIncomingMessageType.ErrorMessage:
Logger?.Error(message.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger?.Warning(message.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logger?.Debug(message.ReadString());
break;
case NetIncomingMessageType.UnconnectedData:
{
if (message.ReadByte() == (byte)PacketType.PublicKeyRequest)
{
var msg = MainNetServer.CreateMessage();
var p = new Packets.PublicKeyResponse();
Security.GetPublicKey(out p.Modulus, out p.Exponent);
p.Pack(msg);
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
}
}
break;
default:
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
break;
}
MainNetServer.Recycle(message);
}
private void HandlePacket(PacketType type, NetIncomingMessage msg, Client sender)
{
try
{
switch (type)
{
case PacketType.PedSync:
PedSync(msg.GetPacket<Packets.PedSync>(), sender);
break;
case PacketType.VehicleSync:
VehicleSync(msg.GetPacket<Packets.VehicleSync>(), sender);
break;
case PacketType.ProjectileSync:
ProjectileSync(msg.GetPacket<Packets.ProjectileSync>(), sender);
break;
case PacketType.ChatMessage:
{
Packets.ChatMessage packet = new((b) =>
{
return Security.Decrypt(b, sender.EndPoint);
});
packet.Deserialize(msg);
ChatMessageReceived(packet.Username, packet.Message, sender);
}
break;
case PacketType.Voice:
{
if (Settings.UseVoice && !Settings.UseP2P)
{
Forward(msg.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice);
}
}
break;
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Deserialize(msg);
QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
default:
Logger?.Error("Unhandled Data / Packet type");
break;
}
}
catch (Exception e)
{
DisconnectAndLog(sender.Connection, type, e);
}
}
}
}

View File

@ -1,22 +1,16 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Net;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Net.Http;
using Lidgren.Network;
using RageCoop.Core;
using Newtonsoft.Json;
using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting;
using System.Net.Sockets;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Timer = System.Timers.Timer;
namespace RageCoop.Server
{
@ -36,23 +30,25 @@ namespace RageCoop.Server
internal ServerEntities Entities;
internal readonly Dictionary<Command, Action<CommandContext>> Commands = new();
internal readonly Dictionary<long,Client> ClientsByNetHandle = new();
internal readonly Dictionary<long, Client> ClientsByNetHandle = new();
internal readonly Dictionary<string, Client> ClientsByName = new();
internal readonly Dictionary<int, Client> ClientsByID = new();
internal Client _hostClient;
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
private readonly Dictionary<int, FileTransfer> InProgressFileTransfers = new();
internal Resources Resources;
internal Logger Logger;
private Security Security;
internal Security Security;
private bool _stopping = false;
private Thread _listenerThread;
private Thread _announceThread;
private Thread _latencyThread;
private Worker _worker;
private HashSet<char> _allowedCharacterSet;
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new();
private readonly Thread _listenerThread;
private readonly Timer _announceTimer = new();
private readonly Timer _playerUpdateTimer = new();
private readonly Timer _antiAssholesTimer = new();
private readonly Timer _updateTimer = new();
private readonly Worker _worker;
private readonly HashSet<char> _allowedCharacterSet;
private readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new();
internal Dictionary<PacketType, Func<NetIncomingMessage, Client, Packet>> RequestHandlers = new();
/// <summary>
/// Get the current server version
/// </summary>
@ -63,151 +59,49 @@ namespace RageCoop.Server
/// <param name="settings"></param>
/// <param name="logger"></param>
/// <exception cref="ArgumentNullException"></exception>
public Server(Settings settings,Logger logger=null)
public Server(Settings settings, Logger logger = null)
{
Settings = settings;
if (settings==null) { throw new ArgumentNullException("Server settings cannot be null!"); }
Logger=logger;
if (Logger!=null) { Logger.LogLevel=Settings.LogLevel;}
API=new API(this);
Resources=new Resources(this);
Security=new Security(Logger);
Entities=new ServerEntities(this);
BaseScript=new BaseScript(this);
_allowedCharacterSet=new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray());
if (settings == null) { throw new ArgumentNullException("Server settings cannot be null!"); }
Logger = logger;
if (Logger != null) { Logger.LogLevel = Settings.LogLevel; }
API = new API(this);
Resources = new Resources(this);
Security = new Security(Logger);
Entities = new ServerEntities(this);
BaseScript = new BaseScript(this);
_allowedCharacterSet = new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray());
_worker=new Worker("ServerWorker", Logger);
_worker = new Worker("ServerWorker", Logger);
_listenerThread=new Thread(() => Listen());
_latencyThread=new Thread(() =>
_listenerThread = new Thread(() => Listen());
_announceTimer.Interval = 1;
_announceTimer.Elapsed += (s, e) =>
{
while (!_stopping)
{
foreach(var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.Player.ID,
Username=c.Username,
Latency=c.Latency,
Position=c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch(Exception ex)
{
Logger?.Error(ex);
}
}
Thread.Sleep(1000);
}
});
_announceThread=new Thread(async () =>
_announceTimer.Interval = 10000;
_announceTimer.Stop();
Announce();
_announceTimer.Start();
};
_playerUpdateTimer.Interval = 1000;
_playerUpdateTimer.Elapsed += (s, e) => SendPlayerUpdate();
_antiAssholesTimer.Interval = 5000;
_antiAssholesTimer.Elapsed += (s, e) => KickAssholes();
_updateTimer.Interval = 1;
_updateTimer.Elapsed += (s, e) =>
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpClient httpClient = new();
IpInfo info;
try
{
info = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {info.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
while (!_stopping)
{
HttpResponseMessage response = null;
try
{
Security.GetPublicKey(out var pModulus,out var pExpoenet);
var serverInfo = new ServerInfo
{
address = info.Address,
port=Settings.Port.ToString(),
country=info.Country,
name=Settings.Name,
version=Version.ToString(),
players=MainNetServer.ConnectionsCount.ToString(),
maxPlayers=Settings.MaxPlayers.ToString(),
description=Settings.Description,
website=Settings.Website,
gameMode=Settings.GameMode,
language=Settings.Language,
useP2P=Settings.UseP2P,
useZT=Settings.UseZeroTier,
ztID=Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress=Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus=Convert.ToBase64String(pModulus),
publicKeyExponent=Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = await httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
// Sleep for 5s
Thread.Sleep(5000);
continue;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = await response.Content.ReadAsStringAsync();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]");
}
}
// Sleep for 10s
for (int i = 0; i<10; i++)
{
if (_stopping)
{
break;
}
else
{
Thread.Sleep(1000);
}
}
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
});
_updateTimer.Interval = 1000 * 60 * 10; // 10 minutes
_updateTimer.Stop();
CheckUpdate();
_updateTimer.Start();
};
}
@ -217,15 +111,26 @@ namespace RageCoop.Server
public void Start()
{
Logger?.Info("================");
Logger?.Info($"Server bound to: 0.0.0.0:{Settings.Port}");
Logger?.Info($"Listening port: {Settings.Port}");
Logger?.Info($"Server version: {Version}");
Logger?.Info($"Compatible RAGECOOP versions: {Version.ToString(3)}");
Logger?.Info($"Compatible client version: {Version.ToString(3)}");
Logger?.Info($"Runtime: {CoreUtils.GetInvariantRID()} => {System.Runtime.InteropServices.RuntimeInformation.RuntimeIdentifier}");
Logger?.Info("================");
Logger?.Info($"Listening addresses:");
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
Logger?.Info($"[{netInterface.Description}]:");
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
{
Logger.Info(string.Join(", ", addr.Address));
}
Logger.Info("");
}
if (Settings.UseZeroTier)
{
Logger?.Info($"Joining ZeroTier network: "+Settings.ZeroTierNetworkID);
if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID)==null)
Logger?.Info($"Joining ZeroTier network: " + Settings.ZeroTierNetworkID);
if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID) == null)
{
throw new Exception("Failed to obtain ZeroTier network IP");
}
@ -242,7 +147,7 @@ namespace RageCoop.Server
MaximumConnections = Settings.MaxPlayers,
EnableUPnP = false,
AutoFlushSendQueue = true,
PingInterval=5
PingInterval = 5
};
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
@ -250,289 +155,58 @@ namespace RageCoop.Server
MainNetServer = new NetServer(config);
MainNetServer.Start();
Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port));
BaseScript.API=API;
BaseScript.API = API;
BaseScript.OnStart();
Resources.LoadAll();
_listenerThread.Start();
_latencyThread.Start();
Logger?.Info("Listening for clients");
_playerUpdateTimer.Enabled = true;
if (Settings.AnnounceSelf)
{
_announceThread.Start();
_announceTimer.Enabled = true;
}
if (Settings.AutoUpdate)
{
_updateTimer.Enabled = true;
}
_antiAssholesTimer.Enabled = true;
Logger?.Info("Listening for clients");
}
/// <summary>
/// Terminate threads and stop the server
/// </summary>
public void Stop()
{
_stopping = true;
Logger?.Flush();
Logger?.Dispose();
_stopping = true;
_listenerThread.Join();
_latencyThread.Join();
if (_announceThread.IsAlive)
{
_announceThread.Join();
}
_playerUpdateTimer.Enabled = false;
_announceTimer.Enabled = false;
_worker.Dispose();
}
private void Listen()
{
NetIncomingMessage msg=null;
while (!_stopping)
{
try
{
msg=MainNetServer.WaitMessage(200);
ProcessMessage(msg);
}
catch(Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
if (msg!=null)
{
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
}
}
}
Logger?.Info("Server is shutting down!");
MainNetServer.Shutdown("Server is shutting down!");
BaseScript.OnStop();
Resources.UnloadAll();
}
private void ProcessMessage(NetIncomingMessage message)
{
Client sender;
if (message == null) { return; }
switch (message.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
{
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
if (message.ReadByte() != (byte)PacketType.Handshake)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
message.SenderConnection.Deny("Wrong packet!");
}
else
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
}
catch (Exception e)
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
Logger?.Error(e);
message.SenderConnection.Deny(e.Message);
}
}
break;
}
case NetIncomingMessageType.StatusChanged:
{
// Get sender client
if (!ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
if (status == NetConnectionStatus.Disconnected)
{
PlayerDisconnected(sender);
}
else if (status == NetConnectionStatus.Connected)
{
PlayerConnected(sender);
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender);
}
break;
}
case NetIncomingMessageType.Data:
{
// Get sender client
if (ClientsByNetHandle.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
// Get packet type
var type = (PacketType)message.ReadByte();
switch (type)
{
case PacketType.Response:
{
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
PendingResponses.Remove(id);
}
break;
}
case PacketType.Request:
{
int id = message.ReadInt32();
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
{
var response=MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(message.ReadInt32()),sender).Pack(response);
MainNetServer.SendMessage(response,message.SenderConnection,NetDeliveryMethod.ReliableOrdered);
}
break;
}
default:
{
byte[] data = message.ReadBytes(message.ReadInt32());
if (type.IsSyncEvent())
{
// Sync Events
if (Settings.UseP2P) { break; }
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count!=0)
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type);
outgoingMessage.Write(data.Length);
outgoingMessage.Write(data);
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
HandlePacket(type, data, sender);
}
break;
}
}
}
break;
}
case NetIncomingMessageType.ErrorMessage:
Logger?.Error(message.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logger?.Warning(message.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logger?.Debug(message.ReadString());
break;
case NetIncomingMessageType.UnconnectedData:
{
if (message.ReadByte()==(byte)PacketType.PublicKeyRequest)
{
var msg = MainNetServer.CreateMessage();
var p=new Packets.PublicKeyResponse();
Security.GetPublicKey(out p.Modulus,out p.Exponent);
p.Pack(msg);
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
}
}
break;
default:
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
break;
}
MainNetServer.Recycle(message);
}
internal void QueueJob(Action job)
{
_worker.QueueJob(job);
}
private void HandlePacket(PacketType type,byte[] data,Client sender)
{
try
{
switch (type)
{
case PacketType.PedSync:
PedSync(data.GetPacket<Packets.PedSync>(), sender);
break;
case PacketType.VehicleSync:
VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender);
break;
case PacketType.ProjectileSync:
ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender);
break;
case PacketType.ChatMessage:
{
Packets.ChatMessage packet = new((b) =>
{
return Security.Decrypt(b,sender.EndPoint);
});
packet.Deserialize(data);
ChatMessageReceived(packet.Username,packet.Message, sender);
}
break;
case PacketType.Voice:
{
if (Settings.UseVoice)
{
Forward(data.GetPacket<Packets.Voice>(), sender, ConnectionChannel.Voice);
}
}
break;
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Deserialize(data);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
default:
Logger?.Error("Unhandled Data / Packet type");
break;
}
}
catch (Exception e)
{
DisconnectAndLog(sender.Connection, type, e);
}
}
// Send a message to targets or all players
internal void ChatMessageReceived(string name, string message,Client sender=null)
internal void ChatMessageReceived(string name, string message, Client sender = null)
{
if (message.StartsWith('/'))
{
string[] cmdArgs = message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1);
_worker.QueueJob(()=>API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
QueueJob(() => API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
return;
}
message = message.Replace("~", "");
_worker.QueueJob(() => API.Events.InvokeOnChatMessage(message, sender));
QueueJob(() => API.Events.InvokeOnChatMessage(message, sender));
foreach(var c in ClientsByNetHandle.Values)
foreach (var c in ClientsByNetHandle.Values)
{
var msg = MainNetServer.CreateMessage();
var crypt = new Func<string, byte[]>((s) =>
@ -541,25 +215,23 @@ namespace RageCoop.Server
});
new Packets.ChatMessage(crypt)
{
Username=name,
Message=message
Username = name,
Message = message
}.Pack(msg);
MainNetServer.SendMessage(msg,c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
MainNetServer.SendMessage(msg, c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
}
Logger?.Info(name + ": " + message);
}
internal void SendChatMessage(string name, string message, Client target)
{
if(target == null) { return; }
if (target == null) { return; }
var msg = MainNetServer.CreateMessage();
new Packets.ChatMessage(new Func<string, byte[]>((s) =>
{
return Security.Encrypt(s.GetBytes(), target.EndPoint);
}))
{
Username= name,
Message=message,
Username = name,
Message = message,
}.Pack(msg);
MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
}
@ -598,62 +270,60 @@ namespace RageCoop.Server
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
}
}
internal T GetResponse<T>(Client client,Packet request, ConnectionChannel channel = ConnectionChannel.RequestResponse,int timeout=5000) where T:Packet, new()
internal T GetResponse<T>(Client client, Packet request, ConnectionChannel channel = ConnectionChannel.RequestResponse, int timeout = 5000) where T : Packet, new()
{
if (Thread.CurrentThread==_listenerThread)
if (Thread.CurrentThread == _listenerThread)
{
throw new InvalidOperationException("Cannot wait for response from the listener thread!");
}
var received=new AutoResetEvent(false);
byte[] response=null;
var received = new AutoResetEvent(false);
T response = new T();
var id = NewRequestID();
PendingResponses.Add(id, (type,p) =>
PendingResponses.Add(id, (type, m) =>
{
response=p;
response.Deserialize(m);
received.Set();
});
var msg = MainNetServer.CreateMessage();
msg.Write((byte)PacketType.Request);
msg.Write(id);
request.Pack(msg);
MainNetServer.SendMessage(msg,client.Connection,NetDeliveryMethod.ReliableOrdered,(int)channel);
MainNetServer.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableOrdered, (int)channel);
if (received.WaitOne(timeout))
{
var p = new T();
p.Deserialize(response);
return p;
return response;
}
else
{
return null;
}
}
internal void SendFile(string path,string name,Client client,Action<float> updateCallback=null)
{
SendFile(File.OpenRead(path), name,client,NewFileID(),updateCallback);
}
internal void SendFile(Stream stream, string name, Client client,int id=default, Action<float> updateCallback = null)
{
id = id ==default? NewFileID(): id ;
return null;
}
internal void SendFile(string path, string name, Client client, Action<float> updateCallback = null)
{
var fs = File.OpenRead(path);
SendFile(fs, name, client, NewFileID(), updateCallback);
fs.Close();
fs.Dispose();
}
internal void SendFile(Stream stream, string name, Client client, int id = default, Action<float> updateCallback = null)
{
stream.Seek(0, SeekOrigin.Begin);
id = id == default ? NewFileID() : id;
var total = stream.Length;
Logger?.Debug($"Requesting file transfer:{name}, {total}");
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{
FileLength= total,
Name=name,
ID=id,
}, ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload)
FileLength = total,
Name = name,
ID = id,
}, ConnectionChannel.File)?.Response != FileResponse.NeedToDownload)
{
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
stream.Close();
stream.Dispose();
return;
}
Logger?.Debug($"Initiating file transfer:{name}, {total}");
FileTransfer transfer = new()
{
ID=id,
ID = id,
Name = name,
};
InProgressFileTransfers.Add(id, transfer);
@ -663,40 +333,38 @@ namespace RageCoop.Server
{
// 4 KB chunk
byte[] chunk = new byte[4096];
read += thisRead=stream.Read(chunk, 0, 4096);
if (thisRead!=chunk.Length)
read += thisRead = stream.Read(chunk, 0, 4096);
if (thisRead != chunk.Length)
{
if (thisRead==0) { break; }
if (thisRead == 0) { break; }
Logger?.Trace($"Purging chunk:{thisRead}");
Array.Resize(ref chunk, thisRead);
}
Send(
new Packets.FileTransferChunk()
{
ID=id,
FileChunk=chunk,
ID = id,
FileChunk = chunk,
},
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered);
transfer.Progress=read/stream.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress); }
transfer.Progress = read / stream.Length;
if (updateCallback != null) { updateCallback(transfer.Progress); }
} while (thisRead>0);
} while (thisRead > 0);
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
{
ID= id,
}, ConnectionChannel.File)?.Response!=FileResponse.Completed)
ID = id,
}, ConnectionChannel.File)?.Response != FileResponse.Completed)
{
Logger.Warning($"File trasfer to {client.Username} failed: "+name);
Logger.Warning($"File trasfer to {client.Username} failed: " + name);
}
stream.Close();
stream.Dispose();
Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id);
}
internal int NewFileID()
{
int ID = 0;
while ((ID==0)
while ((ID == 0)
|| InProgressFileTransfers.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
@ -711,7 +379,7 @@ namespace RageCoop.Server
private int NewRequestID()
{
int ID = 0;
while ((ID==0)
while ((ID == 0)
|| PendingResponses.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
@ -723,11 +391,11 @@ namespace RageCoop.Server
}
return ID;
}
internal void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
internal void Send(Packet p, Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
p.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, client.Connection,method,(int)channel);
MainNetServer.SendMessage(outgoingMessage, client.Connection, method, (int)channel);
}
internal void Forward(Packet p, Client except, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{

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