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

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
**/packages **/packages
**/.vs **/.vs
**/.vscode **/.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 # 🌐 RAGECOOP
[![Downloads][downloads-shield]][downloads-url] [![Downloads][downloads-shield]][downloads-url]
[![Contributors][contributors-shield]][contributors-url] [![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url] [![Forks][forks-shield]][forks-url]
@ -9,24 +10,37 @@
# 🧠 That's it # 🧠 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! RAGECOOP brings multiplayer experience to the story mode, you can complete missions together with your friends, use mods without any restriction/getting banned, or just mess around with your fella!
# 📋 Requirements # 👁 Requirements
- Visual Studio 2022 - ScriptHookV
- .NET 6.0 - ScriptHookVDotNet 3.6.0 or later
- .NET Framework 4.8 - .NET Framework 4.8 Runtime or SDK
# 📚 Libraries # 📋 Building the project
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.4.0)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/releases/tag/v1.6) You'll need:
- Lidgren Network Custom (***PRIVATE***) - .NET 6.0 SDK
- .NET Framework 4.8 SDK
Recommended IDE:
- Visual Studio Code
- Visul Studio 2022
Then run `dotnet build` in the solution directory, built binaries are in the `bin` folder
# 📚 Third-party libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI)
- Lidgren Network Custom
- - No new features (only improvements) - - No new features (only improvements)
- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/13.0.1) - [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
- [ClearScript](https://github.com/microsoft/ClearScript) - [ClearScript](https://github.com/microsoft/ClearScript)
- [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) - [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
- [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins) - [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
# Features # 👋 Features
1. Synchronized bullets 1. Synchronized bullets
2. Synchronized vehicle/player/NPC 2. Synchronized vehicle/player/NPC
@ -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! 5. Decent compatibility with other mods, set up a private modded server to have some fun!
6. Weaponized vehicle sync(WIP). 6. Weaponized vehicle sync(WIP).
7. Optimization for high-Ping condition, play with friends around the world! 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) See [Bugs](https://github.com/RAGECOOP/RAGECOOP-V/issues/33)
## Installation # 🔫 Installation
Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki) Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki)
# Downloads # 🧨 Downloads
Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest) Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
@ -56,6 +70,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
# 🦆 Special thanks to # 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm) - [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
- - For testing, ideas, contributions and the first modification with the API - - For testing, ideas, contributions and the first modification with the API
- [crosire](https://github.com/crosire) - [crosire](https://github.com/crosire)
@ -64,6 +79,7 @@ Please note that this is incompatible with all previous versions of ragecoop, re
- - For the extensive work in LemonUI - - For the extensive work in LemonUI
# 📝 License # 📝 License
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE) This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
[downloads-shield]: https://img.shields.io/github/downloads/RAGECOOP/RAGECOOP-V/total?style=for-the-badge [downloads-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-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues [issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

View File

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

View File

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

View File

@ -1,7 +1,8 @@
using GTA; #if DEBUG
using GTA;
using LemonUI.Menus; using LemonUI.Menus;
using System.Drawing;
using System; using System;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -18,15 +19,16 @@ namespace RageCoop.Client
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left 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 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); private static readonly NativeCheckboxItem ShowNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
static DebugMenu() static DebugMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
DiagnosticMenu.Opening+=(sender, e) => DiagnosticMenu.Opening += (sender, e) =>
{ {
DiagnosticMenu.Clear(); DiagnosticMenu.Clear();
DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug())); 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())); DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
} }
}; };
SimulatedLatencyItem.Activated+=(s, e) => SimulatedLatencyItem.Activated += (s, e) =>
{ {
try 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(SimulatedLatencyItem);
Menu.Add(ShowNetworkInfoItem); Menu.Add(ShowNetworkInfoItem);
Menu.Add(ShowOwnerItem);
Menu.AddSubMenu(DiagnosticMenu); Menu.AddSubMenu(DiagnosticMenu);
} }
@ -53,3 +57,4 @@ namespace RageCoop.Client
} }
} }
#endif

View File

@ -12,9 +12,9 @@ namespace RageCoop.Client
UseMouse = false, UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
private static NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable"); private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable");
private static NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles"); private static readonly NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles");
public static NativeItem boneIndexItem = new NativeItem("Current bone index"); public static NativeItem boneIndexItem = new NativeItem("Current bone index");
public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index"); public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index");
public static NativeItem clipboardItem = new NativeItem("Copy to clipboard"); public static NativeItem clipboardItem = new NativeItem("Copy to clipboard");
@ -22,20 +22,20 @@ namespace RageCoop.Client
static DevToolMenu() static DevToolMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
enableItem.Activated+=enableItem_Activated; enableItem.Activated += enableItem_Activated;
enableItem.Checked=false; enableItem.Checked = false;
enableSecondaryItem.CheckboxChanged+=EnableSecondaryItem_Changed; enableSecondaryItem.CheckboxChanged += EnableSecondaryItem_Changed;
secondaryBoneIndexItem.Enabled=false; secondaryBoneIndexItem.Enabled = false;
clipboardItem.Activated+=ClipboardItem_Activated; clipboardItem.Activated += ClipboardItem_Activated;
dirItem.ItemChanged+=DirItem_ItemChanged; dirItem.ItemChanged += DirItem_ItemChanged;
foreach (var d in Enum.GetValues(typeof(MuzzleDir))) foreach (var d in Enum.GetValues(typeof(MuzzleDir)))
{ {
dirItem.Items.Add((MuzzleDir)d); dirItem.Items.Add((MuzzleDir)d);
} }
dirItem.SelectedIndex=0; dirItem.SelectedIndex = 0;
Menu.Add(enableItem); Menu.Add(enableItem);
Menu.Add(boneIndexItem); Menu.Add(boneIndexItem);
@ -49,19 +49,19 @@ namespace RageCoop.Client
{ {
if (enableSecondaryItem.Checked) if (enableSecondaryItem.Checked)
{ {
DevTool.UseSecondary=true; DevTool.UseSecondary = true;
secondaryBoneIndexItem.Enabled=true; secondaryBoneIndexItem.Enabled = true;
} }
else else
{ {
DevTool.UseSecondary=false; DevTool.UseSecondary = false;
secondaryBoneIndexItem.Enabled=false; secondaryBoneIndexItem.Enabled = false;
} }
} }
private static void DirItem_ItemChanged(object sender, ItemChangedEventArgs<MuzzleDir> e) 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) private static void ClipboardItem_Activated(object sender, EventArgs e)
@ -73,11 +73,11 @@ namespace RageCoop.Client
{ {
if (enableItem.Checked) if (enableItem.Checked)
{ {
DevTool.ToMark=Game.Player.Character.CurrentVehicle; DevTool.ToMark = Game.Player.Character.CurrentVehicle;
} }
else 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 Newtonsoft.Json;
using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using RageCoop.Core;
using GTA.UI;
namespace RageCoop.Client.Menus namespace RageCoop.Client.Menus
{ {
@ -30,7 +30,7 @@ namespace RageCoop.Client.Menus
static ServersMenu() static ServersMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) => Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
{ {
@ -38,7 +38,7 @@ namespace RageCoop.Client.Menus
Menu.Add(ResultItem = new NativeItem("Loading...")); Menu.Add(ResultItem = new NativeItem("Loading..."));
// Prevent freezing // Prevent freezing
GetServersThread=new Thread(() => GetAllServers()); GetServersThread = new Thread(() => GetAllServers());
GetServersThread.Start(); GetServersThread.Start();
}; };
Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) => Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
@ -76,7 +76,7 @@ namespace RageCoop.Client.Menus
foreach (ServerInfo server in serverList) foreach (ServerInfo server in serverList)
{ {
string address = $"{server.address}:{server.port}"; string address = $"{server.address}:{server.port}";
NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}.x~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" }; NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" };
tmpItem.Activated += (object sender, EventArgs e) => tmpItem.Activated += (object sender, EventArgs e) =>
{ {
try try
@ -84,14 +84,14 @@ namespace RageCoop.Client.Menus
Menu.Visible = false; Menu.Visible = false;
if (server.useZT) if (server.useZT)
{ {
address=$"{server.ztAddress}:{server.port}"; address = $"{server.ztAddress}:{server.port}";
Main.QueueAction(() => { Notification.Show($"~y~Joining ZeroTier network... {server.ztID}"); }); Notification.Show($"~y~Joining ZeroTier network... {server.ztID}");
if (ZeroTierHelper.Join(server.ztID)==null) if (ZeroTierHelper.Join(server.ztID) == null)
{ {
throw new Exception("Failed to obtain ZeroTier network IP"); 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 #if !NON_INTERACTIVE
CoopMenu.ServerIpItem.AltTitle = address; 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 _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic);
private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu); private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableTraffic); private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableAlternatePause);
private static readonly NativeCheckboxItem _showBlip = new NativeCheckboxItem("Show player blip", "Show other player's blip on map", Main.Settings.ShowPlayerBlip);
private static readonly NativeCheckboxItem _showNametag = new NativeCheckboxItem("Show player nametag", "Show other player's nametag on your screen", Main.Settings.ShowPlayerNameTag);
private static readonly NativeCheckboxItem _disableVoice = new NativeCheckboxItem("Enable voice", "Check your GTA:V settings to find the right key on your keyboard for PushToTalk and talk to your friends", Main.Settings.Voice); private static readonly NativeCheckboxItem _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 readonly 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 readonly 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 _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() static SettingsMenu()
{ {
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0); Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0); Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
_disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged; _disableTrafficItem.CheckboxChanged += DisableTrafficCheckboxChanged;
_disablePauseAlt.CheckboxChanged+= DisablePauseAltCheckboxChanged; _disablePauseAlt.CheckboxChanged += DisablePauseAltCheckboxChanged;
_disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged; _disableVoice.CheckboxChanged += DisableVoiceCheckboxChanged;
_flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged; _flipMenuItem.CheckboxChanged += FlipMenuCheckboxChanged;
_menuKey.Activated+= ChaneMenuKey; _menuKey.Activated += ChaneMenuKey;
_passengerKey.Activated+= ChangePassengerKey; _passengerKey.Activated += ChangePassengerKey;
_vehicleSoftLimit.Activated+= VehicleSoftLimitActivated; _vehicleSoftLimit.Activated += VehicleSoftLimitActivated;
_pedSoftLimit.Activated += PedSoftLimitActivated;
_showBlip.Activated += (s, e) =>
{
Main.Settings.ShowPlayerBlip = _showBlip.Checked;
Util.SaveSettings();
};
_showNametag.Activated += (s, e) =>
{
Main.Settings.ShowPlayerNameTag = _showNametag.Checked;
Util.SaveSettings();
};
Menu.Add(_disableTrafficItem); Menu.Add(_disableTrafficItem);
Menu.Add(_disablePauseAlt); Menu.Add(_disablePauseAlt);
@ -43,6 +57,9 @@ namespace RageCoop.Client.Menus
Menu.Add(_menuKey); Menu.Add(_menuKey);
Menu.Add(_passengerKey); Menu.Add(_passengerKey);
Menu.Add(_vehicleSoftLimit); Menu.Add(_vehicleSoftLimit);
Menu.Add(_pedSoftLimit);
Menu.Add(_showBlip);
Menu.Add(_showNametag);
} }
private static void DisableVoiceCheckboxChanged(object sender, EventArgs e) private static void DisableVoiceCheckboxChanged(object sender, EventArgs e)
@ -53,7 +70,9 @@ namespace RageCoop.Client.Menus
{ {
Voice.Init(); Voice.Init();
} }
} else { }
else
{
Voice.ClearAll(); Voice.ClearAll();
} }
@ -63,17 +82,29 @@ namespace RageCoop.Client.Menus
private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e) private static void DisablePauseAltCheckboxChanged(object sender, EventArgs e)
{ {
Main.Settings.DisableAlternatePause=_disablePauseAlt.Checked; Main.Settings.DisableAlternatePause = _disablePauseAlt.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }
private static void VehicleSoftLimitActivated(object sender, EventArgs e) private static void VehicleSoftLimitActivated(object sender, EventArgs e)
{ {
try try
{ {
Main.Settings.WorldVehicleSoftLimit =int.Parse( Main.Settings.WorldVehicleSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.WorldVehicleSoftLimit.ToString(), 20)); Main.Settings.WorldVehicleSoftLimit.ToString(), 20));
_menuKey.AltTitle=Main.Settings.WorldVehicleSoftLimit.ToString(); _vehicleSoftLimit.AltTitle = Main.Settings.WorldVehicleSoftLimit.ToString();
Util.SaveSettings();
}
catch { }
}
private static void PedSoftLimitActivated(object sender, EventArgs e)
{
try
{
Main.Settings.WorldPedSoftLimit = int.Parse(
Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.WorldPedSoftLimit.ToString(), 20));
_pedSoftLimit.AltTitle = Main.Settings.WorldPedSoftLimit.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -82,11 +113,11 @@ namespace RageCoop.Client.Menus
{ {
try try
{ {
Main.Settings.MenuKey =(Keys)Enum.Parse( Main.Settings.MenuKey = (Keys)Enum.Parse(
typeof(Keys), typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.MenuKey.ToString(), 20)); Main.Settings.MenuKey.ToString(), 20));
_menuKey.AltTitle=Main.Settings.MenuKey.ToString(); _menuKey.AltTitle = Main.Settings.MenuKey.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -96,11 +127,11 @@ namespace RageCoop.Client.Menus
{ {
try try
{ {
Main.Settings.PassengerKey =(Keys)Enum.Parse( Main.Settings.PassengerKey = (Keys)Enum.Parse(
typeof(Keys), typeof(Keys),
Game.GetUserInput(WindowTitle.EnterMessage20, Game.GetUserInput(WindowTitle.EnterMessage20,
Main.Settings.PassengerKey.ToString(), 20)); Main.Settings.PassengerKey.ToString(), 20));
_passengerKey.AltTitle=Main.Settings.PassengerKey.ToString(); _passengerKey.AltTitle = Main.Settings.PassengerKey.ToString();
Util.SaveSettings(); Util.SaveSettings();
} }
catch { } catch { }
@ -108,6 +139,7 @@ namespace RageCoop.Client.Menus
public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b) public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{ {
WorldThread.Traffic(!_disableTrafficItem.Checked);
Main.Settings.DisableTraffic = _disableTrafficItem.Checked; Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
Util.SaveSettings(); Util.SaveSettings();
} }

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

View File

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

View File

@ -1,12 +1,10 @@
using System; using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using RageCoop.Core;
using Lidgren.Network;
using System.Net; using System.Net;
using System.Timers;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -15,9 +13,9 @@ namespace RageCoop.Client
static HolePunch() static HolePunch()
{ {
// Periodically send hole punch message as needed // Periodically send hole punch message as needed
var timer=new Timer(1000); var timer = new Timer(1000);
timer.Elapsed+=DoPunch; timer.Elapsed += DoPunch;
timer.Enabled=true; timer.Enabled = true;
} }
private static void DoPunch(object sender, ElapsedEventArgs e) private static void DoPunch(object sender, ElapsedEventArgs e)
@ -27,14 +25,14 @@ namespace RageCoop.Client
if (!Networking.IsOnServer) { return; } if (!Networking.IsOnServer) { return; }
foreach (var p in PlayerList.Players.Values.ToArray()) 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(); var msg = Networking.Peer.CreateMessage();
new Packets.HolePunch new Packets.HolePunch
{ {
Puncher=Main.LocalPlayerID, Puncher = Main.LocalPlayerID,
Status=p.HolePunchStatus Status = p.HolePunchStatus
}.Pack(msg); }.Pack(msg);
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint }); 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) 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"); Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal); player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal); player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
player.ConnectWhenPunched=p.Connect; player.ConnectWhenPunched = p.Connect;
} }
else 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}"); 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); Main.Logger.Debug("Puncher identified as: " + puncher.Username);
puncher.HolePunchStatus=(byte)(p.Status+1); puncher.HolePunchStatus = (byte)(p.Status + 1);
if (p.Status>=3) if (p.Status >= 3)
{ {
Main.Logger.Debug("HolePunch sucess: "+from+", "+puncher.PedID); Main.Logger.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
if (puncher.ConnectWhenPunched && (puncher.Connection==null || puncher.Connection.Status==NetConnectionStatus.Disconnected)) 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(); var msg = Networking.Peer.CreateMessage();
new Packets.P2PConnect { ID=Main.LocalPlayerID }.Pack(msg); new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
puncher.Connection=Networking.Peer.Connect(from,msg); puncher.Connection = Networking.Peer.Connect(from, msg);
Networking.Peer.FlushSendQueue(); Networking.Peer.FlushSendQueue();
} }
} }

View File

@ -1,44 +1,45 @@
using Lidgren.Network; using GTA.UI;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GTA.UI;
using System.Net;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static partial class Networking internal static partial class Networking
{ {
public static CoopPeer Peer; 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 bool ShowNetworkInfo = false;
public static Security Security; public static Security Security;
public static NetConnection ServerConnection; public static NetConnection ServerConnection;
private static readonly Dictionary<int, Action<PacketType, byte[]>> PendingResponses = new Dictionary<int, Action<PacketType, byte[]>>(); private static readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new Dictionary<int, Action<PacketType, NetIncomingMessage>>();
internal static readonly Dictionary<PacketType, Func<byte[], Packet>> RequestHandlers = new Dictionary<PacketType, Func<byte[], Packet>>(); internal static readonly Dictionary<PacketType, Func<NetIncomingMessage, Packet>> RequestHandlers = new Dictionary<PacketType, Func<NetIncomingMessage, Packet>>();
internal static float SimulatedLatency=0; internal static float SimulatedLatency = 0;
public static bool IsConnecting { get; private set; } public static bool IsConnecting { get; private set; }
public static IPEndPoint _targetServerEP; public static IPEndPoint _targetServerEP;
static Networking() 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; Menus.CoopMenu.Menu.Visible = false;
Peer?.Shutdown("Bye"); if (IsConnecting)
if (IsOnServer)
{ {
// ?
}
else if (IsConnecting) {
_publicKeyReceived.Set(); _publicKeyReceived.Set();
IsConnecting = false; 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 else
{ {
@ -46,19 +47,20 @@ namespace RageCoop.Client
IsConnecting = true; IsConnecting = true;
password = password ?? Main.Settings.Password; password = password ?? Main.Settings.Password;
username=username ?? Main.Settings.Username; username = username ?? Main.Settings.Username;
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP // 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0") NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
{ {
AutoFlushSendQueue = false, AutoFlushSendQueue = false,
SimulatedMinimumLatency =SimulatedLatency,
SimulatedRandomLatency=0,
AcceptIncomingConnections = true, AcceptIncomingConnections = true,
MaximumConnections = 32, MaximumConnections = 32,
PingInterval = 5 PingInterval = 5
}; };
#if DEBUG
config.SimulatedMinimumLatency = SimulatedLatency;
config.SimulatedRandomLatency = 0;
#endif
config.EnableMessageType(NetIncomingMessageType.UnconnectedData); config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess); config.EnableMessageType(NetIncomingMessageType.NatIntroductionSuccess);
@ -78,45 +80,54 @@ namespace RageCoop.Client
PlayerList.Cleanup(); PlayerList.Cleanup();
EntityPool.AddPlayer(); EntityPool.AddPlayer();
if (publicKey==null && !string.IsNullOrEmpty(password) && !Menus.CoopMenu.ShowPopUp("", "WARNING", "Server's IP can be spoofed when using direct connection, do you wish to continue?", "", true)) 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; return;
} }
Task.Run(() => Task.Run(() =>
{ {
try try
{ {
_targetServerEP=CoreUtils.StringToEndPoint(address); _targetServerEP = CoreUtils.StringToEndPoint(address);
// Ensure static constructor invocation
DownloadManager.Cleanup(); DownloadManager.Cleanup();
Peer = new CoopPeer(config); Peer = new CoopPeer(config);
Peer.OnMessageReceived+= (s, m) => Peer.OnMessageReceived += (s, m) =>
{ {
try { ProcessMessage(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..."); }); Main.QueueAction(() => { Notification.Show($"~y~Trying to connect..."); });
Menus.CoopMenu._serverConnectItem.Enabled=false; Menus.CoopMenu._serverConnectItem.Enabled = false;
Security.Regen(); Security.Regen();
if(publicKey==null){ if (publicKey == null)
if (!GetServerPublicKey(ip[0],int.Parse(ip[1]))) {
if (!GetServerPublicKey(ip[0], int.Parse(ip[1])))
{ {
Menus.CoopMenu._serverConnectItem.Enabled=true; Menus.CoopMenu._serverConnectItem.Enabled = true;
throw new TimeoutException("Failed to retrive server's public key"); throw new TimeoutException("Failed to retrive server's public key");
} }
} }
else{ else
Security.SetServerPublicKey(publicKey.Modulus,publicKey.Exponent); {
Security.SetServerPublicKey(publicKey.Modulus, publicKey.Exponent);
} }
// Send handshake packet // Send handshake packet
NetOutgoingMessage outgoingMessage = Peer.CreateMessage(); NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
var handshake = new Packets.Handshake() var handshake = new Packets.Handshake()
{ {
PedID = Main.LocalPlayerID, PedID = Main.LocalPlayerID,
Username =username, Username = username,
ModVersion = Main.Version.ToString(), 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) InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]), Peer.Port)
}; };
@ -128,9 +139,9 @@ namespace RageCoop.Client
catch (Exception ex) catch (Exception ex)
{ {
Main.Logger.Error("Cannot connect to server: ", 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 var p = new Player
{ {
PedID = packet.PedID, ID = packet.PedID,
Username= packet.Username, Username = packet.Username,
}; };
PlayerList.SetPlayer(packet.PedID, packet.Username); PlayerList.SetPlayer(packet.PedID, packet.Username);
@ -153,9 +164,10 @@ namespace RageCoop.Client
private static void PlayerDisconnect(Packets.PlayerDisconnect packet) private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{ {
var player = PlayerList.GetPlayer(packet.PedID); var player = PlayerList.GetPlayer(packet.PedID);
if (player==null) { return; } if (player == null) { return; }
PlayerList.RemovePlayer(packet.PedID); PlayerList.RemovePlayer(packet.PedID);
Main.QueueAction(() => { Main.QueueAction(() =>
{
EntityPool.RemoveAllFromPlayer(packet.PedID); EntityPool.RemoveAllFromPlayer(packet.PedID);
GTA.UI.Notification.Show($"~h~{player.Username}~h~ left."); GTA.UI.Notification.Show($"~h~{player.Username}~h~ left.");
}); });
@ -163,13 +175,13 @@ namespace RageCoop.Client
#endregion // -- PLAYER -- #endregion // -- PLAYER --
#region -- GET -- #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(); var msg = Peer.CreateMessage();
new Packets.PublicKeyRequest().Pack(msg); new Packets.PublicKeyRequest().Pack(msg);
Peer.SendUnconnectedMessage(msg, host, port); 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() 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((byte)PacketType.Request);
msg.Write(id); msg.Write(id);
request.Pack(msg); request.Pack(msg);
Peer.SendMessage(msg,ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel); Peer.SendMessage(msg, ServerConnection, NetDeliveryMethod.ReliableOrdered, (int)channel);
} }
#endregion #endregion
private static int NewRequestID() private static int NewRequestID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) || PendingResponses.ContainsKey(ID)) while ((ID == 0) || PendingResponses.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];

View File

@ -3,7 +3,7 @@ using Lidgren.Network;
using RageCoop.Client.Menus; using RageCoop.Client.Menus;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Linq;
using System.Threading; using System.Threading;
namespace RageCoop.Client namespace RageCoop.Client
@ -14,7 +14,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Reduce GC pressure by reusing frequently used packets /// Reduce GC pressure by reusing frequently used packets
/// </summary> /// </summary>
static class ReceivedPackets private static class ReceivedPackets
{ {
public static Packets.PedSync PedPacket = new Packets.PedSync(); public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync(); public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -24,7 +24,7 @@ namespace RageCoop.Client
/// <summary> /// <summary>
/// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/> /// Used to reslove entity handle in a <see cref="Packets.CustomEvent"/>
/// </summary> /// </summary>
private static readonly Func<byte, BitReader, object> _resolveHandle = (t, reader) => private static readonly Func<byte, NetIncomingMessage, object> _resolveHandle = (t, reader) =>
{ {
switch (t) switch (t)
{ {
@ -37,14 +37,15 @@ namespace RageCoop.Client
case 60: case 60:
return EntityPool.ServerBlips[reader.ReadInt32()].Handle; return EntityPool.ServerBlips[reader.ReadInt32()].Handle;
default: default:
throw new ArgumentException("Cannot resolve server side argument: "+t); throw new ArgumentException("Cannot resolve server side argument: " + t);
} }
}; };
private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false); private static readonly AutoResetEvent _publicKeyReceived = new AutoResetEvent(false);
private static bool _recycle;
public static void ProcessMessage(NetIncomingMessage message) public static void ProcessMessage(NetIncomingMessage message)
{ {
if (message == null) { return; } if (message == null) { return; }
_recycle = true;
switch (message.MessageType) switch (message.MessageType)
{ {
case NetIncomingMessageType.StatusChanged: case NetIncomingMessageType.StatusChanged:
@ -53,48 +54,36 @@ namespace RageCoop.Client
switch (status) switch (status)
{ {
case NetConnectionStatus.InitiatedConnect: case NetConnectionStatus.InitiatedConnect:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
CoopMenu.InitiateConnectionMenuSetting(); CoopMenu.InitiateConnectionMenuSetting();
} }
break; break;
case NetConnectionStatus.Connected: case NetConnectionStatus.Connected:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
Memory.ApplyPatches();
var response = message.SenderConnection.RemoteHailMessage; var response = message.SenderConnection.RemoteHailMessage;
if ((PacketType)response.ReadByte()!=PacketType.HandshakeSuccess) if ((PacketType)response.ReadByte() != PacketType.HandshakeSuccess)
{ {
throw new Exception("Invalid handshake response!"); throw new Exception("Invalid handshake response!");
} }
var p = new Packets.HandshakeSuccess(); var p = new Packets.HandshakeSuccess();
p.Deserialize(response.ReadBytes(response.ReadInt32())); p.Deserialize(response);
foreach(var player in p.Players) foreach (var player in p.Players)
{ {
PlayerList.SetPlayer(player.ID,player.Username); PlayerList.SetPlayer(player.ID, player.Username);
} }
Main.QueueAction(() => Main.Connected();
{
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
if (Main.Settings.Voice && !Voice.WasInitialized())
{
Voice.Init();
}
GTA.UI.Notification.Show("~g~Connected!");
});
Main.Logger.Info(">> Connected <<");
} }
else else
{ {
// Self-initiated connection // Self-initiated connection
if (message.SenderConnection.RemoteHailMessage==null) { return; } if (message.SenderConnection.RemoteHailMessage == null) { return; }
var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>(); 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"); Main.Logger.Debug($"Direct connection to {player.Username} established");
} }
else else
@ -105,28 +94,16 @@ namespace RageCoop.Client
} }
break; break;
case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnected:
if (message.SenderConnection==ServerConnection) if (message.SenderConnection == ServerConnection)
{ {
Memory.RestorePatches(); Main.Disconnected(reason);
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();
} }
break; break;
} }
break; break;
case NetIncomingMessageType.Data: case NetIncomingMessageType.Data:
{ {
if (message.LengthBytes==0) { break; } if (message.LengthBytes == 0) { break; }
var packetType = PacketType.Unknown; var packetType = PacketType.Unknown;
try try
{ {
@ -139,7 +116,7 @@ namespace RageCoop.Client
int id = message.ReadInt32(); int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback)) if (PendingResponses.TryGetValue(id, out var callback))
{ {
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32())); callback((PacketType)message.ReadByte(), message);
PendingResponses.Remove(id); PendingResponses.Remove(id);
} }
break; break;
@ -148,57 +125,60 @@ namespace RageCoop.Client
{ {
int id = message.ReadInt32(); int id = message.ReadInt32();
var realType = (PacketType)message.ReadByte(); var realType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
if (RequestHandlers.TryGetValue(realType, out var handler)) if (RequestHandlers.TryGetValue(realType, out var handler))
{ {
var response = Peer.CreateMessage(); var response = Peer.CreateMessage();
response.Write((byte)PacketType.Response); response.Write((byte)PacketType.Response);
response.Write(id); response.Write(id);
handler(message.ReadBytes(len)).Pack(response); handler(message).Pack(response);
Peer.SendMessage(response,ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel); Peer.SendMessage(response, ServerConnection, NetDeliveryMethod.ReliableOrdered, message.SequenceChannel);
Peer.FlushSendQueue(); Peer.FlushSendQueue();
} }
else
{
Main.Logger.Debug("Did not find a request handler of type: " + realType);
}
break; break;
} }
default: default:
{ {
byte[] data = message.ReadBytes(message.ReadInt32());
HandlePacket(packetType, data,message.SenderConnection); HandlePacket(packetType, message, message.SenderConnection, ref _recycle);
break; break;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
#if DEBUG
Main.QueueAction(() => Main.QueueAction(() =>
{ {
GTA.UI.Notification.Show("~r~~h~Packet Error"); GTA.UI.Notification.Show($"~r~~h~Packet Error {ex.Message}");
return true; return true;
}); });
Main.Logger.Error($"[{packetType}] {ex.Message}"); Main.Logger.Error($"[{packetType}] {ex.Message}");
Main.Logger.Error(ex); Main.Logger.Error(ex);
Peer.Shutdown($"Packet Error [{packetType}]"); Peer.Shutdown($"Packet Error [{packetType}]");
#endif
_recycle = false;
} }
break; break;
} }
case NetIncomingMessageType.UnconnectedData: case NetIncomingMessageType.UnconnectedData:
{ {
var packetType = (PacketType)message.ReadByte(); var packetType = (PacketType)message.ReadByte();
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
switch (packetType) switch (packetType)
{ {
case PacketType.HolePunch: case PacketType.HolePunch:
{ {
HolePunch.Punched(data.GetPacket<Packets.HolePunch>(), message.SenderEndPoint); HolePunch.Punched(message.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
break; break;
} }
case PacketType.PublicKeyResponse: case PacketType.PublicKeyResponse:
{ {
if(message.SenderEndPoint.ToString()!=_targetServerEP.ToString() ||!IsConnecting){break;} if (message.SenderEndPoint.ToString() != _targetServerEP.ToString() || !IsConnecting) { break; }
var packet = data.GetPacket<Packets.PublicKeyResponse>(); var packet = message.GetPacket<Packets.PublicKeyResponse>();
Security.SetServerPublicKey(packet.Modulus, packet.Exponent); Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
_publicKeyReceived.Set(); _publicKeyReceived.Set();
break; break;
@ -215,41 +195,42 @@ namespace RageCoop.Client
default: default:
break; break;
} }
if (_recycle)
Peer.Recycle(message); {
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) switch (packetType)
{ {
case PacketType.HolePunchInit: case PacketType.HolePunchInit:
HolePunch.Add(data.GetPacket<Packets.HolePunchInit>()); HolePunch.Add(msg.GetPacket<Packets.HolePunchInit>());
break; break;
case PacketType.PlayerConnect: case PacketType.PlayerConnect:
PlayerConnect(data.GetPacket<Packets.PlayerConnect>()); PlayerConnect(msg.GetPacket<Packets.PlayerConnect>());
break; break;
case PacketType.PlayerDisconnect: case PacketType.PlayerDisconnect:
PlayerDisconnect(data.GetPacket<Packets.PlayerDisconnect>()); PlayerDisconnect(msg.GetPacket<Packets.PlayerDisconnect>());
break; break;
case PacketType.PlayerInfoUpdate: case PacketType.PlayerInfoUpdate:
PlayerList.UpdatePlayer(data.GetPacket<Packets.PlayerInfoUpdate>()); PlayerList.UpdatePlayer(msg.GetPacket<Packets.PlayerInfoUpdate>());
break; break;
case PacketType.VehicleSync: case PacketType.VehicleSync:
ReceivedPackets.VehicelPacket.Deserialize(data); ReceivedPackets.VehicelPacket.Deserialize(msg);
VehicleSync(ReceivedPackets.VehicelPacket); VehicleSync(ReceivedPackets.VehicelPacket);
break; break;
case PacketType.PedSync: case PacketType.PedSync:
ReceivedPackets.PedPacket.Deserialize(data); ReceivedPackets.PedPacket.Deserialize(msg);
PedSync(ReceivedPackets.PedPacket); PedSync(ReceivedPackets.PedPacket);
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
ReceivedPackets.ProjectilePacket.Deserialize(data); ReceivedPackets.ProjectilePacket.Deserialize(msg);
ProjectileSync(ReceivedPackets.ProjectilePacket); ProjectileSync(ReceivedPackets.ProjectilePacket);
break; break;
@ -257,7 +238,7 @@ namespace RageCoop.Client
{ {
Packets.ChatMessage packet = new Packets.ChatMessage((b) => Security.Decrypt(b)); 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; }); Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
} }
@ -268,7 +249,7 @@ namespace RageCoop.Client
if (Main.Settings.Voice) if (Main.Settings.Voice)
{ {
Packets.Voice packet = new Packets.Voice(); Packets.Voice packet = new Packets.Voice();
packet.Deserialize(data); packet.Deserialize(msg);
SyncedPed player = EntityPool.GetPedByID(packet.ID); SyncedPed player = EntityPool.GetPedByID(packet.ID);
@ -283,18 +264,20 @@ namespace RageCoop.Client
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
packet.Deserialize(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
} }
break; break;
case PacketType.CustomEventQueued: case PacketType.CustomEventQueued:
{ {
recycle = false;
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
Main.QueueAction(() => Main.QueueAction(() =>
{ {
packet.Deserialize(data); packet.Deserialize(msg);
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
Peer.Recycle(msg);
}); });
} }
break; break;
@ -302,7 +285,7 @@ namespace RageCoop.Client
case PacketType.FileTransferChunk: case PacketType.FileTransferChunk:
{ {
Packets.FileTransferChunk packet = new Packets.FileTransferChunk(); Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
packet.Deserialize(data); packet.Deserialize(msg);
DownloadManager.Write(packet.ID, packet.FileChunk); DownloadManager.Write(packet.ID, packet.FileChunk);
} }
break; break;
@ -310,8 +293,9 @@ namespace RageCoop.Client
default: default:
if (packetType.IsSyncEvent()) if (packetType.IsSyncEvent())
{ {
recycle = false;
// Dispatch to script thread // Dispatch to script thread
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; }); Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, msg); return true; });
} }
break; break;
} }
@ -320,33 +304,38 @@ namespace RageCoop.Client
private static void PedSync(Packets.PedSync packet) private static void PedSync(Packets.PedSync packet)
{ {
SyncedPed c = EntityPool.GetPedByID(packet.ID); SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null) if (c == null)
{ {
// Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}"); if (EntityPool.PedsByID.Count(x => x.Value.OwnerID == packet.OwnerID) < Main.Settings.WorldPedSoftLimit / PlayerList.Players.Count ||
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID)); 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.ID=packet.ID; c.OwnerID = packet.OwnerID;
c.OwnerID=packet.OwnerID;
c.Health = packet.Health; c.Health = packet.Health;
c.Rotation = packet.Rotation; c.Rotation = packet.Rotation;
c.Velocity = packet.Velocity; c.Velocity = packet.Velocity;
c.Speed = packet.Speed; c.Speed = packet.Speed;
c.Flags=packet.Flags; c.Flags = packet.Flags;
c.Heading=packet.Heading; c.Heading = packet.Heading;
c.Position = packet.Position; c.Position = packet.Position;
c.LastSyncedStopWatch.Restart();
if (c.IsRagdoll) if (c.IsRagdoll)
{ {
c.HeadPosition=packet.HeadPosition; c.HeadPosition = packet.HeadPosition;
c.RightFootPosition=packet.RightFootPosition; c.RightFootPosition = packet.RightFootPosition;
c.LeftFootPosition=packet.LeftFootPosition; c.LeftFootPosition = packet.LeftFootPosition;
} }
else if (c.Speed>=4) else if (c.Speed >= 4)
{ {
c.VehicleID=packet.VehicleID; c.VehicleID = packet.VehicleID;
c.Seat=packet.Seat; c.Seat = packet.Seat;
} }
c.LastSynced = Main.Ticked; c.LastSynced = Main.Ticked;
if (c.IsAiming) if (c.IsAiming)
{ {
c.AimCoords = packet.AimCoords; c.AimCoords = packet.AimCoords;
@ -354,13 +343,13 @@ namespace RageCoop.Client
if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync)) if (packet.Flags.HasPedFlag(PedDataFlags.IsFullSync))
{ {
c.CurrentWeaponHash = packet.CurrentWeaponHash; c.CurrentWeaponHash = packet.CurrentWeaponHash;
c.Clothes=packet.Clothes; c.Clothes = packet.Clothes;
c.WeaponComponents=packet.WeaponComponents; c.WeaponComponents = packet.WeaponComponents;
c.WeaponTint=packet.WeaponTint; c.WeaponTint = packet.WeaponTint;
c.Model=packet.ModelHash; c.Model = packet.ModelHash;
c.BlipColor=packet.BlipColor; c.BlipColor = packet.BlipColor;
c.BlipSprite=packet.BlipSprite; c.BlipSprite = packet.BlipSprite;
c.BlipScale=packet.BlipScale; c.BlipScale = packet.BlipScale;
c.LastFullSynced = Main.Ticked; c.LastFullSynced = Main.Ticked;
} }
@ -368,58 +357,64 @@ namespace RageCoop.Client
private static void VehicleSync(Packets.VehicleSync packet) private static void VehicleSync(Packets.VehicleSync packet)
{ {
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID); SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null) if (v == null)
{ {
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; } if (v.IsLocal) { return; }
v.ID= packet.ID; v.ID = packet.ID;
v.OwnerID= packet.OwnerID; v.OwnerID = packet.OwnerID;
v.Flags=packet.Flags; v.Flags = packet.Flags;
v.Position=packet.Position; v.Position = packet.Position;
v.Quaternion=packet.Quaternion; v.Quaternion = packet.Quaternion;
v.SteeringAngle=packet.SteeringAngle; v.SteeringAngle = packet.SteeringAngle;
v.ThrottlePower=packet.ThrottlePower; v.ThrottlePower = packet.ThrottlePower;
v.BrakePower=packet.BrakePower; v.BrakePower = packet.BrakePower;
v.Velocity=packet.Velocity; v.Velocity = packet.Velocity;
v.RotationVelocity=packet.RotationVelocity; v.RotationVelocity = packet.RotationVelocity;
v.DeluxoWingRatio=packet.DeluxoWingRatio; v.DeluxoWingRatio = packet.DeluxoWingRatio;
v.LastSynced=Main.Ticked; v.LastSynced = Main.Ticked;
v.LastSyncedStopWatch.Restart(); v.LastSyncedStopWatch.Restart();
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync)) if (packet.Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{ {
v.DamageModel=packet.DamageModel; v.DamageModel = packet.DamageModel;
v.EngineHealth=packet.EngineHealth; v.EngineHealth = packet.EngineHealth;
v.Mods=packet.Mods; v.Mods = packet.Mods;
v.Model=packet.ModelHash; v.Model = packet.ModelHash;
v.Colors=packet.Colors; v.Colors = packet.Colors;
v.LandingGear=packet.LandingGear; v.LandingGear = packet.LandingGear;
v.RoofState=(VehicleRoofState)packet.RoofState; v.RoofState = (VehicleRoofState)packet.RoofState;
v.LockStatus=packet.LockStatus; v.LockStatus = packet.LockStatus;
v.RadioStation=packet.RadioStation; v.RadioStation = packet.RadioStation;
v.LicensePlate=packet.LicensePlate; v.LicensePlate = packet.LicensePlate;
v.Livery=packet.Livery; v.Livery = packet.Livery;
v.LastFullSynced= Main.Ticked; v.LastFullSynced = Main.Ticked;
} }
} }
private static void ProjectileSync(Packets.ProjectileSync packet) private static void ProjectileSync(Packets.ProjectileSync packet)
{ {
var p = EntityPool.GetProjectileByID(packet.ID); var p = EntityPool.GetProjectileByID(packet.ID);
if (p==null) if (p == null)
{ {
if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; } if (packet.Flags.HasProjDataFlag(ProjectileDataFlags.Exploded)) { return; }
// Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}"); // Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
EntityPool.ThreadSafe.Add(p=new SyncedProjectile(packet.ID)); EntityPool.ThreadSafe.Add(p = new SyncedProjectile(packet.ID));
} }
p.Flags=packet.Flags; p.Flags = packet.Flags;
p.Position=packet.Position; p.Position = packet.Position;
p.Rotation=packet.Rotation; p.Rotation = packet.Rotation;
p.Velocity=packet.Velocity; p.Velocity = packet.Velocity;
p.WeaponHash=(WeaponHash)packet.WeaponHash; p.WeaponHash = (WeaponHash)packet.WeaponHash;
p.Shooter= packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ? p.Shooter = packet.Flags.HasProjDataFlag(ProjectileDataFlags.IsShotByVehicle) ?
(SyncedEntity)EntityPool.GetVehicleByID(packet.ShooterID) : EntityPool.GetPedByID(packet.ShooterID); (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> /// <summary>
/// Reduce GC pressure by reusing frequently used packets /// Reduce GC pressure by reusing frequently used packets
/// </summary> /// </summary>
static class SendPackets private static class SendPackets
{ {
public static Packets.PedSync PedPacket = new Packets.PedSync(); public static Packets.PedSync PedPacket = new Packets.PedSync();
public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync(); public static Packets.VehicleSync VehicelPacket = new Packets.VehicleSync();
@ -28,50 +28,50 @@ namespace RageCoop.Client
public static void SendPed(SyncedPed sp, bool full) public static void SendPed(SyncedPed sp, bool full)
{ {
if (sp.LastSentStopWatch.ElapsedMilliseconds<SyncInterval) if (sp.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{ {
return; return;
} }
Ped ped = sp.MainPed; Ped ped = sp.MainPed;
var p = SendPackets.PedPacket; var p = SendPackets.PedPacket;
p.ID =sp.ID; p.ID = sp.ID;
p.OwnerID=sp.OwnerID; p.OwnerID = sp.OwnerID;
p.Health = ped.Health; p.Health = ped.Health;
p.Rotation = ped.ReadRotation(); p.Rotation = ped.ReadRotation();
p.Velocity = ped.ReadVelocity(); p.Velocity = ped.ReadVelocity();
p.Speed = ped.GetPedSpeed(); p.Speed = ped.GetPedSpeed();
p.Flags = ped.GetPedFlags(); p.Flags = ped.GetPedFlags();
p.Heading=ped.Heading; p.Heading = ped.Heading;
if (p.Flags.HasPedFlag(PedDataFlags.IsAiming)) if (p.Flags.HasPedFlag(PedDataFlags.IsAiming))
{ {
p.AimCoords = ped.GetAimCoord(); p.AimCoords = ped.GetAimCoord();
} }
if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll)) if (p.Flags.HasPedFlag(PedDataFlags.IsRagdoll))
{ {
p.HeadPosition=ped.Bones[Bone.SkelHead].Position; p.HeadPosition = ped.Bones[Bone.SkelHead].Position;
p.RightFootPosition=ped.Bones[Bone.SkelRightFoot].Position; p.RightFootPosition = ped.Bones[Bone.SkelRightFoot].Position;
p.LeftFootPosition=ped.Bones[Bone.SkelLeftFoot].Position; p.LeftFootPosition = ped.Bones[Bone.SkelLeftFoot].Position;
} }
else else
{ {
// Seat sync // Seat sync
if (p.Speed>=4) if (p.Speed >= 4)
{ {
var veh = ped.CurrentVehicle?.GetSyncEntity() ?? ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity(); var veh = ped.CurrentVehicle?.GetSyncEntity() ?? ped.VehicleTryingToEnter?.GetSyncEntity() ?? ped.LastVehicle?.GetSyncEntity();
p.VehicleID = veh?.ID ?? 0; p.VehicleID = veh?.ID ?? 0;
if (p.VehicleID==0) { Main.Logger.Error("Invalid vehicle"); } if (p.VehicleID == 0) { Main.Logger.Error("Invalid vehicle"); }
if (p.Speed==5) if (p.Speed == 5)
{ {
p.Seat=ped.GetSeatTryingToEnter(); p.Seat = ped.GetSeatTryingToEnter();
} }
else 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; veh.OwnerID = Main.LocalPlayerID;
SyncEvents.TriggerChangeOwner(veh.ID,Main.LocalPlayerID); SyncEvents.TriggerChangeOwner(veh.ID, Main.LocalPlayerID);
} }
} }
p.Position = ped.ReadPosition(); p.Position = ped.ReadPosition();
@ -80,57 +80,57 @@ namespace RageCoop.Client
if (full) if (full)
{ {
var w = ped.VehicleWeapon; 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.Flags |= PedDataFlags.IsFullSync;
p.Clothes=ped.GetPedClothes(); p.Clothes = ped.GetPedClothes();
p.ModelHash=ped.Model.Hash; p.ModelHash = ped.Model.Hash;
p.WeaponComponents=ped.Weapons.Current.GetWeaponComponents(); p.WeaponComponents = ped.Weapons.Current.GetWeaponComponents();
p.WeaponTint=(byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash); p.WeaponTint = (byte)Function.Call<int>(Hash.GET_PED_WEAPON_TINT_INDEX, ped, ped.Weapons.Current.Hash);
Blip b; Blip b;
if (sp.IsPlayer) if (sp.IsPlayer)
{ {
p.BlipColor=Scripting.API.Config.BlipColor; p.BlipColor = Scripting.API.Config.BlipColor;
p.BlipSprite=Scripting.API.Config.BlipSprite; p.BlipSprite = Scripting.API.Config.BlipSprite;
p.BlipScale=Scripting.API.Config.BlipScale; p.BlipScale = Scripting.API.Config.BlipScale;
} }
else if ((b = ped.AttachedBlip) !=null) else if ((b = ped.AttachedBlip) != null)
{ {
p.BlipColor=b.Color; p.BlipColor = b.Color;
p.BlipSprite=b.Sprite; 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 else
{ {
p.BlipColor=(BlipColor)255; p.BlipColor = (BlipColor)255;
} }
} }
SendSync(p, ConnectionChannel.PedSync); SendSync(p, ConnectionChannel.PedSync);
} }
public static void SendVehicle(SyncedVehicle v, bool full) public static void SendVehicle(SyncedVehicle v, bool full)
{ {
if (v.LastSentStopWatch.ElapsedMilliseconds<SyncInterval) if (v.LastSentStopWatch.ElapsedMilliseconds < SyncInterval)
{ {
return; return;
} }
Vehicle veh = v.MainVehicle; Vehicle veh = v.MainVehicle;
var packet = SendPackets.VehicelPacket; var packet = SendPackets.VehicelPacket;
packet.ID =v.ID; packet.ID = v.ID;
packet.OwnerID=v.OwnerID; packet.OwnerID = v.OwnerID;
packet.Flags = v.GetVehicleFlags(); packet.Flags = v.GetVehicleFlags();
packet.SteeringAngle = veh.SteeringAngle; packet.SteeringAngle = veh.SteeringAngle;
packet.Position = veh.ReadPosition(); packet.Position = veh.ReadPosition();
packet.Velocity=veh.Velocity; packet.Velocity = veh.Velocity;
packet.Quaternion=veh.ReadQuaternion(); packet.Quaternion = veh.ReadQuaternion();
packet.RotationVelocity=veh.RotationVelocity; packet.RotationVelocity = veh.RotationVelocity;
packet.ThrottlePower = veh.ThrottlePower; packet.ThrottlePower = veh.ThrottlePower;
packet.BrakePower = veh.BrakePower; packet.BrakePower = veh.BrakePower;
v.LastSentStopWatch.Restart(); 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) if (full)
{ {
byte primaryColor = 0; byte primaryColor = 0;
@ -141,24 +141,24 @@ namespace RageCoop.Client
} }
packet.Flags |= VehicleDataFlags.IsFullSync; packet.Flags |= VehicleDataFlags.IsFullSync;
packet.Colors = new byte[] { primaryColor, secondaryColor }; packet.Colors = new byte[] { primaryColor, secondaryColor };
packet.DamageModel=veh.GetVehicleDamageModel(); packet.DamageModel = veh.GetVehicleDamageModel();
packet.LandingGear = veh.IsAircraft ? (byte)veh.LandingGearState : (byte)0; 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.Mods = veh.Mods.GetVehicleMods();
packet.ModelHash=veh.Model.Hash; packet.ModelHash = veh.Model.Hash;
packet.EngineHealth=veh.EngineHealth; packet.EngineHealth = veh.EngineHealth;
packet.LockStatus=veh.LockStatus; packet.LockStatus = veh.LockStatus;
packet.LicensePlate=Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh); packet.LicensePlate = Function.Call<string>(Hash.GET_VEHICLE_NUMBER_PLATE_TEXT, veh);
packet.Livery=Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh); packet.Livery = Function.Call<int>(Hash.GET_VEHICLE_LIVERY, veh);
if (v.MainVehicle==Game.Player.LastVehicle) if (v.MainVehicle == Game.Player.LastVehicle)
{ {
packet.RadioStation=Util.GetPlayerRadioIndex(); packet.RadioStation = Util.GetPlayerRadioIndex();
} }
if (packet.EngineHealth>v.LastEngineHealth) if (packet.EngineHealth > v.LastEngineHealth)
{ {
packet.Flags |= VehicleDataFlags.Repaired; packet.Flags |= VehicleDataFlags.Repaired;
} }
v.LastEngineHealth=packet.EngineHealth; v.LastEngineHealth = packet.EngineHealth;
} }
SendSync(packet, ConnectionChannel.VehicleSync); SendSync(packet, ConnectionChannel.VehicleSync);
} }
@ -178,25 +178,25 @@ namespace RageCoop.Client
StartPosition = start, StartPosition = start,
EndPosition = end, EndPosition = end,
OwnerID = ownerID, OwnerID = ownerID,
WeaponHash=weapon, WeaponHash = weapon,
}, ConnectionChannel.SyncEvents); }, 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 SendSync(new Packets.VehicleBulletShot
{ {
StartPosition = b.Position, StartPosition = b.Position,
EndPosition = b.Position+b.ForwardVector, EndPosition = b.Position + b.ForwardVector,
OwnerID=owner.ID, OwnerID = owner.ID,
Bone=(ushort)b.Index, Bone = (ushort)b.Index,
WeaponHash=hash WeaponHash = hash
}); });
} }
#endregion #endregion
public static void SendChatMessage(string message) public static void SendChatMessage(string message)
{ {
Peer.SendTo(new Packets.ChatMessage(new Func<string, byte[]>((s) => Security.Encrypt(s.GetBytes()))) 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(); Peer.FlushSendQueue();
} }
public static void SendVoiceMessage(byte[] buffer, int recorded) public static void SendVoiceMessage(byte[] buffer, int recorded)

View File

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

View File

@ -1,11 +1,11 @@
using GTA; using GTA;
using GTA.Math; using GTA.Math;
using GTA.Native; using GTA.Native;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System.Collections.Generic; using System.Collections.Generic;
using Lidgren.Network;
using System.Net;
using System.Linq; using System.Linq;
using System.Net;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -50,11 +50,11 @@ namespace RageCoop.Client
_mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0); _mainScaleform.CallFunction("SET_DATA_SLOT_EMPTY", 0);
int i=0; int i = 0;
foreach (var player in Players.Values) 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"); _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) public static void SetPlayer(int id, string username, float latency = 0)
{ {
Main.Logger.Debug($"{id},{username},{latency}"); Main.Logger.Debug($"{id},{username},{latency}");
Player p; if (Players.TryGetValue(id, out Player p))
if (Players.TryGetValue(id, out p))
{ {
p.Username=username; p.Username = username;
p.PedID=id; p.ID = id;
p._latencyToServer=latency; p._latencyToServer = latency;
} }
else else
{ {
p = new Player { PedID=id, Username=username, _latencyToServer=latency }; p = new Player { ID = id, Username = username, _latencyToServer = latency };
Players.Add(id, p); Players.Add(id, p);
} }
} }
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet) public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
{ {
var p = GetPlayer(packet.PedID); var p = GetPlayer(packet.PedID);
if (p!=null) if (p != null)
{ {
p._latencyToServer = packet.Latency; p._latencyToServer = packet.Latency;
p.Position = packet.Position; p.Position = packet.Position;
p.IsHost = packet.IsHost;
Main.QueueAction(() => 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.DisplayType = BlipDisplayType.NoDisplay;
p.FakeBlip.Scale=Scripting.API.Config.BlipScale;
p.FakeBlip.Sprite=Scripting.API.Config.BlipSprite;
p.FakeBlip.DisplayType=BlipDisplayType.Default;
p.FakeBlip.Position=p.Position;
} }
else 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) public static Player GetPlayer(int id)
{ {
Player p; Players.TryGetValue(id, out Player p);
Players.TryGetValue(id, out p);
return p; return p;
} }
public static Player GetPlayer(SyncedPed p) public static Player GetPlayer(SyncedPed p)
{ {
var player = GetPlayer(p.ID); var player = GetPlayer(p.ID);
if (player!=null) if (player != null)
{ {
player.Character=p; player.Character = p;
} }
return player; return player;
} }
public static void RemovePlayer(int id) public static void RemovePlayer(int id)
{ {
if (Players.TryGetValue(id,out var player)) if (Players.TryGetValue(id, out var player))
{ {
Players.Remove(id); Players.Remove(id);
Main.QueueAction(() => player.FakeBlip?.Delete()); Main.QueueAction(() => player.FakeBlip?.Delete());
@ -131,39 +129,40 @@ namespace RageCoop.Client
} }
public static void Cleanup() public static void Cleanup()
{ {
foreach(var p in Players.Values.ToArray()) foreach (var p in Players.Values.ToArray())
{ {
p.FakeBlip?.Delete(); 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; } public string Username { get; internal set; }
/// <summary> /// <summary>
/// Universal character ID. /// Universal ped ID.
/// </summary> /// </summary>
public int PedID public int ID
{ {
get; internal set; get; internal set;
} }
public IPEndPoint InternalEndPoint { get; set; } public IPEndPoint InternalEndPoint { get; internal set; }
public IPEndPoint ExternalEndPoint { get; set; } public IPEndPoint ExternalEndPoint { get; internal set; }
public bool ConnectWhenPunched { get; set; } internal bool ConnectWhenPunched { get; set; }
public Blip FakeBlip { get; set; } public Blip FakeBlip { get; internal set; }
public Vector3 Position { get; set; } public Vector3 Position { get; internal set; }
public SyncedPed Character { get; set; } public SyncedPed Character { get; internal set; }
/// <summary> /// <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> /// </summary>
public float Ping => Main.LocalPlayerID==PedID ? Networking.Latency*2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer*2); public float Ping => Main.LocalPlayerID == ID ? Networking.Latency * 2 : (HasDirectConnection ? Connection.AverageRoundtripTime : _latencyToServer * 2);
public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime/2 : Networking.Latency+_latencyToServer; public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime / 2 : Networking.Latency + _latencyToServer;
public float _latencyToServer = 0; internal float _latencyToServer = 0;
public bool DisplayNameTag { get; set; } = true; public bool DisplayNameTag { get; set; } = true;
public NetConnection Connection { get; set; } public NetConnection Connection { get; internal set; }
public bool HasDirectConnection => Connection?.Status==NetConnectionStatus.Connected; public bool HasDirectConnection => Connection?.Status == NetConnectionStatus.Connected;
} }
} }

View File

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

View File

@ -21,6 +21,7 @@
<OutPutPath>..\bin\Debug\Client</OutPutPath> <OutPutPath>..\bin\Debug\Client</OutPutPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>DEBUG</DefineConstants> <DefineConstants>DEBUG</DefineConstants>
<WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutPutPath>..\bin\Release\Client</OutPutPath> <OutPutPath>..\bin\Release\Client</OutPutPath>
@ -35,7 +36,6 @@
<Compile Include="Menus\Sub\DevToolMenu.cs" /> <Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" /> <Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" /> <Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" /> <Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" /> <Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" /> <Compile Include="Networking\HolePunch.cs" />
@ -55,16 +55,18 @@
<Compile Include="Scripting\Resources.cs" /> <Compile Include="Scripting\Resources.cs" />
<Compile Include="Security.cs" /> <Compile Include="Security.cs" />
<Compile Include="Settings.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\Ped\SyncedPed.Animations.cs" />
<Compile Include="Sync\Entities\SyncedEntity.cs" /> <Compile Include="Sync\Entities\SyncedEntity.cs" />
<Compile Include="Sync\Entities\Ped\SyncedPed.cs" /> <Compile Include="Sync\Entities\Ped\SyncedPed.cs" />
<Compile Include="Sync\Entities\SyncedProjectile.cs" /> <Compile Include="Sync\Entities\SyncedProjectile.cs" />
<Compile Include="Sync\Entities\SyncedProp.cs" /> <Compile Include="Sync\Entities\SyncedProp.cs" />
<Compile Include="Sync\Entities\SyncedVehicle.cs" /> <Compile Include="Sync\Entities\Vehicle\SyncedVehicle.cs" />
<Compile Include="Sync\Entities\SyncedVehicle.Members.cs" /> <Compile Include="Sync\Entities\Vehicle\SyncedVehicle.Members.cs" />
<Compile Include="Sync\EntityPool.cs" /> <Compile Include="Sync\EntityPool.cs" />
<Compile Include="Sync\SyncEvents.cs" /> <Compile Include="Sync\SyncEvents.cs" />
<Compile Include="Sync\Voice.cs" /> <Compile Include="Sync\Voice.cs" />
<Compile Include="Util\AddOnDataProvider.cs" />
<Compile Include="Util\Memory.cs" /> <Compile Include="Util\Memory.cs" />
<Compile Include="Util\NativeCaller.cs" /> <Compile Include="Util\NativeCaller.cs" />
<Compile Include="Util\PedConfigFlags.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"> <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> <HintPath>..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference> </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> <SpecificVersion>False</SpecificVersion>
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath> <HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
</Reference> </Reference>

View File

@ -1,5 +1,6 @@
#undef DEBUG #undef DEBUG
using GTA; using GTA;
using Newtonsoft.Json;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -39,7 +40,7 @@ namespace RageCoop.Client.Scripting
/// </summary> /// </summary>
public static string Username public static string Username
{ {
get { return Main.Settings.Username; } get => Main.Settings.Username;
set set
{ {
if (Networking.IsOnServer || string.IsNullOrEmpty(value)) if (Networking.IsOnServer || string.IsNullOrEmpty(value))
@ -90,7 +91,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// The local player is dead /// The local player is dead
/// </summary> /// </summary>
public static event EmptyEvent OnPlayerDied; public static event EventHandler<string> OnPlayerDied;
/// <summary> /// <summary>
/// A local vehicle is spawned /// A local vehicle is spawned
@ -132,7 +133,7 @@ namespace RageCoop.Client.Scripting
internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); } internal static void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); } internal static void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); } internal static void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal static void InvokePlayerDied() { OnPlayerDied?.Invoke(); } internal static void InvokePlayerDied(string m) { OnPlayerDied?.Invoke(null, m); }
internal static void InvokeTick() { OnTick?.Invoke(); } internal static void InvokeTick() { OnTick?.Invoke(); }
internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); } internal static void InvokeKeyDown(object s, KeyEventArgs e) { OnKeyDown?.Invoke(s, e); }
@ -141,12 +142,11 @@ namespace RageCoop.Client.Scripting
internal static void InvokeCustomEventReceived(Packets.CustomEvent p) 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()); // Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
List<Action<CustomEventReceivedArgs>> handlers; if (CustomEventHandlers.TryGetValue(p.Hash, out List<Action<CustomEventReceivedArgs>> handlers))
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
{ {
handlers.ForEach((x) => { x.Invoke(args); }); handlers.ForEach((x) => { x.Invoke(args); });
} }
@ -160,65 +160,48 @@ namespace RageCoop.Client.Scripting
/// Get the local player's ID /// Get the local player's ID
/// </summary> /// </summary>
/// <returns>PlayerID</returns> /// <returns>PlayerID</returns>
public static int LocalPlayerID public static int LocalPlayerID => Main.LocalPlayerID;
{
get { return Main.LocalPlayerID; }
}
/// <summary> /// <summary>
/// Check if player is connected to a server /// Check if player is connected to a server
/// </summary> /// </summary>
public static bool IsOnServer { get { return Networking.IsOnServer; } } public static bool IsOnServer => Networking.IsOnServer;
/// <summary> /// <summary>
/// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server /// Get an <see cref="System.Net.IPEndPoint"/> that the player is currently connected to, or null if not connected to the server
/// </summary> /// </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> /// <summary>
/// Check if a RAGECOOP menu is visible /// Check if a RAGECOOP menu is visible
/// </summary> /// </summary>
public static bool IsMenuVisible public static bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
{
get { return Menus.CoopMenu.MenuPool.AreAnyVisible; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP chat is visible /// Check if the RAGECOOP chat is visible
/// </summary> /// </summary>
public static bool IsChatFocused public static bool IsChatFocused => Main.MainChat.Focused;
{
get { return Main.MainChat.Focused; }
}
/// <summary> /// <summary>
/// Check if the RAGECOOP list of players is visible /// Check if the RAGECOOP list of players is visible
/// </summary> /// </summary>
public static bool IsPlayerListVisible public static bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
{
get { return Util.GetTickCount64() - PlayerList.Pressed < 5000; }
}
/// <summary> /// <summary>
/// Get the version of RAGECOOP /// Get the version of RAGECOOP
/// </summary> /// </summary>
public static Version CurrentVersion public static Version CurrentVersion => Main.Version;
{
get { return Main.Version; }
}
/// <summary> /// <summary>
/// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using. /// Get a <see cref="Core.Logger"/> that RAGECOOP is currently using.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Logger Logger public static Logger Logger => Main.Logger;
{ /// <summary>
get /// Get all players indexed by their ID
{ /// </summary>
return Main.Logger; public static Dictionary<int, Player> Players => new Dictionary<int, Player>(PlayerList.Players);
}
}
#endregion #endregion
#region FUNCTIONS #region FUNCTIONS
@ -245,6 +228,16 @@ namespace RageCoop.Client.Scripting
Networking.ToggleConnection(null); Networking.ToggleConnection(null);
} }
} }
/// <summary>
/// List all servers from master server address
/// </summary>
/// <returns></returns>
public static List<ServerInfo> ListServers()
{
return JsonConvert.DeserializeObject<List<ServerInfo>>(HttpHelper.DownloadString(Main.Settings.MasterServer));
}
/// <summary> /// <summary>
/// Send a local chat message to this player /// Send a local chat message to this player
/// </summary> /// </summary>
@ -255,6 +248,15 @@ namespace RageCoop.Client.Scripting
Main.MainChat.AddMessage(from, message); Main.MainChat.AddMessage(from, message);
} }
/// <summary>
/// Send a chat message or command to server/other players
/// </summary>
/// <param name="message"></param>
public static void SendChatMessage(string message)
{
Networking.SendChatMessage(message);
}
/// <summary> /// <summary>
/// Queue an action to be executed on next tick. /// Queue an action to be executed on next tick.
/// </summary> /// </summary>
@ -283,9 +285,9 @@ namespace RageCoop.Client.Scripting
Networking.Peer.SendTo(new Packets.CustomEvent() Networking.Peer.SendTo(new Packets.CustomEvent()
{ {
Args=args, Args = args,
Hash=eventHash Hash = eventHash
},Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered); }, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
} }
/// <summary> /// <summary>
@ -318,17 +320,17 @@ namespace RageCoop.Client.Scripting
callback(e); callback(e);
} }
}; };
DownloadManager.DownloadCompleted+=handler; DownloadManager.DownloadCompleted += handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest() Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{ {
Name=name, Name = name,
}, },
(p) => (p) =>
{ {
if (p.Response != FileResponse.Loaded) if (p.Response != FileResponse.Loaded)
{ {
DownloadManager.DownloadCompleted-=handler; DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: "+name); 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; private bool _isHost = false;
public override void OnStart() public override void OnStart()
{ {
API.Events.OnPedDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); }; API.Events.OnPedDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted, p.ID); };
API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); }; API.Events.OnVehicleDeleted += (s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); };
API.Events.OnPlayerDied+=() => { API.SendCustomEvent(CustomEvents.OnPlayerDied); }; API.Events.OnPlayerDied += (s, m) => { API.SendCustomEvent(CustomEvents.OnPlayerDied, m); };
API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn); API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn, SetAutoRespawn);
API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag); API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetDisplayNameTag);
@ -29,9 +29,9 @@ namespace RageCoop.Client.Scripting
API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip); API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip);
API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle); API.RegisterCustomEventHandler(CustomEvents.CreateVehicle, CreateVehicle);
API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip); API.RegisterCustomEventHandler(CustomEvents.UpdatePedBlip, UpdatePedBlip);
API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost=(bool)e.Args[0]; }); API.RegisterCustomEventHandler(CustomEvents.IsHost, (e) => { _isHost = (bool)e.Args[0]; });
API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync); API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, WeatherTimeSync);
API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show($"~h~{e.Args[0]}~h~ died."); }); API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) => { GTA.UI.Notification.Show((string)e.Args[0]); });
Task.Run(() => Task.Run(() =>
{ {
while (true) while (true)
@ -46,7 +46,7 @@ namespace RageCoop.Client.Scripting
int weather1 = default(int); int weather1 = default(int);
int weather2 = default(int); int weather2 = default(int);
float percent2 = default(float); float percent2 = default(float);
Function.Call(Hash._GET_WEATHER_TYPE_TRANSITION, &weather1, &weather2, &percent2); Function.Call(Hash.GET_CURR_WEATHER_STATE, &weather1, &weather2, &percent2);
API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2); API.SendCustomEvent(CustomEvents.WeatherTimeSync, time.Hours, time.Minutes, time.Seconds, weather1, weather2, percent2);
} }
}); });
@ -59,33 +59,33 @@ namespace RageCoop.Client.Scripting
private void WeatherTimeSync(CustomEventReceivedArgs e) private void WeatherTimeSync(CustomEventReceivedArgs e)
{ {
World.CurrentTimeOfDay=new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]); World.CurrentTimeOfDay = new TimeSpan((int)e.Args[0], (int)e.Args[1], (int)e.Args[2]);
Function.Call(Hash._SET_WEATHER_TYPE_TRANSITION, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]); Function.Call(Hash.SET_CURR_WEATHER_STATE, (int)e.Args[3], (int)e.Args[4], (float)e.Args[5]);
} }
private void SetDisplayNameTag(CustomEventReceivedArgs e) private void SetDisplayNameTag(CustomEventReceivedArgs e)
{ {
var p = PlayerList.GetPlayer((int)e.Args[0]); 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) private void UpdatePedBlip(CustomEventReceivedArgs e)
{ {
var p = Entity.FromHandle((int)e.Args[0]); var p = Entity.FromHandle((int)e.Args[0]);
if (p == null) { return; } 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.BlipColor = (BlipColor)(byte)e.Args[1];
API.Config.BlipSprite=(BlipSprite)(ushort)e.Args[2]; API.Config.BlipSprite = (BlipSprite)(ushort)e.Args[2];
API.Config.BlipScale=(float)e.Args[3]; API.Config.BlipScale = (float)e.Args[3];
} }
else else
{ {
var b = p.AttachedBlip; var b = p.AttachedBlip;
if (b == null) { b=p.AddBlip(); } if (b == null) { b = p.AddBlip(); }
b.Color=(BlipColor)(byte)e.Args[1]; b.Color = (BlipColor)(byte)e.Args[1];
b.Sprite=(BlipSprite)(ushort)e.Args[2]; b.Sprite = (BlipSprite)(ushort)e.Args[2];
b.Scale=(float)e.Args[3]; b.Scale = (float)e.Args[3];
} }
} }
@ -94,17 +94,17 @@ namespace RageCoop.Client.Scripting
var vehicleModel = (Model)e.Args[1]; var vehicleModel = (Model)e.Args[1];
vehicleModel.Request(1000); vehicleModel.Request(1000);
Vehicle veh = World.CreateVehicle(vehicleModel, (Vector3)e.Args[2], (float)e.Args[3]); 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); Thread.Sleep(10);
} }
veh.CanPretendOccupants=false; veh.CanPretendOccupants = false;
var v = new SyncedVehicle() var v = new SyncedVehicle()
{ {
ID=(int)e.Args[0], ID = (int)e.Args[0],
MainVehicle=veh, MainVehicle = veh,
OwnerID=Main.LocalPlayerID, OwnerID = Main.LocalPlayerID,
}; };
EntityPool.Add(v); EntityPool.Add(v);
} }
@ -127,10 +127,9 @@ namespace RageCoop.Client.Scripting
var pos = (Vector3)obj.Args[4]; var pos = (Vector3)obj.Args[4];
int rot = (int)obj.Args[5]; int rot = (int)obj.Args[5];
var name = (string)obj.Args[6]; var name = (string)obj.Args[6];
Blip blip; if (!EntityPool.ServerBlips.TryGetValue(id, out Blip blip))
if (!EntityPool.ServerBlips.TryGetValue(id, out blip))
{ {
EntityPool.ServerBlips.Add(id, blip=World.CreateBlip(pos)); EntityPool.ServerBlips.Add(id, blip = World.CreateBlip(pos));
} }
blip.Sprite = sprite; blip.Sprite = sprite;
blip.Color = color; blip.Color = color;
@ -152,14 +151,14 @@ namespace RageCoop.Client.Scripting
private void SetNameTag(CustomEventReceivedArgs e) private void SetNameTag(CustomEventReceivedArgs e)
{ {
var p = PlayerList.GetPlayer((int)e.Args[0]); 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) private void SetAutoRespawn(CustomEventReceivedArgs args)
{ {
API.Config.EnableAutoRespawn=(bool)args.Args[0]; API.Config.EnableAutoRespawn = (bool)args.Args[0];
} }
private void DeleteServerProp(CustomEventReceivedArgs e) private void DeleteServerProp(CustomEventReceivedArgs e)
{ {
@ -179,13 +178,13 @@ namespace RageCoop.Client.Scripting
{ {
if (!EntityPool.ServerProps.TryGetValue(id, out prop)) 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.LastSynced = Main.Ticked + 1;
prop.Model= (Model)e.Args[1]; prop.Model = (Model)e.Args[1];
prop.Position=(Vector3)e.Args[2]; prop.Position = (Vector3)e.Args[2];
prop.Rotation=(Vector3)e.Args[3]; prop.Rotation = (Vector3)e.Args[3];
prop.Update(); prop.Update();
} }
private void NativeCall(CustomEventReceivedArgs e) private void NativeCall(CustomEventReceivedArgs e)
@ -194,14 +193,14 @@ namespace RageCoop.Client.Scripting
int i; int i;
var ty = (byte)e.Args[0]; var ty = (byte)e.Args[0];
TypeCode returnType = (TypeCode)ty; TypeCode returnType = (TypeCode)ty;
i = returnType==TypeCode.Empty ? 1 : 2; i = returnType == TypeCode.Empty ? 1 : 2;
var hash = (Hash)e.Args[i++]; var hash = (Hash)e.Args[i++];
for (; i<e.Args.Length; i++) for (; i < e.Args.Length; i++)
{ {
arguments.Add(GetInputArgument(e.Args[i])); arguments.Add(GetInputArgument(e.Args[i]));
} }
if (returnType==TypeCode.Empty) if (returnType == TypeCode.Empty)
{ {
Function.Call(hash, arguments.ToArray()); Function.Call(hash, arguments.ToArray());
return; return;

View File

@ -30,7 +30,7 @@ namespace RageCoop.Client.Scripting
/// <summary> /// <summary>
/// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/> /// Eqivalent of <see cref="ClientResource.Logger"/> in <see cref="CurrentResource"/>
/// </summary> /// </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 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() public Resources()
{ {
BaseScriptType = "RageCoop.Client.Scripting.ClientScript";
Logger = Main.Logger; Logger = Main.Logger;
} }
private void StartAll() private void StartAll()
@ -53,12 +55,12 @@ namespace RageCoop.Client.Scripting
{ {
try try
{ {
s.CurrentResource=d; s.CurrentResource = d;
s.OnStart(); s.OnStart();
} }
catch (Exception ex) 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); Logger?.Error(ex);
} }
} }
@ -79,7 +81,7 @@ namespace RageCoop.Client.Scripting
} }
catch (Exception ex) 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); Logger?.Error(ex);
} }
} }
@ -106,16 +108,6 @@ namespace RageCoop.Client.Scripting
} }
LoadedResources.Clear(); LoadedResources.Clear();
} }
private List<string> ToIgnore = new List<string>
{
"RageCoop.Client.dll",
"RageCoop.Core.dll",
"RageCoop.Server.dll",
"ScriptHookVDotNet3.dll"
};
private List<ClientResource> LoadedResources = new List<ClientResource>();
private string BaseScriptType;
public Logger Logger { get; set; }
private void LoadResource(ZipFile file, string dataFolderRoot) private void LoadResource(ZipFile file, string dataFolderRoot)
{ {
@ -124,22 +116,22 @@ namespace RageCoop.Client.Scripting
{ {
Logger = Main.Logger, Logger = Main.Logger,
Scripts = new List<ClientScript>(), Scripts = new List<ClientScript>(),
Name=Path.GetFileNameWithoutExtension(file.Name), Name = Path.GetFileNameWithoutExtension(file.Name),
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name)) DataFolder = Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
}; };
Directory.CreateDirectory(r.DataFolder); Directory.CreateDirectory(r.DataFolder);
foreach (ZipEntry entry in file) foreach (ZipEntry entry in file)
{ {
ResourceFile rFile; ResourceFile rFile;
r.Files.Add(entry.Name, rFile=new ResourceFile() r.Files.Add(entry.Name, rFile = new ResourceFile()
{ {
Name=entry.Name, Name = entry.Name,
IsDirectory=entry.IsDirectory, IsDirectory = entry.IsDirectory,
}); });
if (!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("/")) if (entry.Name.EndsWith(".dll") && !entry.Name.Contains("/"))
{ {
// Don't load API assembly // Don't load API assembly
@ -152,12 +144,12 @@ namespace RageCoop.Client.Scripting
{ {
continue; continue;
} }
var asm=Assembly.LoadFrom(tmp); var asm = Assembly.LoadFrom(tmp);
toLoad.Add(() => LoadScriptsFromAssembly(rFile,asm, entry.Name,r)); toLoad.Add(() => LoadScriptsFromAssembly(rFile, asm, entry.Name, r));
} }
} }
} }
foreach(var a in toLoad) foreach (var a in toLoad)
{ {
a(); a();
} }
@ -181,8 +173,8 @@ namespace RageCoop.Client.Scripting
// Invoke script constructor // Invoke script constructor
var script = constructor.Invoke(null) as ClientScript; var script = constructor.Invoke(null) as ClientScript;
// script.CurrentResource = toload; // script.CurrentResource = toload;
script.CurrentFile=rfile; script.CurrentFile = rfile;
script.CurrentResource=toload; script.CurrentResource = toload;
toload.Scripts.Add(script); toload.Scripts.Add(script);
count++; count++;
} }

View File

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

View File

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

View File

@ -116,10 +116,10 @@ namespace RageCoop.Client
case unchecked((uint)-1357824103): case unchecked((uint)-1357824103):
case unchecked((uint)-1074790547): case unchecked((uint)-1074790547):
case unchecked((uint)2132975508): case unchecked(2132975508):
case unchecked((uint)-2084633992): case unchecked((uint)-2084633992):
case unchecked((uint)-952879014): case unchecked((uint)-952879014):
case unchecked((uint)100416529): case unchecked(100416529):
case unchecked((uint)WeaponHash.Gusenberg): case unchecked((uint)WeaponHash.Gusenberg):
case unchecked((uint)WeaponHash.MG): case unchecked((uint)WeaponHash.MG):
case unchecked((uint)WeaponHash.CombatMG): 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> /// </summary>
public partial class SyncedPed : SyncedEntity public partial class SyncedPed : SyncedEntity
{ {
#region CONSTRUCTORS
/// <summary> /// <summary>
/// Create a local entity (outgoing sync) /// Create a local entity (outgoing sync)
@ -23,13 +22,13 @@ namespace RageCoop.Client
/// <param name="p"></param> /// <param name="p"></param>
internal SyncedPed(Ped p) internal SyncedPed(Ped p)
{ {
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
p.CanWrithe=false; p.CanWrithe = false;
p.IsOnlyDamagedByPlayer=false; p.IsOnlyDamagedByPlayer = false;
MainPed=p; MainPed = p;
OwnerID=Main.LocalPlayerID; OwnerID = Main.LocalPlayerID;
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false); //Function.Call(Hash.SET_PED_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
// MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true); // MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
@ -40,82 +39,13 @@ namespace RageCoop.Client
/// </summary> /// </summary>
internal SyncedPed(int id) internal SyncedPed(int id)
{ {
ID=id; ID = id;
LastSynced=Main.Ticked; 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() internal override void Update()
{ {
if (Owner==null) { return; } if (Owner == null) { OwnerID = OwnerID; return; }
if (IsPlayer) if (IsPlayer)
{ {
RenderNameTag(); RenderNameTag();
@ -136,9 +66,9 @@ namespace RageCoop.Client
} }
// Need to update state // 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()) 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.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) if (IsPlayer)
{ {
// Main.Logger.Debug("blip:"+Player.Username); PedBlip.Name = Owner.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;
} }
} }
@ -180,11 +111,13 @@ namespace RageCoop.Client
{ {
SetClothes(); SetClothes();
} }
CheckCurrentWeapon();
} }
if (MainPed.IsDead) if (MainPed.IsDead)
{ {
if (Health>0) if (Health > 0)
{ {
if (IsPlayer) if (IsPlayer)
{ {
@ -196,7 +129,7 @@ namespace RageCoop.Client
} }
} }
} }
else if (IsPlayer&&(MainPed.Health != Health)) else if (IsPlayer && (MainPed.Health != Health))
{ {
MainPed.Health = Health; MainPed.Health = Health;
@ -207,13 +140,20 @@ namespace RageCoop.Client
return; return;
} }
} }
if (Speed>=4)
if (!IsPlayer && Health <= 0 && !MainPed.IsDead)
{
MainPed.Kill();
return;
}
if (Speed >= 4)
{ {
DisplayInVehicle(); DisplayInVehicle();
} }
else else
{ {
if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); } if (MainPed.IsInVehicle()) { MainPed.Task.LeaveVehicle(LeaveVehicleFlags.WarpOut); return; }
DisplayOnFoot(); DisplayOnFoot();
} }
@ -232,12 +172,12 @@ namespace RageCoop.Client
} }
} }
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
private void RenderNameTag() 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; return;
} }
@ -246,12 +186,12 @@ namespace RageCoop.Client
Point toDraw = default; Point toDraw = default;
if (Util.WorldToScreen(targetPos, ref toDraw)) if (Util.WorldToScreen(targetPos, ref toDraw))
{ {
toDraw.Y-=100; toDraw.Y -= 100;
new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon) new ScaledText(toDraw, Owner.Username, 0.4f, GTA.UI.Font.ChaletLondon)
{ {
Outline = true, Outline = true,
Alignment = GTA.UI.Alignment.Center, Alignment = GTA.UI.Alignment.Center,
Color=Owner.HasDirectConnection? Color.FromArgb(179, 229, 252) : Color.White, Color = Owner.HasDirectConnection ? Color.FromArgb(179, 229, 252) : Color.White,
}.Draw(); }.Draw();
} }
} }
@ -271,7 +211,7 @@ namespace RageCoop.Client
MainPed = null; MainPed = null;
} }
if (PedBlip != null && PedBlip.Exists()) if (PedBlip != null)
{ {
PedBlip.Delete(); PedBlip.Delete();
PedBlip = null; PedBlip = null;
@ -290,19 +230,19 @@ namespace RageCoop.Client
Model.MarkAsNoLongerNeeded(); Model.MarkAsNoLongerNeeded();
MainPed.BlockPermanentEvents = true; MainPed.BlockPermanentEvents = true;
MainPed.CanWrithe=false; MainPed.CanWrithe = false;
MainPed.CanBeDraggedOutOfVehicle = true; MainPed.CanBeDraggedOutOfVehicle = true;
MainPed.IsOnlyDamagedByPlayer = false; MainPed.IsOnlyDamagedByPlayer = false;
MainPed.RelationshipGroup=Main.SyncedPedsGroup; MainPed.RelationshipGroup = Main.SyncedPedsGroup;
MainPed.IsFireProof=false; MainPed.IsFireProof = false;
MainPed.IsExplosionProof=false; MainPed.IsExplosionProof = false;
Function.Call(Hash.SET_PED_DROPS_WEAPONS_WHEN_DEAD, MainPed.Handle, 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, MainPed.Handle, true);
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true); Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false); Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true); Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false); Function.Call(Hash.SET_PED_IS_IGNORED_BY_AUTO_OPEN_DOORS, false);
Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false); Function.Call(Hash.SET_PED_CAN_EVASIVE_DIVE, MainPed.Handle, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false); MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater, false);
@ -318,8 +258,8 @@ namespace RageCoop.Client
SetClothes(); SetClothes();
if (IsPlayer) { MainPed.IsInvincible=true; } if (IsPlayer) { MainPed.IsInvincible = true; }
if (IsInvincible) { MainPed.IsInvincible=true; } if (IsInvincible) { MainPed.IsInvincible = true; }
lock (EntityPool.PedsLock) lock (EntityPool.PedsLock)
{ {
@ -334,19 +274,15 @@ namespace RageCoop.Client
{ {
for (byte i = 0; i < 12; i++) 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; _lastClothes = Clothes;
} }
#region ONFOOT
private string[] _currentAnimation = new string[2] { "", "" };
private void DisplayOnFoot() private void DisplayOnFoot()
{ {
CheckCurrentWeapon();
if (IsInParachuteFreeFall) if (IsInParachuteFreeFall)
{ {
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f); MainPed.PositionNoOffset = Vector3.Lerp(MainPed.ReadPosition(), Position + Velocity, 0.5f);
@ -490,7 +426,7 @@ namespace RageCoop.Client
} }
_lastIsJumping = false; _lastIsJumping = false;
if (IsRagdoll || Health==0) if (IsRagdoll || (IsPlayer && Health == 0))
{ {
if (!MainPed.IsRagdoll) if (!MainPed.IsRagdoll)
{ {
@ -500,26 +436,22 @@ namespace RageCoop.Client
if (!_lastRagdoll) if (!_lastRagdoll)
{ {
_lastRagdoll = true; _lastRagdoll = true;
_lastRagdollTime=Main.Ticked; _lastRagdollTime = Main.Ticked;
} }
return; return;
} }
else if (MainPed.IsRagdoll)
{ {
if (MainPed.IsRagdoll) if (Speed == 0)
{ {
if (Speed==0) MainPed.CancelRagdoll();
{ }
MainPed.CancelRagdoll(); else
} {
else MainPed.Task.ClearAllImmediately();
{
MainPed.Task.ClearAllImmediately();
}
return;
} }
_lastRagdoll = false; _lastRagdoll = false;
return;
} }
if (IsReloading) if (IsReloading)
@ -549,11 +481,11 @@ namespace RageCoop.Client
Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle); Function.Call(Hash.TASK_STAY_IN_COVER, MainPed.Handle);
} }
_lastInCover=true; _lastInCover = true;
if (IsAiming) if (IsAiming)
{ {
DisplayAiming(); DisplayAiming();
_lastInCover=false; _lastInCover = false;
} }
else if (MainPed.IsInCover) else if (MainPed.IsInCover)
{ {
@ -563,7 +495,7 @@ namespace RageCoop.Client
else if (_lastInCover) else if (_lastInCover)
{ {
MainPed.Task.ClearAllImmediately(); MainPed.Task.ClearAllImmediately();
_lastInCover=false; _lastInCover = false;
} }
else if (IsAiming) else if (IsAiming)
{ {
@ -579,17 +511,15 @@ namespace RageCoop.Client
} }
} }
#region WEAPON
private void CheckCurrentWeapon() private void CheckCurrentWeapon()
{ {
if (!WeaponAsset.IsLoaded) { WeaponAsset.Request(); } if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents) || (Speed <= 3 && _weaponObj?.IsVisible != true))
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents))
{ {
if (WeaponAsset!=null) { WeaponAsset.MarkAsNoLongerNeeded(); } new WeaponAsset(CurrentWeaponHash).Request();
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);
MainPed.Weapons.RemoveAll();
_weaponObj = Entity.FromHandle(Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0));
if (_weaponObj == null) { return; }
if (CurrentWeaponHash != (uint)WeaponHash.Unarmed) if (CurrentWeaponHash != (uint)WeaponHash.Unarmed)
{ {
if (WeaponComponents != null && WeaponComponents.Count != 0) if (WeaponComponents != null && WeaponComponents.Count != 0)
@ -598,15 +528,15 @@ namespace RageCoop.Client
{ {
if (comp.Value) if (comp.Value)
{ {
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _lastWeaponObj, comp.Key); Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _weaponObj, comp.Key);
} }
} }
} }
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle); Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _weaponObj, MainPed.Handle);
} }
_lastWeaponComponents = WeaponComponents; _lastWeaponComponents = WeaponComponents;
} }
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); Function.Call<int>(Hash.SET_PED_WEAPON_TINT_INDEX, MainPed, CurrentWeaponHash, WeaponTint);
} }
@ -614,27 +544,25 @@ namespace RageCoop.Client
private void DisplayAiming() private void DisplayAiming()
{ {
if (Velocity==default) if (Velocity == default)
{ {
MainPed.Task.AimAt(AimCoords, 1000); MainPed.Task.AimAt(AimCoords, 1000);
} }
else else
{ {
Function.Call(Hash.TASK_GO_TO_COORD_WHILE_AIMING_AT_COORD, MainPed.Handle, 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); AimCoords.X, AimCoords.Y, AimCoords.Z, 3f, false, 0x3F000000, 0x40800000, false, 512, false, 0);
} }
SmoothTransition(); SmoothTransition();
} }
#endregion
private bool LastMoving;
private void WalkTo() private void WalkTo()
{ {
MainPed.Task.ClearAll(); MainPed.Task.ClearAll();
Function.Call(Hash.SET_PED_STEALTH_MOVEMENT, MainPed, IsInStealthMode, 0); 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()); float range = predictPosition.DistanceToSquared(MainPed.ReadPosition());
switch (Speed) switch (Speed)
@ -676,6 +604,8 @@ namespace RageCoop.Client
MainPed.Task.StandStill(2000); MainPed.Task.StandStill(2000);
LastMoving = false; LastMoving = false;
} }
if (MainPed.IsTaskActive(TaskType.CTaskDiveToGround)) MainPed.Task.ClearAll();
break; break;
} }
SmoothTransition(); SmoothTransition();
@ -684,21 +614,27 @@ namespace RageCoop.Client
private void SmoothTransition() private void SmoothTransition()
{ {
var localRagdoll = MainPed.IsRagdoll; var localRagdoll = MainPed.IsRagdoll;
var dist = Position.DistanceTo(MainPed.ReadPosition()); var predicted = Predict(Position);
if (dist>3) var dist = predicted.DistanceTo(MainPed.ReadPosition());
if (IsOff(dist))
{ {
MainPed.PositionNoOffset=Position; MainPed.PositionNoOffset = predicted;
return; return;
} }
if (!(localRagdoll || MainPed.IsDead)) if (!(localRagdoll || MainPed.IsDead))
{ {
if (!IsAiming && !MainPed.IsGettingUp) 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; return;
} }
@ -708,44 +644,49 @@ namespace RageCoop.Client
var head = MainPed.Bones[Bone.SkelHead]; var head = MainPed.Bones[Bone.SkelHead];
var rightFoot = MainPed.Bones[Bone.SkelRightFoot]; var rightFoot = MainPed.Bones[Bone.SkelRightFoot];
var leftFoot = MainPed.Bones[Bone.SkelLeftFoot]; var leftFoot = MainPed.Bones[Bone.SkelLeftFoot];
Vector3 amount;
// 20:head, 3:left foot, 6:right foot, 17:right hand, // 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.EqualizeAmount = 1;
helper.PartIndex=20; helper.PartIndex = 20;
helper.Impulse=20*(HeadPosition-head.Position); helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
amount = 20 * (Predict(RightFootPosition) - rightFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1; helper.EqualizeAmount = 1;
helper.PartIndex=6; helper.PartIndex = 6;
helper.Impulse=20*(RightFootPosition-rightFoot.Position); helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
amount = 20 * (Predict(LeftFootPosition) - leftFoot.Position);
if (amount.Length() > 50) { amount = amount.Normalized * 50; }
helper.EqualizeAmount = 1; helper.EqualizeAmount = 1;
helper.PartIndex=3; helper.PartIndex = 3;
helper.Impulse=20*(LeftFootPosition-leftFoot.Position); helper.Impulse = amount;
helper.Start(); helper.Start();
helper.Stop(); helper.Stop();
} }
else 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() private void DisplayInVehicle()
{ {
if (CurrentVehicle==null || CurrentVehicle.MainVehicle==null) { Main.Logger.Error("Veh not found"); return; } if (CurrentVehicle?.MainVehicle == null) { return; }
switch (Speed) switch (Speed)
{ {
case 4: case 4:
if (MainPed.CurrentVehicle!=CurrentVehicle.MainVehicle) if (MainPed.CurrentVehicle != CurrentVehicle.MainVehicle || MainPed.SeatIndex != Seat || (!MainPed.IsSittingInVehicle() && !MainPed.IsBeingJacked))
{ {
MainPed.SetIntoVehicle(CurrentVehicle.MainVehicle, Seat); 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.SET_VEHICLE_TURRET_SPEED_THIS_FRAME, MainPed.CurrentVehicle, 100);
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z); Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, MainPed.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
} }
if (MainPed.VehicleWeapon==VehicleWeaponHash.Invalid) if (MainPed.VehicleWeapon == VehicleWeaponHash.Invalid)
{ {
// World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue); // World.DrawMarker(MarkerType.DebugSphere,AimCoords,default,default,new Vector3(0.2f,0.2f,0.2f),Color.AliceBlue);
CheckCurrentWeapon();
if (IsAiming) if (IsAiming)
{ {
Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z); Function.Call(Hash.SET_DRIVEBY_TASK_TARGET, MainPed, 0, 0, AimCoords.X, AimCoords.Y, AimCoords.Z);
if (!_lastDriveBy) 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); 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)) else if (_lastDriveBy || MainPed.IsTaskActive(TaskType.CTaskAimGunVehicleDriveBy))
{ {
MainPed.Task.ClearAll(); 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; break;
case 5: 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; break;
case 6: case 6:
if (!MainPed.IsTaskActive(TaskType.CTaskExitVehicle)) 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; break;
} }

View File

@ -33,9 +33,13 @@ namespace RageCoop.Client
get => _ownerID; get => _ownerID;
internal set internal set
{ {
if (value==_ownerID && Owner!=null) { return; } if (value == _ownerID && Owner != null) { return; }
_ownerID = value; _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>
/// ///
/// </summary> /// </summary>
internal protected bool _lastFrozen = false; protected internal bool _lastFrozen = false;
internal Model Model { get; set; } internal Model Model { get; set; }
internal Vector3 Position { get; set; } internal Vector3 Position { get; set; }
internal Vector3 Rotation { get; set; } internal Vector3 Rotation { get; set; }
internal Quaternion Quaternion { get; set; } internal Quaternion Quaternion { get; set; }
internal Vector3 Velocity { get; set; } internal Vector3 Velocity { get; set; }
public Stopwatch LastSyncedStopWatch = new Stopwatch();
internal abstract void Update(); internal abstract void Update();
internal void PauseUpdate(ulong frames) internal void PauseUpdate(ulong frames)
{ {
LastUpdated=Main.Ticked+frames; LastUpdated = Main.Ticked + frames;
}
protected Vector3 Predict(Vector3 input)
{
return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input;
}
private float _accumulatedOff = 0;
protected bool IsOff(float thisOff, float tolerance = 3, float limit = 30)
{
_accumulatedOff += thisOff - tolerance;
if (_accumulatedOff < 0) { _accumulatedOff = 0; }
else if (_accumulatedOff >= limit)
{
_accumulatedOff = 0;
return true;
}
return false;
} }
} }
} }

View File

@ -1,13 +1,13 @@
using GTA; using GTA;
using GTA.Math; using GTA.Math;
using RageCoop.Core;
using GTA.Native; using GTA.Native;
using RageCoop.Core;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class SyncedProjectile : SyncedEntity internal class SyncedProjectile : SyncedEntity
{ {
public ProjectileDataFlags Flags { private get; set; }=ProjectileDataFlags.None; public ProjectileDataFlags Flags { private get; set; } = ProjectileDataFlags.None;
public readonly Vector3 Origin; public readonly Vector3 Origin;
private bool _firstSend = false; private bool _firstSend = false;
@ -19,28 +19,27 @@ namespace RageCoop.Client
public SyncedEntity Shooter { get; set; } public SyncedEntity Shooter { get; set; }
public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded); public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded);
internal override Player Owner => Shooter.Owner;
/// <summary> /// <summary>
/// Invalid property for projectile. /// Invalid property for projectile.
/// </summary> /// </summary>
private new int OwnerID { set { } } private new int OwnerID { set { } }
internal override Player Owner => Shooter.Owner;
public WeaponHash WeaponHash { get; set; } public WeaponHash WeaponHash { get; set; }
private WeaponAsset Asset { get; set; } private WeaponAsset Asset { get; set; }
public void ExtractData(ref Packets.ProjectileSync p) public void ExtractData(ref Packets.ProjectileSync p)
{ {
p.Position=MainProjectile.Position; p.Position = MainProjectile.Position;
p.Velocity=MainProjectile.Velocity; p.Velocity = MainProjectile.Velocity;
p.Rotation=MainProjectile.Rotation; p.Rotation = MainProjectile.Rotation;
p.ID=ID; p.ID = ID;
p.ShooterID=Shooter.ID; p.ShooterID = Shooter.ID;
p.WeaponHash=(uint)MainProjectile.WeaponHash; p.WeaponHash = (uint)MainProjectile.WeaponHash;
p.Flags=ProjectileDataFlags.None; p.Flags = ProjectileDataFlags.None;
if (MainProjectile.IsDead) if (MainProjectile.IsDead)
{ {
p.Flags |= ProjectileDataFlags.Exploded; p.Flags |= ProjectileDataFlags.Exploded;
} }
if (MainProjectile.AttachedEntity!=null) if (MainProjectile.AttachedEntity != null)
{ {
p.Flags |= ProjectileDataFlags.IsAttached; p.Flags |= ProjectileDataFlags.IsAttached;
} }
@ -51,44 +50,44 @@ namespace RageCoop.Client
if (_firstSend) if (_firstSend)
{ {
p.Flags |= ProjectileDataFlags.IsAttached; p.Flags |= ProjectileDataFlags.IsAttached;
_firstSend=false; _firstSend = false;
} }
} }
public SyncedProjectile(Projectile p) public SyncedProjectile(Projectile p)
{ {
var owner = p.OwnerEntity; var owner = p.OwnerEntity;
if (owner==null) { IsValid=false;return; } if (owner == null) { IsValid = false; return; }
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
MainProjectile = p; MainProjectile = p;
Origin=p.Position; Origin = p.Position;
if(EntityPool.PedsByHandle.TryGetValue(owner.Handle,out var shooter)) if (EntityPool.PedsByHandle.TryGetValue(owner.Handle, out var shooter))
{ {
if (shooter.MainPed!=null if (shooter.MainPed != null
&& (p.AttachedEntity==shooter.MainPed.Weapons.CurrentWeaponObject && (p.AttachedEntity == shooter.MainPed.Weapons.CurrentWeaponObject
|| p.AttachedEntity== shooter.MainPed)) || p.AttachedEntity == shooter.MainPed))
{ {
// Reloading // Reloading
IsValid=false; IsValid = false;
return; return;
} }
Shooter=shooter; Shooter = shooter;
IsLocal=shooter.IsLocal; 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; Shooter = shooterVeh;
IsLocal=shooterVeh.IsLocal; IsLocal = shooterVeh.IsLocal;
} }
else else
{ {
IsValid=false; IsValid = false;
} }
} }
public SyncedProjectile(int id) public SyncedProjectile(int id)
{ {
ID= id; ID = id;
IsLocal=false; IsLocal = false;
} }
internal override void Update() internal override void Update()
{ {
@ -101,27 +100,26 @@ namespace RageCoop.Client
CreateProjectile(); CreateProjectile();
return; return;
} }
MainProjectile.Velocity=Velocity+(Position+Shooter.Owner.PacketTravelTime*Velocity-MainProjectile.Position); MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
MainProjectile.Rotation=Rotation; MainProjectile.Rotation = Rotation;
LastUpdated=Main.Ticked; LastUpdated = Main.Ticked;
} }
private void CreateProjectile() private void CreateProjectile()
{ {
Asset=new WeaponAsset(WeaponHash); Asset = new WeaponAsset(WeaponHash);
if (!Asset.IsLoaded) { Asset.Request(); return; } if (!Asset.IsLoaded) { Asset.Request(); return; }
if(Shooter == null) { return; } if (Shooter == null) { return; }
Entity owner; Entity owner;
owner=(Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle; owner = (Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle;
var end = Position+Velocity; Position = (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Shooter.Velocity + Position;
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); var end = Position + Velocity;
Function.Call(Hash.SHOOT_SINGLE_BULLET_BETWEEN_COORDS_IGNORE_ENTITY, Position.X, Position.Y, Position.Z, end.X, end.Y, end.Z, 0, 1, WeaponHash, owner?.Handle ?? 0, 1, 0, -1);
var ps = World.GetAllProjectiles(); var ps = World.GetAllProjectiles();
MainProjectile=ps[ps.Length-1]; MainProjectile = ps[ps.Length - 1];
MainProjectile.IsCollisionEnabled=false; MainProjectile.Position = Position;
MainProjectile.Position=Position; MainProjectile.Rotation = Rotation;
MainProjectile.Rotation =Rotation; MainProjectile.Velocity = Velocity;
MainProjectile.Velocity=Velocity;
Main.Delay(()=>MainProjectile.IsCollisionEnabled=true, 100);
EntityPool.Add(this); EntityPool.Add(this);
} }
} }

View File

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

View File

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

View File

@ -4,8 +4,7 @@ using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Linq;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -24,22 +23,24 @@ namespace RageCoop.Client
internal SyncedVehicle(Vehicle v) internal SyncedVehicle(Vehicle v)
{ {
ID=EntityPool.RequestNewID(); ID = EntityPool.RequestNewID();
MainVehicle=v; MainVehicle = v;
MainVehicle.CanPretendOccupants=false; MainVehicle.CanPretendOccupants = false;
OwnerID=Main.LocalPlayerID; OwnerID = Main.LocalPlayerID;
SetUpFixedData(); SetUpFixedData();
} }
private void SetUpFixedData(){ internal void SetUpFixedData()
{
if (MainVehicle == null) { return; }
IsAircraft = MainVehicle.IsAircraft; IsAircraft = MainVehicle.IsAircraft;
IsMotorcycle = MainVehicle.IsMotorcycle; IsMotorcycle = MainVehicle.IsMotorcycle;
HasRocketBoost = MainVehicle.HasRocketBoost; HasRocketBoost = MainVehicle.HasRocketBoost;
HasParachute = MainVehicle.HasParachute; HasParachute = MainVehicle.HasParachute;
HasRoof = MainVehicle.HasRoof; HasRoof = MainVehicle.HasRoof;
IsSubmarineCar=MainVehicle.IsSubmarineCar; IsSubmarineCar = MainVehicle.IsSubmarineCar;
IsDeluxo=MainVehicle.Model==1483171323; IsDeluxo = MainVehicle.Model == 1483171323;
} }
/// <summary> /// <summary>
@ -51,8 +52,8 @@ namespace RageCoop.Client
} }
internal SyncedVehicle(int id) internal SyncedVehicle(int id)
{ {
ID=id; ID = id;
LastSynced=Main.Ticked; LastSynced = Main.Ticked;
} }
#endregion #endregion
/// <summary> /// <summary>
@ -85,6 +86,8 @@ namespace RageCoop.Client
} }
} }
DisplayVehicle();
// Skip update if no new sync message has arrived. // Skip update if no new sync message has arrived.
if (!NeedUpdate) if (!NeedUpdate)
{ {
@ -95,10 +98,8 @@ namespace RageCoop.Client
{ {
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle); MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * SteeringAngle);
} }
MainVehicle.ThrottlePower=ThrottlePower; MainVehicle.ThrottlePower = ThrottlePower;
MainVehicle.BrakePower=BrakePower; MainVehicle.BrakePower = BrakePower;
var v = Main.P.CurrentVehicle;
DisplayVehicle(v != null && MainVehicle.IsTouching(v));
if (IsDead) if (IsDead)
{ {
@ -119,7 +120,7 @@ namespace RageCoop.Client
{ {
MainVehicle.Repair(); MainVehicle.Repair();
} }
},1000); }, 1000);
} }
} }
if (MainVehicle.IsOnFire) if (MainVehicle.IsOnFire)
@ -149,8 +150,6 @@ namespace RageCoop.Client
MainVehicle.AreHighBeamsOn = HighBeamsOn; MainVehicle.AreHighBeamsOn = HighBeamsOn;
} }
if (IsAircraft) if (IsAircraft)
{ {
if (LandingGear != (byte)MainVehicle.LandingGearState) if (LandingGear != (byte)MainVehicle.LandingGearState)
@ -179,15 +178,17 @@ namespace RageCoop.Client
MainVehicle.SoundHorn(1); 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)); 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)); MainVehicle.SetParachuteActive(Flags.HasFlag(VehicleDataFlags.IsParachuteActive));
} }
if (IsSubmarineCar) if (IsSubmarineCar)
@ -197,31 +198,32 @@ namespace RageCoop.Client
if (!_lastTransformed) if (!_lastTransformed)
{ {
_lastTransformed = true; _lastTransformed = true;
Function.Call(Hash._TRANSFORM_VEHICLE_TO_SUBMARINE, MainVehicle.Handle, false); Function.Call(Hash.TRANSFORM_TO_SUBMARINE, MainVehicle.Handle, false);
} }
} }
else if (_lastTransformed) else if (_lastTransformed)
{ {
_lastTransformed = false; _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); MainVehicle.SetDeluxoHoverState(IsDeluxoHovering);
if(IsDeluxoHovering){ if (IsDeluxoHovering)
{
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio); MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
} }
} }
Function.Call(Hash.SET_VEHICLE_BRAKE_LIGHTS, MainVehicle.Handle, BrakeLightsOn); 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)) if (Flags.HasVehFlag(VehicleDataFlags.Repaired))
{ {
MainVehicle.Repair(); MainVehicle.Repair();
@ -232,7 +234,7 @@ namespace RageCoop.Client
_lastVehicleColors = Colors; _lastVehicleColors = Colors;
} }
MainVehicle.EngineHealth=EngineHealth; MainVehicle.EngineHealth = EngineHealth;
if (Mods != null && !Mods.Compare(_lastVehicleMods)) if (Mods != null && !Mods.Compare(_lastVehicleMods))
{ {
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0); Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
@ -245,24 +247,24 @@ namespace RageCoop.Client
_lastVehicleMods = Mods; _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); Function.Call(Hash.SET_VEHICLE_NUMBER_PLATE_TEXT, MainVehicle, LicensePlate);
} }
if (_lastLivery!=Livery) if (_lastLivery != Livery)
{ {
Function.Call(Hash.SET_VEHICLE_LIVERY, MainVehicle, 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 = Predict(Position);
_predictedPosition = Position + _elapsed * Velocity;
var current = MainVehicle.ReadPosition(); var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(_predictedPosition); var dist = current.DistanceTo(_predictedPosition);
var cali = dist * (_predictedPosition - current); var cali = dist * (_predictedPosition - current);
@ -274,59 +276,48 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion; MainVehicle.Quaternion = Quaternion;
return; return;
} }
if (dist > 0.03)
{
MainVehicle.Velocity = Velocity + cali;
}
Vector3 calirot;
MainVehicle.Velocity = Velocity+cali; if (IsFlipped || (calirot = GetCalibrationRotation()).Length() > 50)
if (IsFlipped)
{ {
MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f); MainVehicle.Quaternion = Quaternion.Slerp(MainVehicle.ReadQuaternion(), Quaternion, 0.5f);
MainVehicle.RotationVelocity = RotationVelocity; MainVehicle.RotationVelocity = RotationVelocity;
return; return;
} }
MainVehicle.RotationVelocity = RotationVelocity + calirot * 0.2f;
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
} }
private Vector3 GetCalibrationRotation() private Vector3 GetCalibrationRotation()
{ {
var rot = Quaternion.LookRotation(Quaternion*Vector3.RelativeFront, Quaternion*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 curRot = Quaternion.LookRotation(MainVehicle.ReadQuaternion() * Vector3.RelativeFront, MainVehicle.ReadQuaternion() * Vector3.RelativeTop).ToEulerAngles();
var r = (rot-curRot).ToDegree(); var r = (rot - curRot).ToDegree();
if (r.X>180) { r.X=r.X-360; } if (r.X > 180) { r.X = r.X - 360; }
else if (r.X<-180) { r.X=360+r.X; } else if (r.X < -180) { r.X = 360 + r.X; }
if (r.Y>180) { r.Y=r.Y-360; } if (r.Y > 180) { r.Y = r.Y - 360; }
else if (r.Y<-180) { r.Y=360+r.Y; } else if (r.Y < -180) { r.Y = 360 + r.Y; }
if (r.Z>180) { r.Z=r.Z-360; } if (r.Z > 180) { r.Z = r.Z - 360; }
else if (r.Z<-180) { r.Z=360+r.Z; } else if (r.Z < -180) { r.Z = 360 + r.Z; }
return r; return r;
} }
private bool CreateVehicle() 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?.Delete();
MainVehicle = Util.CreateVehicle(Model, Position); MainVehicle = Util.CreateVehicle(Model, Position);
if (!Model.IsInCdImage) if (!Model.IsInCdImage)
@ -334,7 +325,7 @@ namespace RageCoop.Client
// GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!"); // GTA.UI.Notification.Show($"~r~(Vehicle)Model ({CurrentVehicleModelHash}) cannot be loaded!");
return false; return false;
} }
else if (MainVehicle==null) if (MainVehicle == null)
{ {
Model.Request(); Model.Request();
return false; return false;
@ -346,54 +337,16 @@ namespace RageCoop.Client
MainVehicle.Quaternion = Quaternion; MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof) if (MainVehicle.HasRoof)
{ {
MainVehicle.RoofState=RoofState; MainVehicle.RoofState = RoofState;
} }
foreach(var w in MainVehicle.Wheels) foreach (var w in MainVehicle.Wheels)
{ {
w.Fix(); w.Fix();
} }
if (IsInvincible) { MainVehicle.IsInvincible=true; } if (IsInvincible) { MainVehicle.IsInvincible = true; }
SetUpFixedData(); SetUpFixedData();
Model.MarkAsNoLongerNeeded(); Model.MarkAsNoLongerNeeded();
return true; 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;
using GTA.Native; using GTA.Native;
using Lidgren.Network;
using RageCoop.Client.Scripting; using RageCoop.Client.Scripting;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Lidgren.Network;
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal class EntityPool internal class EntityPool
{ {
public static object PedsLock = new object(); public static object PedsLock = new object();
public static int CharactersCount { get { return PedsByID.Count; } }
#if BENCHMARK #if BENCHMARK
private static Stopwatch PerfCounter=new Stopwatch(); private static Stopwatch PerfCounter=new Stopwatch();
private static Stopwatch PerfCounter2=Stopwatch.StartNew(); private static Stopwatch PerfCounter2=Stopwatch.StartNew();
@ -44,17 +43,17 @@ namespace RageCoop.Client
#endregion #endregion
public static void Cleanup(bool keepPlayer = true, bool keepMine = true) public static void Cleanup(bool keepPlayer = true, bool keepMine = true)
{ {
foreach (int id in new List<int>(PedsByID.Keys)) foreach (var ped in PedsByID.Values.ToArray())
{ {
if (keepPlayer && (id==Main.LocalPlayerID)|| keepMine && (PedsByID[id].OwnerID == Main.LocalPlayerID)) { continue; } if ((keepPlayer && (ped.ID == Main.LocalPlayerID)) || (keepMine && (ped.OwnerID == Main.LocalPlayerID))) { continue; }
RemovePed(id); RemovePed(ped.ID);
} }
PedsByID.Clear(); PedsByID.Clear();
PedsByHandle.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); RemoveVehicle(id);
} }
VehiclesByID.Clear(); VehiclesByID.Clear();
@ -62,7 +61,7 @@ namespace RageCoop.Client
foreach (var p in ProjectilesByID.Values) 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(); p.MainProjectile.Delete();
} }
@ -93,8 +92,10 @@ namespace RageCoop.Client
public static bool AddPlayer() public static bool AddPlayer()
{ {
Ped p = Game.Player.Character; Ped p = Game.Player.Character;
// var clipset=p.Gender==Gender.Male? "MOVE_M@TOUGH_GUY@" : "MOVE_F@TOUGH_GUY@";
// Function.Call(Hash.SET_PED_MOVEMENT_CLIPSET,p,clipset,1f);
SyncedPed player = GetPedByID(Main.LocalPlayerID); SyncedPed player = GetPedByID(Main.LocalPlayerID);
if (player==null) if (player == null)
{ {
Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}"); Main.Logger.Debug($"Creating SyncEntity for player, handle:{p.Handle}");
SyncedPed c = new SyncedPed(p); SyncedPed c = new SyncedPed(p);
@ -131,16 +132,16 @@ namespace RageCoop.Client
{ {
if (PedsByID.ContainsKey(c.ID)) if (PedsByID.ContainsKey(c.ID))
{ {
PedsByID[c.ID]=c; PedsByID[c.ID] = c;
} }
else else
{ {
PedsByID.Add(c.ID, c); PedsByID.Add(c.ID, c);
} }
if (c.MainPed==null) { return; } if (c.MainPed == null) { return; }
if (PedsByHandle.ContainsKey(c.MainPed.Handle)) if (PedsByHandle.ContainsKey(c.MainPed.Handle))
{ {
PedsByHandle[c.MainPed.Handle]=c; PedsByHandle[c.MainPed.Handle] = c;
} }
else else
{ {
@ -157,7 +158,7 @@ namespace RageCoop.Client
{ {
SyncedPed c = PedsByID[id]; SyncedPed c = PedsByID[id];
var p = c.MainPed; var p = c.MainPed;
if (p!=null) if (p != null)
{ {
if (PedsByHandle.ContainsKey(p.Handle)) if (PedsByHandle.ContainsKey(p.Handle))
{ {
@ -182,23 +183,23 @@ namespace RageCoop.Client
#endregion #endregion
#region VEHICLES #region VEHICLES
public static SyncedVehicle GetVehicleByID(int id) => VehiclesByID.TryGetValue(id,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 SyncedVehicle GetVehicleByHandle(int handle) => VehiclesByHandle.TryGetValue(handle, out var v) ? v : null;
public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys); public static List<int> GetVehicleIDs() => new List<int>(VehiclesByID.Keys);
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
if (VehiclesByID.ContainsKey(v.ID)) if (VehiclesByID.ContainsKey(v.ID))
{ {
VehiclesByID[v.ID]=v; VehiclesByID[v.ID] = v;
} }
else else
{ {
VehiclesByID.Add(v.ID, v); VehiclesByID.Add(v.ID, v);
} }
if (v.MainVehicle==null) { return; } if (v.MainVehicle == null) { return; }
if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle)) if (VehiclesByHandle.ContainsKey(v.MainVehicle.Handle))
{ {
VehiclesByHandle[v.MainVehicle.Handle]=v; VehiclesByHandle[v.MainVehicle.Handle] = v;
} }
else else
{ {
@ -215,7 +216,7 @@ namespace RageCoop.Client
{ {
SyncedVehicle v = VehiclesByID[id]; SyncedVehicle v = VehiclesByID[id];
var veh = v.MainVehicle; var veh = v.MainVehicle;
if (veh!=null) if (veh != null)
{ {
if (VehiclesByHandle.ContainsKey(veh.Handle)) if (VehiclesByHandle.ContainsKey(veh.Handle))
{ {
@ -237,27 +238,27 @@ namespace RageCoop.Client
#region PROJECTILES #region PROJECTILES
public static SyncedProjectile GetProjectileByID(int id) 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) public static void Add(SyncedProjectile p)
{ {
if (!p.IsValid) { return; } 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)) if (ProjectilesByID.ContainsKey(p.ID))
{ {
ProjectilesByID[p.ID]=p; ProjectilesByID[p.ID] = p;
} }
else else
{ {
ProjectilesByID.Add(p.ID, p); ProjectilesByID.Add(p.ID, p);
} }
if (p.MainProjectile==null) { return; } if (p.MainProjectile == null) { return; }
if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle)) if (ProjectilesByHandle.ContainsKey(p.MainProjectile.Handle))
{ {
ProjectilesByHandle[p.MainProjectile.Handle]=p; ProjectilesByHandle[p.MainProjectile.Handle] = p;
} }
else else
{ {
@ -270,14 +271,14 @@ namespace RageCoop.Client
{ {
SyncedProjectile sp = ProjectilesByID[id]; SyncedProjectile sp = ProjectilesByID[id];
var p = sp.MainProjectile; var p = sp.MainProjectile;
if (p!=null) if (p != null)
{ {
if (ProjectilesByHandle.ContainsKey(p.Handle)) if (ProjectilesByHandle.ContainsKey(p.Handle))
{ {
ProjectilesByHandle.Remove(p.Handle); ProjectilesByHandle.Remove(p.Handle);
} }
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}"); //Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
p.Explode(); if (sp.Exploded) p.Explode();
} }
ProjectilesByID.Remove(id); ProjectilesByID.Remove(id);
} }
@ -287,11 +288,11 @@ namespace RageCoop.Client
public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id); public static bool VehicleExists(int id) => VehiclesByID.ContainsKey(id);
public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id); public static bool ProjectileExists(int id) => ProjectilesByID.ContainsKey(id);
#endregion #endregion
static int vehStateIndex; private static int vehStateIndex;
static int pedStateIndex; private static int pedStateIndex;
static int vehStatesPerFrame; private static int vehStatesPerFrame;
static int pedStatesPerFrame; private static int pedStatesPerFrame;
static int i; private static int i;
public static Ped[] allPeds = new Ped[0]; public static Ped[] allPeds = new Ped[0];
public static Vehicle[] allVehicles = new Vehicle[0]; public static Vehicle[] allVehicles = new Vehicle[0];
public static Projectile[] allProjectiles = new Projectile[0]; public static Projectile[] allProjectiles = new Projectile[0];
@ -304,21 +305,10 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
#endif #endif
allPeds = World.GetAllPeds(); allPeds = World.GetAllPeds();
allVehicles=World.GetAllVehicles(); allVehicles = World.GetAllVehicles();
allProjectiles=World.GetAllProjectiles(); allProjectiles = World.GetAllProjectiles();
vehStatesPerFrame=allVehicles.Length*2/(int)Game.FPS+1; vehStatesPerFrame = allVehicles.Length * 2 / (int)Game.FPS + 1;
pedStatesPerFrame=allPeds.Length*2/(int)Game.FPS+1; pedStatesPerFrame = allPeds.Length * 2 / (int)Game.FPS + 1;
/*
if (Main.Ticked%50==0)
{
bool flag1 = allVehicles.Length>Main.Settings.WorldVehicleSoftLimit && Main.Settings.WorldVehicleSoftLimit>-1;
bool flag2 = allPeds.Length>Main.Settings.WorldPedSoftLimit && Main.Settings.WorldPedSoftLimit>-1;
if ((flag1||flag2) && _trafficSpawning)
{ SetBudget(0); _trafficSpawning=false; }
else if(!_trafficSpawning)
{ SetBudget(1); _trafficSpawning=true; }
}
*/
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
@ -340,10 +330,10 @@ namespace RageCoop.Client
// Outgoing sync // Outgoing sync
if (p.IsLocal) if (p.IsLocal)
{ {
if (p.MainProjectile.AttachedEntity==null) if (p.MainProjectile.AttachedEntity == null)
{ {
// Prevent projectiles from exploding next to vehicle // Prevent projectiles from exploding next to vehicle
if (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; continue;
} }
@ -364,26 +354,29 @@ namespace RageCoop.Client
} }
} }
i=-1; i = -1;
lock (PedsLock) lock (PedsLock)
{ {
EntityPool.AddPlayer(); AddPlayer();
var mainCharacters = new List<PedHash> { PedHash.Michael, PedHash.Franklin, PedHash.Franklin02, PedHash.Trevor };
foreach (Ped p in allPeds) foreach (Ped p in allPeds)
{ {
SyncedPed c = EntityPool.GetPedByHandle(p.Handle); if (!PedsByHandle.ContainsKey(p.Handle) && p != Game.Player.Character && !mainCharacters.Contains((PedHash)p.Model.Hash))
if (c==null && (p!=Game.Player.Character))
{ {
if (allPeds.Length>Main.Settings.WorldPedSoftLimit && p.PopulationType != EntityPopulationType.RandomAmbient) if (PedsByID.Count(x => x.Value.IsLocal) > Main.Settings.WorldPedSoftLimit)
{ {
p.Delete(); if (p.PopulationType == EntityPopulationType.RandomAmbient && !p.IsInVehicle())
continue; {
p.Delete();
continue;
}
if (p.PopulationType == EntityPopulationType.RandomScenario) continue;
} }
// Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}"); // Main.Logger.Trace($"Creating SyncEntity for ped, handle:{p.Handle}");
c=new SyncedPed(p);
EntityPool.Add(c); Add(new SyncedPed(p));
} }
} }
#if BENCHMARK #if BENCHMARK
@ -391,18 +384,18 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddPeds]=PerfCounter.ElapsedTicks;
#endif #endif
var ps = PedsByID.Values.ToArray(); var ps = PedsByID.Values.ToArray();
pedStateIndex+=pedStatesPerFrame; pedStateIndex += pedStatesPerFrame;
if (pedStateIndex>=ps.Length) if (pedStateIndex >= ps.Length)
{ {
pedStateIndex=0; pedStateIndex = 0;
} }
foreach (SyncedPed c in ps) foreach (SyncedPed c in ps)
{ {
i++; i++;
if ((c.MainPed!=null)&&(!c.MainPed.Exists())) if ((c.MainPed != null) && (!c.MainPed.Exists()))
{ {
EntityPool.RemovePed(c.ID, "non-existent"); RemovePed(c.ID, "non-existent");
continue; continue;
} }
@ -415,7 +408,7 @@ namespace RageCoop.Client
// event check // event check
SyncEvents.Check(c); SyncEvents.Check(c);
Networking.SendPed(c, (i-pedStateIndex)<pedStatesPerFrame); Networking.SendPed(c, (i - pedStateIndex) < pedStatesPerFrame);
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start; Debug.TimeStamps[TimeStamp.SendPed]=PerfCounter2.ElapsedTicks-start;
#endif #endif
@ -439,27 +432,25 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
#endif #endif
} }
var check = Main.Ticked % 100 == 0;
i=-1; i = -1;
lock (VehiclesLock) lock (VehiclesLock)
{ {
foreach (Vehicle veh in allVehicles) foreach (Vehicle veh in allVehicles)
{ {
if (!VehiclesByHandle.ContainsKey(veh.Handle)) 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 (veh.PopulationType == EntityPopulationType.RandomAmbient || veh.PopulationType == EntityPopulationType.RandomParked)
if (type==EntityPopulationType.RandomAmbient || type==EntityPopulationType.RandomParked)
{ {
foreach (var p in veh.Occupants) foreach (var p in veh.Occupants)
{ {
p.Delete(); p.Delete();
var c = EntityPool.GetPedByHandle(p.Handle); var c = GetPedByHandle(p.Handle);
if (c!=null) if (c != null)
{ {
EntityPool.RemovePed(c.ID, "ThrottleTraffic"); RemovePed(c.ID, "ThrottleTraffic");
} }
} }
veh.Delete(); veh.Delete();
@ -475,28 +466,31 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.AddVehicles]=PerfCounter.ElapsedTicks;
#endif #endif
var vs = VehiclesByID.Values.ToArray(); var vs = VehiclesByID.Values.ToArray();
vehStateIndex+=vehStatesPerFrame; vehStateIndex += vehStatesPerFrame;
if (vehStateIndex>=vs.Length) if (vehStateIndex >= vs.Length)
{ {
vehStateIndex=0; vehStateIndex = 0;
} }
foreach (SyncedVehicle v in vs) foreach (SyncedVehicle v in vs)
{ {
i++; i++;
if ((v.MainVehicle!=null)&&(!v.MainVehicle.Exists())) if ((v.MainVehicle != null) && (!v.MainVehicle.Exists()))
{ {
EntityPool.RemoveVehicle(v.ID, "non-existent"); RemoveVehicle(v.ID, "non-existent");
continue; continue;
} }
if (check)
{
v.SetUpFixedData();
}
// Outgoing sync // Outgoing sync
if (v.IsLocal) if (v.IsLocal)
{ {
if (!v.MainVehicle.IsVisible) { continue; } if (!v.MainVehicle.IsVisible) { continue; }
SyncEvents.Check(v); SyncEvents.Check(v);
Networking.SendVehicle(v, (i-vehStateIndex)<vehStatesPerFrame); Networking.SendVehicle(v, (i - vehStateIndex) < vehStatesPerFrame);
} }
else // Incoming sync else // Incoming sync
{ {
@ -514,12 +508,13 @@ namespace RageCoop.Client
} }
Networking.Peer.FlushSendQueue(); 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()) 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); Networking.Targets.Add(p.Connection);
} }
@ -530,7 +525,7 @@ namespace RageCoop.Client
{ {
foreach (SyncedPed p in PedsByID.Values.ToArray()) foreach (SyncedPed p in PedsByID.Values.ToArray())
{ {
if (p.OwnerID==playerPedId) if (p.OwnerID == playerPedId)
{ {
RemovePed(p.ID); RemovePed(p.ID);
} }
@ -538,7 +533,7 @@ namespace RageCoop.Client
foreach (SyncedVehicle v in VehiclesByID.Values.ToArray()) foreach (SyncedVehicle v in VehiclesByID.Values.ToArray())
{ {
if (v.OwnerID==playerPedId) if (v.OwnerID == playerPedId)
{ {
RemoveVehicle(v.ID); RemoveVehicle(v.ID);
} }
@ -548,7 +543,7 @@ namespace RageCoop.Client
public static int RequestNewID() public static int RequestNewID()
{ {
int ID = 0; 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]; byte[] rngBytes = new byte[4];
@ -579,21 +574,21 @@ namespace RageCoop.Client
{ {
public static void Add(SyncedVehicle v) public static void Add(SyncedVehicle v)
{ {
lock (EntityPool.VehiclesLock) lock (VehiclesLock)
{ {
EntityPool.Add(v); EntityPool.Add(v);
} }
} }
public static void Add(SyncedPed p) public static void Add(SyncedPed p)
{ {
lock (EntityPool.PedsLock) lock (PedsLock)
{ {
EntityPool.Add(p); EntityPool.Add(p);
} }
} }
public static void Add(SyncedProjectile sp) public static void Add(SyncedProjectile sp)
{ {
lock (EntityPool.ProjectilesLock) lock (ProjectilesLock)
{ {
EntityPool.Add(sp); EntityPool.Add(sp);
} }

View File

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

View File

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

View File

@ -1,152 +1,145 @@
using System; using GTA.Math;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native; using GTA.Native;
using GTA.Math; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using LemonUI.Elements;
using System.Drawing;
namespace RageCoop.Client namespace RageCoop.Client
{ {
[StructLayout(LayoutKind.Explicit, Size = 80)] [StructLayout(LayoutKind.Explicit, Size = 80)]
public struct HeadBlendData public struct HeadBlendData
{ {
[FieldOffset(0)] [FieldOffset(0)]
public int ShapeFirst; public int ShapeFirst;
[FieldOffset(8)] [FieldOffset(8)]
public int ShapeSecond; public int ShapeSecond;
[FieldOffset(16)] [FieldOffset(16)]
public int ShapeThird; public int ShapeThird;
[FieldOffset(24)] [FieldOffset(24)]
public int SkinFirst; public int SkinFirst;
[FieldOffset(32)] [FieldOffset(32)]
public int SkinSecond; public int SkinSecond;
[FieldOffset(40)] [FieldOffset(40)]
public int SkinThird; public int SkinThird;
[FieldOffset(48)] [FieldOffset(48)]
public float ShapeMix; public float ShapeMix;
[FieldOffset(56)] [FieldOffset(56)]
public float SkinMix; public float SkinMix;
[FieldOffset(64)] [FieldOffset(64)]
public float ThirdMix; public float ThirdMix;
} }
[StructLayout(LayoutKind.Explicit, Size = 24)] [StructLayout(LayoutKind.Explicit, Size = 24)]
public struct NativeVector3 public struct NativeVector3
{ {
[FieldOffset(0)] [FieldOffset(0)]
public float X; public float X;
[FieldOffset(8)] [FieldOffset(8)]
public float Y; public float Y;
[FieldOffset(16)] [FieldOffset(16)]
public float Z; 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 }; return new NativeVector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
} }
} }
public static class NativeCaller public static class NativeCaller
{ {
// These are borrowed from ScriptHookVDotNet's // These are borrowed from ScriptHookVDotNet's
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")]
static extern void NativeInit(ulong hash); private static extern void NativeInit(ulong hash);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")]
static extern void NativePush64(ulong val); private static extern void NativePush64(ulong val);
[DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")] [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")]
static extern unsafe ulong* NativeCall(); private static extern unsafe ulong* NativeCall();
// These are from ScriptHookV's nativeCaller.h // These are from ScriptHookV's nativeCaller.h
static unsafe void NativePush<T>(T val) where T : unmanaged private static unsafe void NativePush<T>(T val) where T : unmanaged
{ {
ulong val64 = 0; ulong val64 = 0;
*(T*)(&val64) = val; *(T*)(&val64) = val;
NativePush64(val64); NativePush64(val64);
} }
public static unsafe R Invoke<R>(ulong hash) where R : unmanaged public static unsafe R Invoke<R>(ulong hash) where R : unmanaged
{ {
NativeInit(hash); NativeInit(hash);
return *(R*)(NativeCall()); return *(R*)(NativeCall());
} }
public static unsafe R Invoke<R>(Hash hash, params object[] args) public static unsafe R Invoke<R>(Hash hash, params object[] args)
where R : unmanaged where R : unmanaged
{ {
NativeInit((ulong)hash); NativeInit((ulong)hash);
var arguments=ConvertPrimitiveArguments(args); var arguments = ConvertPrimitiveArguments(args);
foreach (var arg in arguments) foreach (var arg in arguments)
NativePush(arg); NativePush(arg);
return *(R*)(NativeCall()); return *(R*)(NativeCall());
} }
/// <summary> /// <summary>
/// Helper function that converts an array of primitive values to a native stack. /// Helper function that converts an array of primitive values to a native stack.
/// </summary> /// </summary>
/// <param name="args"></param> /// <param name="args"></param>
/// <returns></returns> /// <returns></returns>
static unsafe ulong[] ConvertPrimitiveArguments(object[] args) private static unsafe ulong[] ConvertPrimitiveArguments(object[] args)
{ {
var result = new ulong[args.Length]; var result = new ulong[args.Length];
for (int i = 0; i < args.Length; ++i) for (int i = 0; i < args.Length; ++i)
{ {
if (args[i] is bool valueBool) if (args[i] is bool valueBool)
{ {
result[i] = valueBool ? 1ul : 0ul; result[i] = valueBool ? 1ul : 0ul;
continue; continue;
} }
if (args[i] is byte valueByte) if (args[i] is byte valueByte)
{ {
result[i] = (ulong)valueByte; result[i] = valueByte;
continue; continue;
} }
if (args[i] is int valueInt32) if (args[i] is int valueInt32)
{ {
result[i] = (ulong)valueInt32; result[i] = (ulong)valueInt32;
continue; continue;
} }
if (args[i] is ulong valueUInt64) if (args[i] is ulong valueUInt64)
{ {
result[i] = valueUInt64; result[i] = valueUInt64;
continue; continue;
} }
if (args[i] is float valueFloat) if (args[i] is float valueFloat)
{ {
result[i] = *(ulong*)&valueFloat; result[i] = *(ulong*)&valueFloat;
continue; continue;
} }
if (args[i] is IntPtr valueIntPtr) if (args[i] is IntPtr valueIntPtr)
{ {
result[i] = (ulong)valueIntPtr.ToInt64(); result[i] = (ulong)valueIntPtr.ToInt64();
continue; 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.Math;
using GTA.Native; using GTA.Native;
using RageCoop.Core; using RageCoop.Core;
using System.Collections.Generic;
using System; using System;
using System.Collections.Generic;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -80,9 +80,9 @@ namespace RageCoop.Client
var result = new byte[36]; var result = new byte[36];
for (byte i = 0; i < 12; i++) for (byte i = 0; i < 12; i++)
{ {
result[i]=(byte)Function.Call<short>(Hash.GET_PED_DRAWABLE_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 + 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 + 24] = (byte)Function.Call<short>(Hash.GET_PED_PALETTE_VARIATION, ped.Handle, i);
} }
return result; return result;
} }
@ -108,7 +108,7 @@ namespace RageCoop.Client
} }
// Fake death // Fake death
if (ped.IsRagdoll || (ped.Health==1 && ped.IsPlayer)) if (ped.IsRagdoll || (ped.Health == 1 && ped.IsPlayer))
{ {
flags |= PedDataFlags.IsRagdoll; flags |= PedDataFlags.IsRagdoll;
} }
@ -144,17 +144,23 @@ namespace RageCoop.Client
flags |= PedDataFlags.IsInCover; flags |= PedDataFlags.IsInCover;
if (ped.IsInCoverFacingLeft) if (ped.IsInCoverFacingLeft)
{ {
flags |=PedDataFlags.IsInCover; flags |= PedDataFlags.IsInCover;
} }
if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped)){ if (!Function.Call<bool>(Hash.IS_PED_IN_HIGH_COVER, ped))
flags|=PedDataFlags.IsInLowCover; {
flags |= PedDataFlags.IsInLowCover;
} }
if (ped.IsTaskActive(TaskType.CTaskAimGunBlindFire)) 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)) if (Function.Call<bool>(Hash.GET_PED_STEALTH_MOVEMENT, ped))
{ {
flags |= PedDataFlags.IsInStealthMode; flags |= PedDataFlags.IsInStealthMode;
@ -271,7 +277,7 @@ namespace RageCoop.Client
veh, veh,
veh.Bones[text].Index 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) if (flag2)
{ {
num = num2; num = num2;
@ -290,7 +296,7 @@ namespace RageCoop.Client
{ {
result = true; result = true;
} }
else else if (veh.GetPedOnSeat(seat) != null)
{ {
bool isDead = veh.GetPedOnSeat(seat).IsDead; bool isDead = veh.GetPedOnSeat(seat).IsDead;
@ -325,29 +331,29 @@ namespace RageCoop.Client
var v = p.CurrentVehicle; var v = p.CurrentVehicle;
// Rhino // 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 (p.IsOnTurretSeat()) { return p.GetLookingCoord(); }
if (weapon!=null) if (weapon != null)
{ {
// Not very accurate, but doesn't matter // Not very accurate, but doesn't matter
Vector3 dir = weapon.RightVector; Vector3 dir = weapon.RightVector;
return weapon.Position+dir*20; return weapon.Position + dir * 20;
} }
return GetLookingCoord(p); return GetLookingCoord(p);
} }
public static Vector3 GetLookingCoord(this Ped p) public static Vector3 GetLookingCoord(this Ped p)
{ {
if (p==Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE)==4) if (p == Main.P && Function.Call<int>(Hash.GET_FOLLOW_PED_CAM_VIEW_MODE) == 4)
{ {
return RaycastEverything(default); return RaycastEverything(default);
} }
EntityBone b = p.Bones[Bone.FacialForehead]; EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized; Vector3 v = b.UpVector.Normalized;
return b.Position+200*v; return b.Position + 200 * v;
} }
public static VehicleSeat GetSeatTryingToEnter(this Ped p) public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{ {
@ -470,7 +476,7 @@ namespace RageCoop.Client
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3; || (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
case 0: case 0:
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc return (VehicleHash)veh.Model.Hash == VehicleHash.Apc
|| (VehicleHash)veh.Model.Hash==VehicleHash.Dune3; || (VehicleHash)veh.Model.Hash == VehicleHash.Dune3;
case 1: case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2 || (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2

View File

@ -6,13 +6,12 @@ using System;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Collections.Generic;
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
namespace RageCoop.Client namespace RageCoop.Client
{ {
internal static class Util internal static class Util
@ -40,7 +39,7 @@ namespace RageCoop.Client
var res = ResolutionMaintainRatio; var res = ResolutionMaintainRatio;
if (Function.Call<bool>(Hash.GET_SCREEN_COORD_FROM_WORLD_COORD, pos.X, pos.Y, pos.Z, &x, &y)) 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; return true;
} }
} }
@ -112,11 +111,11 @@ namespace RageCoop.Client
#endregion #endregion
public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml"; public static string SettingsPath = "Scripts\\RageCoop\\Data\\RageCoop.Client.Settings.xml";
public static Settings ReadSettings() public static Settings ReadSettings(string path = null)
{ {
path = path ?? SettingsPath;
XmlSerializer ser = new XmlSerializer(typeof(Settings)); XmlSerializer ser = new XmlSerializer(typeof(Settings));
string path = SettingsPath;
Directory.CreateDirectory(Directory.GetParent(path).FullName); Directory.CreateDirectory(Directory.GetParent(path).FullName);
Settings settings = null; Settings settings = null;
@ -124,12 +123,7 @@ namespace RageCoop.Client
{ {
using (FileStream stream = File.OpenRead(path)) using (FileStream stream = File.OpenRead(path))
{ {
settings = (RageCoop.Client.Settings)ser.Deserialize(stream); settings = (Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
} }
} }
else else
@ -142,22 +136,25 @@ namespace RageCoop.Client
return settings; return settings;
} }
public static void SaveSettings() public static bool SaveSettings(string path = null, Settings settings = null)
{ {
try try
{ {
string path = SettingsPath; path = path ?? SettingsPath;
settings = settings ?? Main.Settings;
Directory.CreateDirectory(Directory.GetParent(path).FullName); Directory.CreateDirectory(Directory.GetParent(path).FullName);
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite)) using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{ {
XmlSerializer ser = new XmlSerializer(typeof(Settings)); XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings); ser.Serialize(stream, settings);
} }
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
GTA.UI.Notification.Show("Error saving player settings: " + ex.Message); return false;
// GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
} }
} }
@ -192,7 +189,7 @@ namespace RageCoop.Client
{ {
if (p == null) { return null; } if (p == null) { return null; }
var c = EntityPool.GetPedByHandle(p.Handle); 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; return c;
} }
@ -200,7 +197,7 @@ namespace RageCoop.Client
{ {
if (veh == null) { return null; } if (veh == null) { return null; }
var v = EntityPool.GetVehicleByHandle(veh.Handle); 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; return v;
} }
@ -219,7 +216,7 @@ namespace RageCoop.Client
#region WIN32 #region WIN32
const UInt32 WM_KEYDOWN = 0x0100; private const UInt32 WM_KEYDOWN = 0x0100;
public static void Reload() public static void Reload()
{ {
string reloadKey = "None"; string reloadKey = "None";
@ -227,18 +224,19 @@ namespace RageCoop.Client
foreach (var l in lines) foreach (var l in lines)
{ {
var ss = l.Split('='); var ss = l.Split('=');
if (ss.Length > 0 && ss[0]=="ReloadKey") if (ss.Length > 0 && ss[0] == "ReloadKey")
{ {
reloadKey = ss[1]; reloadKey = ss[1];
} }
} }
var lineList = lines.ToList(); var lineList = lines.ToList();
if (reloadKey=="None") if (reloadKey == "None")
{ {
foreach (var l in lines) foreach (var l in lines)
{ {
var ss = l.Split('='); 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]; reloadKey = ss[1];
lineList.Remove(l); lineList.Remove(l);
@ -253,7 +251,7 @@ namespace RageCoop.Client
// Move log file so it doesn't get deleted // Move log file so it doesn't get deleted
Main.Logger.Dispose(); Main.Logger.Dispose();
var path = Main.Logger.LogPath+".last.log"; var path = Main.Logger.LogPath + ".last.log";
try try
{ {
if (File.Exists(path)) { File.Delete(path); } if (File.Exists(path)) { File.Delete(path); }
@ -268,7 +266,7 @@ namespace RageCoop.Client
} }
[DllImport("user32.dll")] [DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam); private static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]

View File

@ -12,7 +12,7 @@ namespace RageCoop.Client
public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v) public static VehicleDataFlags GetVehicleFlags(this SyncedVehicle v)
{ {
var veh=v.MainVehicle; var veh = v.MainVehicle;
VehicleDataFlags flags = 0; VehicleDataFlags flags = 0;
if (veh.IsEngineRunning) if (veh.IsEngineRunning)
@ -50,51 +50,58 @@ namespace RageCoop.Client
flags |= VehicleDataFlags.IsHornActive; 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; flags |= VehicleDataFlags.IsTransformed;
} }
if (v.IsAircraft) if (v.IsAircraft)
{ {
flags |= VehicleDataFlags.IsAircraft; flags |= VehicleDataFlags.IsAircraft;
} }
if (v.IsDeluxo && veh.IsDeluxoHovering()) if (v.IsDeluxo && veh.IsDeluxoHovering())
{ {
flags|= VehicleDataFlags.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;
} }
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; return flags;
} }
public static bool IsRocketBoostActive(this Vehicle veh) 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){ public static bool IsParachuteActive(this Vehicle veh)
return Function.Call<bool>((Hash)0x3DE51E9C80B116CF,veh);
}
public static void SetRocketBoostActive(this Vehicle veh,bool toggle)
{ {
Function.Call(Hash._SET_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) public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
{ {
@ -133,7 +140,6 @@ namespace RageCoop.Client
} }
} }
// Bursted tires // Bursted tires
short burstedTires = 0; short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels()) foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
@ -176,7 +182,7 @@ namespace RageCoop.Client
} }
if ((model.OpenedDoors & (byte)(1 << i)) != 0) if ((model.OpenedDoors & (byte)(1 << i)) != 0)
{ {
if ((!door.IsOpen)&&(!door.IsBroken)) if ((!door.IsOpen) && (!door.IsBroken))
{ {
door.Open(); door.Open();
} }
@ -220,7 +226,7 @@ namespace RageCoop.Client
{ {
Dictionary<int, int> ps = new Dictionary<int, int>(); Dictionary<int, int> ps = new Dictionary<int, int>();
var d = veh.Driver; var d = veh.Driver;
if (d!=null&&d.IsSittingInVehicle()) if (d != null && d.IsSittingInVehicle())
{ {
ps.Add(-1, d.GetSyncEntity().ID); ps.Add(-1, d.GetSyncEntity().ID);
} }
@ -228,32 +234,31 @@ namespace RageCoop.Client
{ {
if (p.IsSittingInVehicle()) if (p.IsSittingInVehicle())
{ {
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID); ps.Add((int)p.SeatIndex, p.GetSyncEntity().ID);
} }
} }
return ps; return ps;
} }
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover) public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
{ {
Function.Call(Hash._SET_VEHICLE_HOVER_TRANSFORM_PERCENTAGE, deluxo, hover ? 1f : 0f); Function.Call(Hash.SET_SPECIAL_FLIGHT_MODE_TARGET_RATIO, deluxo, hover ? 1f : 0f);
} }
public static bool IsDeluxoHovering(this Vehicle deluxo) public static bool IsDeluxoHovering(this Vehicle deluxo)
{ {
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) public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
{ {
Function.Call(Hash._SET_SPECIALFLIGHT_WING_RATIO, v, ratio); Function.Call(Hash.SET_HOVER_MODE_WING_RATIO, v, ratio);
} }
public static float GetDeluxoWingRatio(this Vehicle v) public static float GetDeluxoWingRatio(this Vehicle v)
{ {
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) public static float GetNozzleAngel(this Vehicle plane)
{ {
return Function.Call<float>(Hash._GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane); return Function.Call<float>(Hash.GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
} }
public static bool HasNozzle(this Vehicle v) public static bool HasNozzle(this Vehicle v)
{ {
@ -283,8 +288,6 @@ namespace RageCoop.Client
{ {
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio); Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
} }
#endregion #endregion
} }
} }

View File

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

View File

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

View File

@ -1,15 +1,13 @@
using System; using GTA.Math;
using System.Text;
using System.Linq;
using GTA.Math;
using System.IO; using System.IO;
using System.Text;
namespace RageCoop.Core 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 GTA.Math;
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 Lidgren.Network; using Lidgren.Network;
using Newtonsoft.Json; 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.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")] [assembly: InternalsVisibleTo("RageCoop.Client")]
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal static class CoreUtils internal static class CoreUtils
@ -31,43 +34,44 @@ namespace RageCoop.Core
{ {
return ToIgnore.Contains(name); return ToIgnore.Contains(name);
} }
public static (byte, byte[]) GetBytesFromObject(object obj) public static void GetBytesFromObject(object obj, NetOutgoingMessage m)
{ {
switch (obj) switch (obj)
{ {
case byte _: case byte value:
return (0x01, new byte[] { (byte)obj }); m.Write((byte)0x01); m.Write(value); break;
case short _: case short value:
return (0x02, BitConverter.GetBytes((short)obj)); m.Write((byte)0x02); m.Write(value); break;
case ushort _: case ushort value:
return (0x03, BitConverter.GetBytes((ushort)obj)); m.Write((byte)0x03); m.Write(value); break;
case int _: case int value:
return (0x04, BitConverter.GetBytes((int)obj)); m.Write((byte)0x04); m.Write(value); break;
case uint _: case uint value:
return (0x05, BitConverter.GetBytes((uint)obj)); m.Write((byte)0x05); m.Write(value); break;
case long _: case long value:
return (0x06, BitConverter.GetBytes((long)obj)); m.Write((byte)0x06); m.Write(value); break;
case ulong _: case ulong value:
return (0x07, BitConverter.GetBytes((ulong)obj)); m.Write((byte)0x07); m.Write(value); break;
case float _: case float value:
return (0x08, BitConverter.GetBytes((float)obj)); m.Write((byte)0x08); m.Write(value); break;
case bool _: case bool value:
return (0x09, BitConverter.GetBytes((bool)obj)); m.Write((byte)0x09); m.Write(value); break;
case string _: case string value:
return (0x10, ((string)obj).GetBytesWithLength()); m.Write((byte)0x10); m.Write(value); break;
case Vector3 _: case Vector3 value:
return (0x11,((Vector3)obj).GetBytes()); m.Write((byte)0x11); m.Write(value); break;
case Quaternion _: case Quaternion value:
return (0x12, ((Quaternion)obj).GetBytes()); m.Write((byte)0x12); m.Write(value); break;
case GTA.Model _: case GTA.Model value:
return (0x13, BitConverter.GetBytes((GTA.Model)obj)); m.Write((byte)0x13); m.Write(value); break;
case Vector2 _: case Vector2 value:
return (0x14, ((Vector2)obj).GetBytes()); m.Write((byte)0x14); m.Write(value); break;
case Tuple<byte, byte[]> _: case byte[] value:
var tup = (Tuple<byte, byte[]>)obj; m.Write((byte)0x15); m.WriteByteArray(value); break;
return (tup.Item1, tup.Item2); case Tuple<byte, byte[]> value:
m.Write(value.Item1); m.Write(value.Item2); break;
default: default:
return (0x0, null); throw new Exception("Unsupported object type: " + obj.GetType());
} }
} }
public static IPEndPoint StringToEndPoint(string endpointstring) public static IPEndPoint StringToEndPoint(string endpointstring)
@ -134,9 +138,8 @@ namespace RageCoop.Core
private static int getPort(string p) 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.MinPort
|| port > IPEndPoint.MaxPort) || port > IPEndPoint.MaxPort)
{ {
@ -145,7 +148,7 @@ namespace RageCoop.Core
return port; 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)) using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{ {
@ -181,10 +184,49 @@ namespace RageCoop.Core
return JsonConvert.DeserializeObject<IpInfo>(content); 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")] [JsonProperty("ip")]
public string Address { get; set; } public string Address { get; set; }
@ -194,74 +236,10 @@ namespace RageCoop.Core
} }
internal static class Extensions internal static class Extensions
{ {
public static void AddVector3(this List<byte> bytes, Vector3 vec3)
{
bytes.AddRange(BitConverter.GetBytes(vec3.X));
bytes.AddRange(BitConverter.GetBytes(vec3.Y));
bytes.AddRange(BitConverter.GetBytes(vec3.Z));
}
public static void AddQuaternion(this List<byte> bytes, Quaternion quat)
{
bytes.AddRange(BitConverter.GetBytes(quat.X));
bytes.AddRange(BitConverter.GetBytes(quat.Y));
bytes.AddRange(BitConverter.GetBytes(quat.Z));
bytes.AddRange(BitConverter.GetBytes(quat.W));
}
public static void AddInt(this List<byte> bytes,int i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUint(this List<byte> bytes, uint i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddShort(this List<byte> bytes, short i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUshort(this List<byte> bytes, ushort i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddLong(this List<byte> bytes, long i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddUlong(this List<byte> bytes, ulong i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void AddFloat(this List<byte> bytes, float i)
{
bytes.AddRange(BitConverter.GetBytes(i));
}
public static void 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) public static byte[] GetBytes(this string s)
{ {
return Encoding.UTF8.GetBytes(s); return Encoding.UTF8.GetBytes(s);
} }
public static byte[] GetBytesWithLength(this string s)
{
var data = new List<byte>(100);
var sb = Encoding.UTF8.GetBytes(s);
data.AddInt(sb.Length);
data.AddRange(sb);
return data.ToArray();
}
public static string GetString(this byte[] data) public static string GetString(this byte[] data)
{ {
return Encoding.UTF8.GetString(data); return Encoding.UTF8.GetString(data);
@ -288,35 +266,28 @@ namespace RageCoop.Core
// 16 bytes // 16 bytes
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
} }
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
public static T GetPacket<T>(this NetIncomingMessage msg, T existingPacket = null) where T : Packet, new()
{ {
msg.ReadByte(); var p = new T();
return GetPacket<T>(msg.ReadBytes(msg.ReadInt32()),existingPacket); p.Deserialize(msg);
}
public static T GetPacket<T>(this byte[] data, T existingPacket=null) where T : Packet, new()
{
var p = existingPacket??new T();
p.Deserialize(data);
return p; return p;
} }
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag) public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static bool HasProjDataFlag(this ProjectileDataFlags flagToCheck, ProjectileDataFlags flag) 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) 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) public static bool HasConfigFlag(this PlayerConfigFlags flagToCheck, PlayerConfigFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag) != 0;
} }
public static Type GetActualType(this TypeCode code) public static Type GetActualType(this TypeCode code)
{ {
@ -383,19 +354,19 @@ namespace RageCoop.Core
public static string DumpWithType(this IEnumerable<object> objects) public static string DumpWithType(this IEnumerable<object> objects)
{ {
StringBuilder sb = new StringBuilder(); 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(); return sb.ToString();
} }
public static string Dump<T>(this IEnumerable<T> objects) public static string Dump<T>(this IEnumerable<T> objects)
{ {
return "{"+string.Join(",",objects)+"}"; return $"{{{string.Join(",", objects)}}}";
} }
public static void ForEach<T>(this IEnumerable<T> objects,Action<T> action) public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action)
{ {
foreach(var obj in objects) foreach (var obj in objects)
{ {
action(obj); action(obj);
} }
@ -411,10 +382,16 @@ namespace RageCoop.Core
return memoryStream.ToArray(); 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 memoryStream = new MemoryStream();
var output = lengthPerArray== -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count*lengthPerArray]; 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; int writeIdx = 0;
foreach (var byteArr in arrays) foreach (var byteArr in arrays)
{ {
@ -468,5 +445,6 @@ namespace RageCoop.Core
{ {
return IPAddress.Parse(ip); return IPAddress.Parse(ip);
} }
} }
} }

View File

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

View File

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

View File

@ -1,7 +1,6 @@
using System; using Lidgren.Network;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading; using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,22 +9,22 @@ namespace RageCoop.Core
{ {
public EventHandler<NetIncomingMessage> OnMessageReceived; public EventHandler<NetIncomingMessage> OnMessageReceived;
private readonly Thread ListenerThread; private readonly Thread ListenerThread;
private bool _stopping=false; private bool _stopping = false;
public CoopPeer(NetPeerConfiguration config):base(config) public CoopPeer(NetPeerConfiguration config) : base(config)
{ {
Start(); Start();
NetIncomingMessage msg; NetIncomingMessage msg;
ListenerThread=new Thread(() => ListenerThread = new Thread(() =>
{ {
while (!_stopping) while (!_stopping)
{ {
msg=WaitMessage(200); msg = WaitMessage(200);
if (msg!=null) if (msg != null)
{ {
OnMessageReceived?.Invoke(this,msg); OnMessageReceived?.Invoke(this, msg);
} }
} }
}); });
ListenerThread.Start(); ListenerThread.Start();
} }
@ -34,7 +33,7 @@ namespace RageCoop.Core
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_stopping=true; _stopping = true;
Shutdown("Bye!"); Shutdown("Bye!");
ListenerThread.Join(); ListenerThread.Join();
} }
@ -51,12 +50,11 @@ namespace RageCoop.Core
p.Pack(outgoingMessage); p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connections, method, (int)channel); 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(); NetOutgoingMessage outgoingMessage = CreateMessage();
p.Pack(outgoingMessage); p.Pack(outgoingMessage);
SendMessage(outgoingMessage, cons, method, (int)channel); 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;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal class PublicKey{ internal class PublicKey
public PublicKey(){ {
public PublicKey()
{
} }
public static PublicKey FromServerInfo(ServerInfo info){ public static PublicKey FromServerInfo(ServerInfo info)
return new PublicKey{ {
Modulus=Convert.FromBase64String(info.publicKeyModulus), return new PublicKey
Exponent=Convert.FromBase64String(info.publicKeyExponent) {
Modulus = Convert.FromBase64String(info.publicKeyModulus),
Exponent = Convert.FromBase64String(info.publicKeyExponent)
}; };
} }
public byte[] Modulus; public byte[] Modulus;

View File

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

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.Net;
using Newtonsoft.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace RageCoop.Core namespace RageCoop.Core
@ -15,13 +13,13 @@ namespace RageCoop.Core
public ZeroTierNetwork(string line) public ZeroTierNetwork(string line)
{ {
// <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips> // <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
var v = Regex.Split(line," ").Skip(2).ToArray(); var v = Regex.Split(line, " ").Skip(2).ToArray();
ID=v[0]; ID = v[0];
Name=v[1]; Name = v[1];
Mac=v[2]; Mac = v[2];
Status=v[3]; Status = v[3];
Type=v[4]; Type = v[4];
Device=v[5]; Device = v[5];
foreach (var i in v[6].Split(',')) foreach (var i in v[6].Split(','))
{ {
Addresses.Add(i.Split('/')[0]); Addresses.Add(i.Split('/')[0]);
@ -33,43 +31,43 @@ namespace RageCoop.Core
public string Status; public string Status;
public string Type; public string Type;
public string Device; public string Device;
public List<string> Addresses=new List<string>(); public List<string> Addresses = new List<string>();
} }
internal static class ZeroTierHelper internal static class ZeroTierHelper
{ {
private static readonly string _path="zerotier-cli"; private static readonly string _path = "zerotier-cli";
private static readonly string _arg = ""; private static readonly string _arg = "";
static ZeroTierHelper() static ZeroTierHelper()
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
var batpath= Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat"); var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat");
_arg=$"/c \"{batpath}\" "; _arg = $"/c \"{batpath}\" ";
_path="cmd.exe"; _path = "cmd.exe";
} }
var status = RunCommand("status"); var status = RunCommand("status");
if (!status.StartsWith("200")) 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(); var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK")) 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; int i = 0;
while (i<=timeout) while (i <= timeout)
{ {
i+=100; i += 100;
if(Networks.TryGetValue(networkId,out var n)) 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; return n;
} }
@ -84,16 +82,17 @@ namespace RageCoop.Core
} }
public static void Leave(string networkId) public static void Leave(string networkId)
{ {
var p = Run("leave "+networkId); var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd(); var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK")) 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 public static Dictionary<string, ZeroTierNetwork> Networks
{ {
get { get
{
Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>(); Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>();
var p = Run("listnetworks"); var p = Run("listnetworks");
var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1); var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
@ -113,14 +112,14 @@ namespace RageCoop.Core
private static Process Run(string args) private static Process Run(string args)
{ {
var p = new Process(); var p = new Process();
p.StartInfo=new ProcessStartInfo() p.StartInfo = new ProcessStartInfo()
{ {
FileName = _path, FileName = _path,
Arguments =_arg+args, Arguments = _arg + args,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = true,
CreateNoWindow=true, CreateNoWindow = true,
}; };
p.Start(); p.Start();
p.WaitForExit(); p.WaitForExit();
@ -129,7 +128,11 @@ namespace RageCoop.Core
private static string RunCommand(string command) private static string RunCommand(string command)
{ {
var p = Run(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 Lidgren.Network;
using System.Collections.Generic; using System;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -10,9 +9,9 @@ namespace RageCoop.Core
internal class ChatMessage : Packet internal class ChatMessage : Packet
{ {
public override PacketType Type => PacketType.ChatMessage; public override PacketType Type => PacketType.ChatMessage;
private Func<string, byte[]> crypt; private readonly Func<string, byte[]> crypt;
private Func<byte[], byte[]> decrypt; private readonly Func<byte[], byte[]> decrypt;
public ChatMessage(Func<string, byte[]> crypter) public ChatMessage(Func<string, byte[]> crypter)
{ {
crypt = crypter; crypt = crypter;
@ -25,33 +24,31 @@ namespace RageCoop.Core
public string Message { get; set; } public string Message { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// Write Username // Write Username
byteArray.AddString(Username); m.Write(Username);
// Write Message // Write Message
byteArray.AddArray(crypt(Message)); m.WriteByteArray(crypt(Message));
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read username // Read username
Username = reader.ReadString(); Username = m.ReadString();
Message = decrypt(reader.ReadByteArray()).GetString(); Message = decrypt(m.ReadByteArray()).GetString();
#endregion #endregion
} }
} }

View File

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

View File

@ -1,18 +1,15 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
internal enum FileResponse:byte internal enum FileResponse : byte
{ {
NeedToDownload=0, NeedToDownload = 0,
AlreadyExists=1, AlreadyExists = 1,
Completed=2, Completed = 2,
Loaded=3, Loaded = 3,
LoadFailed=4, LoadFailed = 4,
} }
internal partial class Packets internal partial class Packets
{ {
@ -25,34 +22,30 @@ namespace RageCoop.Core
public long FileLength { get; set; } 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 // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
// The name of the file // The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(Name); m.Write(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
// The length of the file // The length of the file
byteArray.AddRange(BitConverter.GetBytes(FileLength)); m.Write(FileLength);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32();
Name = reader.ReadString(); ID = m.ReadInt32();
FileLength = reader.ReadInt64(); Name = m.ReadString();
FileLength = m.ReadInt64();
} }
} }
@ -61,86 +54,74 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.FileTransferResponse; public override PacketType Type => PacketType.FileTransferResponse;
public int ID { get; set; } public int ID { get; set; }
public FileResponse Response { get; set; } public FileResponse Response { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
byteArray.Add((byte)Response); m.Write((byte)Response);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
Response = (FileResponse)reader.ReadByte(); Response = (FileResponse)m.ReadByte();
} }
} }
internal class FileTransferChunk : Packet internal class FileTransferChunk : Packet
{ {
public override PacketType Type => PacketType.FileTransferChunk; public override PacketType Type => PacketType.FileTransferChunk;
public int ID { get; set; } public int ID { get; set; }
public byte[] FileChunk { get; set; } public byte[] FileChunk { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
// The ID from the download // The ID from the download
byteArray.AddInt(ID); m.Write(ID);
m.WriteByteArray(FileChunk);
// The chunk of the file
byteArray.AddInt(FileChunk.Length);
byteArray.AddRange(FileChunk);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
BitReader reader = new BitReader(array);
ID = reader.ReadInt32(); ID = m.ReadInt32();
FileChunk = reader.ReadByteArray(); FileChunk = m.ReadByteArray();
} }
} }
internal class FileTransferComplete : Packet internal class FileTransferComplete : Packet
{ {
public override PacketType Type => PacketType.FileTransferComplete; public override PacketType Type => PacketType.FileTransferComplete;
public int ID { get; set; } 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 // The ID for the download
byteArray.AddInt(ID); m.Write(ID);
return byteArray.ToArray();
} }
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 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 Lidgren.Network;
using System.Collections.Generic;
using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -14,26 +12,26 @@ namespace RageCoop.Core
public string TargetInternal { get; set; } public string TargetInternal { get; set; }
public string TargetExternal { get; set; } public string TargetExternal { get; set; }
public bool Connect { 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); m.Write(TargetID);
byteArray.AddString(TargetInternal); m.Write(TargetInternal);
byteArray.AddString(TargetExternal); m.Write(TargetExternal);
byteArray.AddBool(Connect); m.Write(Connect);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
TargetID = reader.ReadInt32(); TargetID = m.ReadInt32();
TargetInternal = reader.ReadString(); TargetInternal = m.ReadString();
TargetExternal = reader.ReadString(); TargetExternal = m.ReadString();
Connect=reader.ReadBoolean(); Connect = m.ReadBoolean();
#endregion #endregion
} }
} }
@ -45,23 +43,23 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed /// 1:initial, 2:acknowledged, 3:confirmed
/// </summary> /// </summary>
public byte Status { get;set;} public byte Status { get; set; }
public override byte[] Serialize() protected override void Serialize(NetOutgoingMessage m)
{ {
List<byte> byteArray = new List<byte>();
byteArray.AddInt(Puncher); m.Write(Puncher);
byteArray.Add(Status); m.Write(Status);
return byteArray.ToArray();
} }
public override void Deserialize(byte[] array) public override void Deserialize(NetIncomingMessage m)
{ {
#region NetIncomingMessageToPacket #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
Puncher = reader.ReadInt32(); Puncher = m.ReadInt32();
Status = reader.ReadByte(); Status = m.ReadByte();
#endregion #endregion
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -10,29 +7,29 @@ namespace RageCoop.Core
{ {
internal class NozzleTransform : Packet internal class NozzleTransform : Packet
{ {
public override PacketType Type => PacketType.NozzleTransform; public override PacketType Type => PacketType.NozzleTransform;
public int VehicleID { get; set; } public int VehicleID { get; set; }
public bool Hover { get; set; } public bool Hover { get; set; }
public override 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 #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VehicleID=reader.ReadInt32(); VehicleID = m.ReadInt32();
Hover=reader.ReadBoolean(); Hover = m.ReadBoolean();
#endregion #endregion
} }

View File

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

View File

@ -1,7 +1,4 @@
using System; 
using System.Collections.Generic;
using System.Text;
using Lidgren.Network; using Lidgren.Network;
namespace RageCoop.Core namespace RageCoop.Core
@ -11,25 +8,25 @@ namespace RageCoop.Core
internal class PedKilled : Packet internal class PedKilled : Packet
{ {
public override PacketType Type => PacketType.PedKilled; public override PacketType Type => PacketType.PedKilled;
public int VictimID { get; set; } 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 #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VictimID=reader.ReadInt32();
VictimID = m.ReadInt32();
#endregion #endregion
} }

View File

@ -1,7 +1,5 @@
using System; using GTA.Math;
using System.Collections.Generic; using Lidgren.Network;
using System.Text;
using GTA.Math;
namespace RageCoop.Core namespace RageCoop.Core
{ {
@ -18,31 +16,31 @@ namespace RageCoop.Core
public Vector3 StartPosition { get; set; } public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; } public Vector3 EndPosition { get; set; }
public override 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 #region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
OwnerID=reader.ReadInt32();
Bone=reader.ReadUInt16(); OwnerID = m.ReadInt32();
WeaponHash=reader.ReadUInt32(); Bone = m.ReadUInt16();
StartPosition=reader.ReadVector3(); WeaponHash = m.ReadUInt32();
EndPosition=reader.ReadVector3(); StartPosition = m.ReadVector3();
EndPosition = m.ReadVector3();
#endregion #endregion
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,17 @@
using System; using System;
using System.Threading;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading;
namespace RageCoop.Core namespace RageCoop.Core
{ {
/// <summary> /// <summary>
/// A worker that constantly execute jobs in a background thread. /// A worker that constantly execute jobs in a background thread.
/// </summary> /// </summary>
public class Worker:IDisposable public class Worker : IDisposable
{ {
private SemaphoreSlim _semaphoreSlim; private readonly SemaphoreSlim _semaphoreSlim;
private Thread _workerThread; private readonly Thread _workerThread;
private bool _stopping=false; private bool _stopping = false;
/// <summary> /// <summary>
/// Name of the worker /// Name of the worker
/// </summary> /// </summary>
@ -19,37 +19,37 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// Whether this worker is busy executing job(s). /// Whether this worker is busy executing job(s).
/// </summary> /// </summary>
public bool IsBusy { get;private set; } public bool IsBusy { get; private set; }
internal Worker(string name,Logger logger,int maxJobs = Int32.MaxValue) internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue)
{ {
Name = name; Name = name;
_semaphoreSlim = new SemaphoreSlim(0,maxJobs); _semaphoreSlim = new SemaphoreSlim(0, maxJobs);
_workerThread=new Thread(() => _workerThread = new Thread(() =>
{ {
while (!_stopping) while (!_stopping)
{ {
IsBusy=false; IsBusy = false;
_semaphoreSlim.Wait(); _semaphoreSlim.Wait();
if(Jobs.TryDequeue(out var job)) if (Jobs.TryDequeue(out var job))
{ {
IsBusy=true; IsBusy = true;
try try
{ {
job.Invoke(); job.Invoke();
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error("Error occurred when executing queued job:"); logger.Error("Error occurred when executing queued job:");
logger.Error(ex); logger.Error(ex);
} }
} }
else else
{ {
throw new InvalidOperationException("Hmm... that's unexpected."); throw new InvalidOperationException("Hmm... that's unexpected.");
} }
} }
IsBusy=false; IsBusy = false;
}); });
_workerThread.Start(); _workerThread.Start();
} }
/// <summary> /// <summary>
@ -66,7 +66,7 @@ namespace RageCoop.Core
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
_stopping=true; _stopping = true;
QueueJob(() => { }); QueueJob(() => { });
if (_workerThread.IsAlive) if (_workerThread.IsAlive)
{ {
@ -81,6 +81,6 @@ namespace RageCoop.Core
Stop(); Stop();
_semaphoreSlim.Dispose(); _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 Lidgren.Network;
using System.Collections.Generic;
using RageCoop.Core; using RageCoop.Core;
using Lidgren.Network;
using System.Diagnostics;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System.Security.Cryptography;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Security.Cryptography;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -18,12 +18,17 @@ namespace RageCoop.Server
private readonly Server Server; private readonly Server Server;
internal Client(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> /// <summary>
/// Th client's IP address and port. /// Th client's IP address and port.
/// </summary> /// </summary>
public IPEndPoint EndPoint { get { return Connection?.RemoteEndPoint; } } public IPEndPoint EndPoint => Connection?.RemoteEndPoint;
/// <summary> /// <summary>
/// Internal(LAN) address of this client, used for NAT hole-punching /// Internal(LAN) address of this client, used for NAT hole-punching
@ -31,7 +36,7 @@ namespace RageCoop.Server
public IPEndPoint InternalEndPoint { get; internal set; } public IPEndPoint InternalEndPoint { get; internal set; }
internal long NetHandle = 0; internal long NetHandle = 0;
internal NetConnection Connection { get;set; } internal NetConnection Connection { get; set; }
/// <summary> /// <summary>
/// The <see cref="ServerPed"/> instance representing the client's main character. /// The <see cref="ServerPed"/> instance representing the client's main character.
/// </summary> /// </summary>
@ -39,45 +44,47 @@ namespace RageCoop.Server
/// <summary> /// <summary>
/// The client's latency in seconds. /// The client's latency in seconds.
/// </summary> /// </summary>
public float Latency => Connection.AverageRoundtripTime/2; public float Latency => Connection.AverageRoundtripTime / 2;
internal readonly Dictionary<int, Action<object>> Callbacks = new(); internal readonly Dictionary<int, Action<object>> Callbacks = new();
internal byte[] PublicKey { get; set; } internal byte[] PublicKey { get; set; }
/// <summary> /// <summary>
/// Indicates whether the client has succefully loaded all resources. /// Indicates whether the client has succefully loaded all resources.
/// </summary> /// </summary>
public bool IsReady { get; internal set; }=false; public bool IsReady { get; internal set; } = false;
/// <summary> /// <summary>
/// The client's username. /// The client's username.
/// </summary> /// </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> /// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped. /// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary> /// </summary>
public bool EnableAutoRespawn { public bool EnableAutoRespawn
get { return _autoRespawn; } {
set { get => _autoRespawn;
BaseScript.SetAutoRespawn(this,value); set
_autoRespawn=value; {
BaseScript.SetAutoRespawn(this, value);
_autoRespawn = value;
} }
} }
private bool _displayNameTag=true; private bool _displayNameTag = true;
private Stopwatch _latencyWatch = new Stopwatch(); private readonly Stopwatch _latencyWatch = new Stopwatch();
/// <summary> /// <summary>
/// Gets or sets whether to enable automatic respawn for this client's main ped. /// Gets or sets whether to enable automatic respawn for this client's main ped.
/// </summary> /// </summary>
public bool DisplayNameTag public bool DisplayNameTag
{ {
get { return _displayNameTag; } get => _displayNameTag;
set set
{ {
Server.BaseScript.SetNameTag(this,value); Server.BaseScript.SetNameTag(this, value);
_displayNameTag=value; _displayNameTag = value;
} }
} }
#region FUNCTIONS #region FUNCTIONS
@ -85,7 +92,7 @@ namespace RageCoop.Server
/// Kick this client /// Kick this client
/// </summary> /// </summary>
/// <param name="reason"></param> /// <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); Connection?.Disconnect(reason);
} }
@ -124,7 +131,7 @@ namespace RageCoop.Server
/// <param name="args"></param> /// <param name="args"></param>
public void SendNativeCall<T>(Action<object> callBack, GTA.Native.Hash hash, params object[] args) 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 }); argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID<T>(callBack), (ulong)hash });
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray()); SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
@ -137,7 +144,7 @@ namespace RageCoop.Server
public void SendNativeCall(GTA.Native.Hash hash, params object[] args) public void SendNativeCall(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)TypeCode.Empty,(ulong)hash }); argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
// Server.Logger?.Debug(argsList.DumpWithType()); // Server.Logger?.Debug(argsList.DumpWithType());
SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray()); SendCustomEventQueued(CustomEvents.NativeCall, argsList.ToArray());
} }
@ -146,7 +153,7 @@ namespace RageCoop.Server
int ID = 0; int ID = 0;
lock (Callbacks) lock (Callbacks)
{ {
while ((ID==0) while ((ID == 0)
|| Callbacks.ContainsKey(ID)) || Callbacks.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -165,7 +172,7 @@ namespace RageCoop.Server
/// </summary> /// </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="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> /// <param name="args">Arguments</param>
public void SendCustomEvent(int hash,params object[] args) public void SendCustomEvent(int hash, params object[] args)
{ {
if (!IsReady) if (!IsReady)
{ {
@ -178,8 +185,8 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent() new Packets.CustomEvent()
{ {
Hash=hash, Hash = hash,
Args=args Args = args
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event); Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);
@ -206,10 +213,10 @@ namespace RageCoop.Server
{ {
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent(null,true) new Packets.CustomEvent(null, true)
{ {
Hash=hash, Hash = hash,
Args=args Args = args
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event); Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);

View File

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

View File

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

View File

@ -1,12 +1,5 @@
using System; using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server namespace RageCoop.Server
{ {
@ -17,19 +10,19 @@ namespace RageCoop.Server
// Send to host // Send to host
Send(new Packets.HolePunchInit Send(new Packets.HolePunchInit
{ {
Connect=false, Connect = false,
TargetID=client.Player.ID, TargetID = client.Player.ID,
TargetInternal=client.InternalEndPoint.ToString(), TargetInternal = client.InternalEndPoint.ToString(),
TargetExternal=client.EndPoint.ToString() TargetExternal = client.EndPoint.ToString()
}, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered); }, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
// Send to client // Send to client
Send(new Packets.HolePunchInit Send(new Packets.HolePunchInit
{ {
Connect=true, Connect = true,
TargetID=host.Player.ID, TargetID = host.Player.ID,
TargetInternal=host.InternalEndPoint.ToString(), TargetInternal = host.InternalEndPoint.ToString(),
TargetExternal=host.EndPoint.ToString() TargetExternal = host.EndPoint.ToString()
}, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered); }, 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 Lidgren.Network;
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 RageCoop.Core; using RageCoop.Core;
using Newtonsoft.Json;
using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting; using RageCoop.Server.Scripting;
using System.Net.Sockets; using System;
using System.Threading.Tasks; using System.Collections.Generic;
using RageCoop.Core.Scripting; 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 namespace RageCoop.Server
{ {
@ -36,23 +30,25 @@ namespace RageCoop.Server
internal ServerEntities Entities; internal ServerEntities Entities;
internal readonly Dictionary<Command, Action<CommandContext>> Commands = new(); 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<string, Client> ClientsByName = new();
internal readonly Dictionary<int, Client> ClientsByID = new(); internal readonly Dictionary<int, Client> ClientsByID = new();
internal Client _hostClient; internal Client _hostClient;
private Dictionary<int,FileTransfer> InProgressFileTransfers=new(); private readonly Dictionary<int, FileTransfer> InProgressFileTransfers = new();
internal Resources Resources; internal Resources Resources;
internal Logger Logger; internal Logger Logger;
private Security Security; internal Security Security;
private bool _stopping = false; private bool _stopping = false;
private Thread _listenerThread; private readonly Thread _listenerThread;
private Thread _announceThread; private readonly Timer _announceTimer = new();
private Thread _latencyThread; private readonly Timer _playerUpdateTimer = new();
private Worker _worker; private readonly Timer _antiAssholesTimer = new();
private HashSet<char> _allowedCharacterSet; private readonly Timer _updateTimer = new();
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new(); private readonly Worker _worker;
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new(); private readonly HashSet<char> _allowedCharacterSet;
private readonly Dictionary<int, Action<PacketType, NetIncomingMessage>> PendingResponses = new();
internal Dictionary<PacketType, Func<NetIncomingMessage, Client, Packet>> RequestHandlers = new();
/// <summary> /// <summary>
/// Get the current server version /// Get the current server version
/// </summary> /// </summary>
@ -63,151 +59,49 @@ namespace RageCoop.Server
/// <param name="settings"></param> /// <param name="settings"></param>
/// <param name="logger"></param> /// <param name="logger"></param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException"></exception>
public Server(Settings settings,Logger logger=null) public Server(Settings settings, Logger logger = null)
{ {
Settings = settings; Settings = settings;
if (settings==null) { throw new ArgumentNullException("Server settings cannot be null!"); } if (settings == null) { throw new ArgumentNullException("Server settings cannot be null!"); }
Logger=logger; Logger = logger;
if (Logger!=null) { Logger.LogLevel=Settings.LogLevel;} if (Logger != null) { Logger.LogLevel = Settings.LogLevel; }
API=new API(this); API = new API(this);
Resources=new Resources(this); Resources = new Resources(this);
Security=new Security(Logger); Security = new Security(Logger);
Entities=new ServerEntities(this); Entities = new ServerEntities(this);
BaseScript=new BaseScript(this); BaseScript = new BaseScript(this);
_allowedCharacterSet=new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray()); _allowedCharacterSet = new HashSet<char>(Settings.AllowedUsernameChars.ToCharArray());
_worker=new Worker("ServerWorker", Logger); _worker = new Worker("ServerWorker", Logger);
_listenerThread=new Thread(() => Listen()); _listenerThread = new Thread(() => Listen());
_latencyThread=new Thread(() =>
_announceTimer.Interval = 1;
_announceTimer.Elapsed += (s, e) =>
{ {
while (!_stopping) _announceTimer.Interval = 10000;
{ _announceTimer.Stop();
foreach(var c in ClientsByNetHandle.Values.ToArray()) Announce();
{ _announceTimer.Start();
try };
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); _playerUpdateTimer.Interval = 1000;
new Packets.PlayerInfoUpdate() _playerUpdateTimer.Elapsed += (s, e) => SendPlayerUpdate();
{
PedID=c.Player.ID,
Username=c.Username, _antiAssholesTimer.Interval = 5000;
Latency=c.Latency, _antiAssholesTimer.Elapsed += (s, e) => KickAssholes();
Position=c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default); _updateTimer.Interval = 1;
} _updateTimer.Elapsed += (s, e) =>
catch(Exception ex)
{
Logger?.Error(ex);
}
}
Thread.Sleep(1000);
}
});
_announceThread=new Thread(async () =>
{ {
try _updateTimer.Interval = 1000 * 60 * 10; // 10 minutes
{ _updateTimer.Stop();
// TLS only CheckUpdate();
ServicePointManager.Expect100Continue = true; _updateTimer.Start();
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}");
}
});
} }
@ -217,15 +111,26 @@ namespace RageCoop.Server
public void Start() public void Start()
{ {
Logger?.Info("================"); 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($"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("================");
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) if (Settings.UseZeroTier)
{ {
Logger?.Info($"Joining ZeroTier network: "+Settings.ZeroTierNetworkID); Logger?.Info($"Joining ZeroTier network: " + Settings.ZeroTierNetworkID);
if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID)==null) if (ZeroTierHelper.Join(Settings.ZeroTierNetworkID) == null)
{ {
throw new Exception("Failed to obtain ZeroTier network IP"); throw new Exception("Failed to obtain ZeroTier network IP");
} }
@ -242,7 +147,7 @@ namespace RageCoop.Server
MaximumConnections = Settings.MaxPlayers, MaximumConnections = Settings.MaxPlayers,
EnableUPnP = false, EnableUPnP = false,
AutoFlushSendQueue = true, AutoFlushSendQueue = true,
PingInterval=5 PingInterval = 5
}; };
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval); config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
@ -250,289 +155,58 @@ namespace RageCoop.Server
MainNetServer = new NetServer(config); MainNetServer = new NetServer(config);
MainNetServer.Start(); MainNetServer.Start();
Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port)); BaseScript.API = API;
BaseScript.API=API;
BaseScript.OnStart(); BaseScript.OnStart();
Resources.LoadAll(); Resources.LoadAll();
_listenerThread.Start(); _listenerThread.Start();
_latencyThread.Start(); Logger?.Info("Listening for clients");
_playerUpdateTimer.Enabled = true;
if (Settings.AnnounceSelf) if (Settings.AnnounceSelf)
{ {
_announceThread.Start(); _announceTimer.Enabled = true;
} }
if (Settings.AutoUpdate)
{
_updateTimer.Enabled = true;
}
_antiAssholesTimer.Enabled = true;
Logger?.Info("Listening for clients");
} }
/// <summary> /// <summary>
/// Terminate threads and stop the server /// Terminate threads and stop the server
/// </summary> /// </summary>
public void Stop() public void Stop()
{ {
_stopping = true;
Logger?.Flush(); Logger?.Flush();
Logger?.Dispose();
_stopping = true;
_listenerThread.Join(); _listenerThread.Join();
_latencyThread.Join(); _playerUpdateTimer.Enabled = false;
if (_announceThread.IsAlive) _announceTimer.Enabled = false;
{
_announceThread.Join();
}
_worker.Dispose(); _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) internal void QueueJob(Action job)
{ {
_worker.QueueJob(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 // 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('/')) if (message.StartsWith('/'))
{ {
string[] cmdArgs = message.Split(" "); string[] cmdArgs = message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1); string cmdName = cmdArgs[0].Remove(0, 1);
_worker.QueueJob(()=>API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender)); QueueJob(() => API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
return; return;
} }
message = message.Replace("~", ""); 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 msg = MainNetServer.CreateMessage();
var crypt = new Func<string, byte[]>((s) => var crypt = new Func<string, byte[]>((s) =>
@ -541,25 +215,23 @@ namespace RageCoop.Server
}); });
new Packets.ChatMessage(crypt) new Packets.ChatMessage(crypt)
{ {
Username=name, Username = name,
Message=message Message = message
}.Pack(msg); }.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) internal void SendChatMessage(string name, string message, Client target)
{ {
if(target == null) { return; } if (target == null) { return; }
var msg = MainNetServer.CreateMessage(); var msg = MainNetServer.CreateMessage();
new Packets.ChatMessage(new Func<string, byte[]>((s) => new Packets.ChatMessage(new Func<string, byte[]>((s) =>
{ {
return Security.Encrypt(s.GetBytes(), target.EndPoint); return Security.Encrypt(s.GetBytes(), target.EndPoint);
})) }))
{ {
Username= name, Username = name,
Message=message, Message = message,
}.Pack(msg); }.Pack(msg);
MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat); 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)); 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!"); 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(); var id = NewRequestID();
PendingResponses.Add(id, (type,p) => PendingResponses.Add(id, (type, m) =>
{ {
response=p; response.Deserialize(m);
received.Set(); received.Set();
}); });
var msg = MainNetServer.CreateMessage(); var msg = MainNetServer.CreateMessage();
msg.Write((byte)PacketType.Request); msg.Write((byte)PacketType.Request);
msg.Write(id); msg.Write(id);
request.Pack(msg); 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)) if (received.WaitOne(timeout))
{ {
var p = new T(); return response;
p.Deserialize(response);
return p;
} }
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); stream.Seek(0, SeekOrigin.Begin);
id = id == default ? NewFileID() : id;
var total = stream.Length; var total = stream.Length;
Logger?.Debug($"Requesting file transfer:{name}, {total}");
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest() if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{ {
FileLength= total, FileLength = total,
Name=name, Name = name,
ID=id, ID = id,
}, ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload) }, ConnectionChannel.File)?.Response != FileResponse.NeedToDownload)
{ {
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}"); Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
stream.Close();
stream.Dispose();
return; return;
} }
Logger?.Debug($"Initiating file transfer:{name}, {total}"); Logger?.Debug($"Initiating file transfer:{name}, {total}");
FileTransfer transfer = new() FileTransfer transfer = new()
{ {
ID=id, ID = id,
Name = name, Name = name,
}; };
InProgressFileTransfers.Add(id, transfer); InProgressFileTransfers.Add(id, transfer);
@ -663,40 +333,38 @@ namespace RageCoop.Server
{ {
// 4 KB chunk // 4 KB chunk
byte[] chunk = new byte[4096]; byte[] chunk = new byte[4096];
read += thisRead=stream.Read(chunk, 0, 4096); read += thisRead = stream.Read(chunk, 0, 4096);
if (thisRead!=chunk.Length) if (thisRead != chunk.Length)
{ {
if (thisRead==0) { break; } if (thisRead == 0) { break; }
Logger?.Trace($"Purging chunk:{thisRead}"); Logger?.Trace($"Purging chunk:{thisRead}");
Array.Resize(ref chunk, thisRead); Array.Resize(ref chunk, thisRead);
} }
Send( Send(
new Packets.FileTransferChunk() new Packets.FileTransferChunk()
{ {
ID=id, ID = id,
FileChunk=chunk, FileChunk = chunk,
}, },
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered); client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered);
transfer.Progress=read/stream.Length; transfer.Progress = read / stream.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress); } if (updateCallback != null) { updateCallback(transfer.Progress); }
} while (thisRead>0); } while (thisRead > 0);
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete() if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
{ {
ID= id, ID = id,
}, ConnectionChannel.File)?.Response!=FileResponse.Completed) }, 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}"); Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id); InProgressFileTransfers.Remove(id);
} }
internal int NewFileID() internal int NewFileID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0)
|| InProgressFileTransfers.ContainsKey(ID)) || InProgressFileTransfers.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -711,7 +379,7 @@ namespace RageCoop.Server
private int NewRequestID() private int NewRequestID()
{ {
int ID = 0; int ID = 0;
while ((ID==0) while ((ID == 0)
|| PendingResponses.ContainsKey(ID)) || PendingResponses.ContainsKey(ID))
{ {
byte[] rngBytes = new byte[4]; byte[] rngBytes = new byte[4];
@ -723,11 +391,11 @@ namespace RageCoop.Server
} }
return ID; 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(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
p.Pack(outgoingMessage); 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) 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