├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ ├── build-test.yaml
│ ├── docker-build.yaml
│ └── nightly-build.yaml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── RageCoop-V.sln
├── RageCoop.Client.Installer
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── RageCoop.Client.Installer.csproj
├── RageCoop.Client.Installer_ki4xybf5_wpftmp.csproj
├── Resource.Designer.cs
├── Resource.resx
├── Resources
│ └── LemonUI.SHVDN3.dll
└── bg.png
├── RageCoop.Client
├── Debug.cs
├── DevTools
│ └── DevTool.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Main.cs
├── Menus
│ ├── CoopMenu.cs
│ └── Sub
│ │ ├── DebugMenu.cs
│ │ ├── DevToolMenu.cs
│ │ ├── ServersMenu.cs
│ │ └── SettingsMenu.cs
├── Networking
│ ├── Chat.cs
│ ├── DownloadManager.cs
│ ├── HolePunch.cs
│ ├── Networking.cs
│ ├── Receive.cs
│ ├── Send.cs
│ └── Statistics.cs
├── PlayerList.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── AssemblyInfo.tt
├── RageCoop.Client.csproj
├── Scripting
│ ├── API.cs
│ ├── BaseScript.cs
│ ├── ClientScript.cs
│ └── Resources.cs
├── Security.cs
├── Settings.cs
├── Sync
│ ├── Entities
│ │ ├── Ped
│ │ │ ├── SyncedPed.Animations.cs
│ │ │ ├── SyncedPed.Members.cs
│ │ │ └── SyncedPed.cs
│ │ ├── SyncedEntity.cs
│ │ ├── SyncedProjectile.cs
│ │ ├── SyncedProp.cs
│ │ └── Vehicle
│ │ │ ├── SyncedVehicle.Members.cs
│ │ │ └── SyncedVehicle.cs
│ ├── EntityPool.cs
│ ├── SyncEvents.cs
│ └── Voice.cs
├── Util
│ ├── AddOnDataProvider.cs
│ ├── Memory.cs
│ ├── NativeCaller.cs
│ ├── PedConfigFlags.cs
│ ├── PedExtensions.cs
│ ├── TaskType.cs
│ ├── Util.cs
│ ├── VehicleExtensions.cs
│ └── WeaponUtil.cs
├── WorldThread.cs
└── packages.config
├── RageCoop.Core
├── BitReader.cs
├── CoreUtils.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Logger.cs
├── MathExtensions.cs
├── Networking
│ ├── CoopPeer.cs
│ ├── HttpHelper.cs
│ ├── PublicKey.cs
│ ├── ServerInfo.cs
│ └── ZeroTierHelper.cs
├── PacketExtensions.cs
├── Packets
│ ├── ChatMessage.cs
│ ├── CustomEvent.cs
│ ├── FilePackets.cs
│ ├── HolePunch.cs
│ ├── Misc.cs
│ ├── Packets.cs
│ ├── PedSync.cs
│ ├── PlayerPackets.cs
│ ├── ProjectileSync.cs
│ ├── SyncEvents
│ │ ├── BulletShot.cs
│ │ ├── NozzleTransform.cs
│ │ ├── OwnerChanged.cs
│ │ ├── PedKilled.cs
│ │ └── VehicleBulletShot.cs
│ ├── VehicleSync.cs
│ └── Voice.cs
├── RageCoop.Core.csproj
├── Scripting
│ ├── CustomEvents.cs
│ └── ResourceFile.cs
└── Worker.cs
├── RageCoop.Server
├── Client.cs
├── FileTransfer.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── HolePunch.cs
├── Networking
│ ├── Server.Background.cs
│ ├── Server.Connections.cs
│ ├── Server.EntitySync.cs
│ ├── Server.HolePunch.cs
│ ├── Server.Listener.cs
│ └── Server.cs
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── AssemblyInfo.tt
├── RageCoop.Server.csproj
├── RageCoop.Server.csproj.user
├── Scripting
│ ├── API.cs
│ ├── BaseScript.cs
│ ├── EventArgs
│ │ └── EventArgs.cs
│ ├── Resources.cs
│ ├── ServerEntities.cs
│ ├── ServerObject.cs
│ ├── ServerResource.cs
│ └── ServerScript.cs
├── Security.cs
├── Settings.cs
├── Util.cs
└── icon.ico
├── docker-compose.yml
├── images
├── icon.ico
└── icon.png
└── libs
├── ClearScript.Core.dll
├── ClearScript.V8.dll
├── ClearScriptV8.win-x64.dll
├── LemonUI.SHVDN3.dll
├── Lidgren.Network.XML
├── Lidgren.Network.deps.json
├── Lidgren.Network.dll
├── McMaster.NETCore.Plugins.dll
├── Newtonsoft.Json.dll
├── ScriptHookVDotNet.dll
└── ScriptHookVDotNet3.dll
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [RAGECOOP] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | #patreon: # Replace with a single Patreon username
5 | #open_collective: # Replace with a single Open Collective username
6 | #ko_fi: # Replace with a single Ko-fi username
7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | #liberapay: # Replace with a single Liberapay username
10 | #issuehunt: # Replace with a single IssueHunt username
11 | #otechie: # Replace with a single Otechie username
12 | #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: ['https://www.buymeacoffee.com/xEntenKoeniqx', 'https://patreon.com/Sardelka']
14 |
--------------------------------------------------------------------------------
/.github/workflows/build-test.yaml:
--------------------------------------------------------------------------------
1 | name: Build test
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*' # matches every branch that doesn't contain a '/'
7 | - '*/*' # matches every branch containing a single '/'
8 | - '**' # matches every branch
9 | - '!main' # excludes main
10 | - '!dev-nightly' # excludes nightly
11 |
12 | pull_request:
13 | branches: [ "main", "dev-nightly" ]
14 |
15 | jobs:
16 | build:
17 |
18 | runs-on: windows-latest
19 | strategy:
20 | matrix:
21 | dotnet-version: ['6.0.x']
22 |
23 | steps:
24 | - uses: actions/checkout@v3
25 | - name: Setup .NET
26 | uses: actions/setup-dotnet@v2
27 | with:
28 | dotnet-version: ${{ matrix.dotnet-version }}
29 | - name: Restore dependencies
30 | run: dotnet restore
31 | - name: Restore nuget packages
32 | run: nuget restore
33 | - name: Build client and installer
34 | run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
35 | - name: Build server win-x64
36 | run: dotnet build RageCoop.Server/RageCoop.Server.csproj -o bin/Release/Server
37 | - name: Upload server
38 | uses: actions/upload-artifact@v3
39 | with:
40 | name: RageCoop.Server
41 | path: bin/Release/Server
42 | - name: Upload Client
43 | uses: actions/upload-artifact@v3
44 | with:
45 | name: RageCoop.Client
46 | path: bin/Release/Client
47 | - uses: actions/checkout@v2
48 |
49 |
--------------------------------------------------------------------------------
/.github/workflows/docker-build.yaml:
--------------------------------------------------------------------------------
1 | name: Build and Push Docker Image to GHCR
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Set repo owner to lowercase
17 | run: echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
18 |
19 | - name: Login to GitHub Container Registry
20 | uses: docker/login-action@v1
21 | with:
22 | registry: ghcr.io
23 | username: ${{ github.repository_owner }}
24 | password: ${{ secrets.GITHUB_TOKEN }}
25 |
26 | - name: Build and push Docker image to GHCR
27 | uses: docker/build-push-action@v2
28 | with:
29 | context: .
30 | push: true
31 | tags: ghcr.io/${{ env.REPO_OWNER }}/ragecoop-v:latest
32 | dockerfile: Dockerfile
--------------------------------------------------------------------------------
/.github/workflows/nightly-build.yaml:
--------------------------------------------------------------------------------
1 | name: Nightly-build
2 |
3 | on:
4 | push:
5 | branches: [ "dev-nightly" ]
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: windows-latest
11 | strategy:
12 | matrix:
13 | dotnet-version: ['6.0.x']
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Setup .NET
18 | uses: actions/setup-dotnet@v2
19 | with:
20 | dotnet-version: ${{ matrix.dotnet-version }}
21 | - name: Restore dependencies
22 | run: dotnet restore
23 | - name: Restore nuget packages
24 | run: nuget restore
25 | - name: Build client and installer
26 | run: dotnet build RageCoop.Client.Installer/RageCoop.Client.Installer.csproj --configuration Release -o bin/Release/Client/RageCoop
27 | - name: Build server win-x64
28 | 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
29 | - name: Build server linux-x64
30 | 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
31 | - uses: vimtor/action-zip@v1
32 | with:
33 | files: bin/Release/Client
34 | dest: RageCoop.Client.zip
35 |
36 | - uses: vimtor/action-zip@v1
37 | with:
38 | files: bin/Release/Server/win-x64
39 | dest: RageCoop.Server-win-x64.zip
40 |
41 | - uses: vimtor/action-zip@v1
42 | with:
43 | files: bin/Release/Server/linux-x64
44 | dest: RageCoop.Server-linux-x64.zip
45 |
46 | - uses: WebFreak001/deploy-nightly@v1.1.0
47 | env:
48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
49 | with:
50 | upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
51 | release_id: 70603992
52 | asset_path: RageCoop.Client.zip
53 | asset_name: RageCoop.Client.zip
54 | asset_content_type: application/zip
55 | max_releases: 7
56 |
57 | - uses: WebFreak001/deploy-nightly@v1.1.0
58 | env:
59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
60 | with:
61 | upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
62 | release_id: 70603992
63 | asset_path: RageCoop.Server-win-x64.zip
64 | asset_name: RageCoop.Server-win-x64.zip
65 | asset_content_type: application/zip
66 | max_releases: 7
67 |
68 | - uses: WebFreak001/deploy-nightly@v1.1.0
69 | env:
70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided by github actions
71 | with:
72 | upload_url: https://uploads.github.com/repos/RAGECOOP/RAGECOOP-V/releases/70603992/assets{?name,label}
73 | release_id: 70603992
74 | asset_path: RageCoop.Server-linux-x64.zip
75 | asset_name: RageCoop.Server-linux-x64.zip
76 | asset_content_type: application/zip
77 | max_releases: 7
78 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/bin
2 | **/obj
3 | **/packages
4 | **/.vs
5 | **/.vscode
6 | **/.idea
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official image as a parent image
2 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
3 | WORKDIR /app
4 | EXPOSE 80
5 |
6 | # Use the SDK image to build the app
7 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
8 | WORKDIR /src
9 |
10 | # Copy csproj and restore as distinct layers
11 | COPY RageCoop.Server/*.csproj ./RageCoop.Server/
12 | COPY libs/*.dll ./libs/
13 |
14 | # Assuming RageCoop.Core is a dependency, if not, you can comment out the next line
15 | COPY RageCoop.Core/*.csproj ./RageCoop.Core/
16 |
17 | RUN dotnet restore RageCoop.Server/RageCoop.Server.csproj
18 |
19 | # Copy everything else and build
20 | COPY . .
21 | WORKDIR /src/RageCoop.Server
22 | RUN dotnet publish -c Release -o /app
23 |
24 | # Build runtime image
25 | FROM base AS final
26 | WORKDIR /app
27 | COPY --from=build-env /app .
28 | ENTRYPOINT ["dotnet", "RageCoop.Server.dll"]
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 github.com/RAGECOOP
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 🌐 RAGECOOP
4 |
5 | [![Downloads][downloads-shield]][downloads-url]
6 | [![Contributors][contributors-shield]][contributors-url]
7 | [![Forks][forks-shield]][forks-url]
8 | [![Stargazers][stars-shield]][stars-url]
9 | [![Issues][issues-shield]][issues-url]
10 |
11 |
12 | # 🧠 That's it
13 |
14 | 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!
15 |
16 | # 👁 Requirements
17 | - ScriptHookV
18 | - ScriptHookVDotNet 3.6.0 or later
19 | - .NET Framework 4.8 Runtime or SDK
20 |
21 | # 📋 Building the project
22 |
23 | You'll need:
24 | - .NET 6.0 SDK
25 | - .NET Framework 4.8 SDK
26 |
27 | Recommended IDE:
28 | - Visual Studio Code
29 | - Visul Studio 2022
30 |
31 | Then run `dotnet build` in the solution directory, built binaries are in the `bin` folder
32 |
33 | # 📚 Third-party libraries
34 | - [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet)
35 | - [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI)
36 | - Lidgren Network Custom
37 | - - No new features (only improvements)
38 | - [Newtonsoft.Json](https://github.com/JamesNK/Newtonsoft.Json)
39 | - [ClearScript](https://github.com/microsoft/ClearScript)
40 | - [SharpZipLib](https://github.com/icsharpcode/SharpZipLib)
41 | - [DotNetCorePlugins](https://github.com/natemcmaster/DotNetCorePlugins)
42 |
43 | # 👋 Features
44 |
45 | 1. Synchronized bullets
46 | 2. Synchronized vehicle/player/NPC
47 | 3. Synchronized projectiles
48 | 4. Simple ragdoll sync
49 | 5. Decent compatibility with other mods, set up a private modded server to have some fun!
50 | 6. Weaponized vehicle sync(WIP).
51 | 7. Optimization for high-Ping condition, play with friends around the world!
52 | 8. Powerful scripting API and resource system, easily [add multiplayer functionality to your mod](HTTPS://docs.ragecoop.com).
53 |
54 | # ⚠ Known issues
55 |
56 | See [Bugs](https://github.com/RAGECOOP/RAGECOOP-V/issues/33)
57 |
58 |
59 | # 🔫 Installation
60 | Refer to the [wiki](https://github.com/RAGECOOP/RAGECOOP-V/wiki)
61 |
62 | # 🧨 Downloads
63 |
64 | Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
65 |
66 | You can also download nightly builds [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/nightly), which includes latest features and bug-fixes, but has not been thoroughly tested.
67 |
68 | Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
69 |
70 |
71 |
72 | # 🦆 Special thanks to
73 |
74 | - [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
75 | - - For testing, ideas, contributions and the first modification with the API
76 | - [crosire](https://github.com/crosire)
77 | - - For the extensive work in ScriptHookVDotNet
78 | - [justalemon](https://github.com/justalemon)
79 | - - For the extensive work in LemonUI
80 |
81 | # 📝 License
82 |
83 | This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
84 |
85 | [downloads-shield]: https://img.shields.io/github/downloads/RAGECOOP/RAGECOOP-V/total?style=for-the-badge
86 | [downloads-url]: https://github.com/RAGECOOP/RAGECOOP-V/releases
87 | [contributors-shield]: https://img.shields.io/github/contributors/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
88 | [contributors-url]: https://github.com/RAGECOOP/RAGECOOP-V/graphs/contributors
89 | [forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
90 | [forks-url]: https://github.com/RAGECOOP/RAGECOOP-V/network/members
91 | [stars-shield]: https://img.shields.io/github/stars/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
92 | [stars-url]: https://github.com/RAGECOOP/RAGECOOP-V/stargazers
93 | [issues-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
94 | [issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues
95 |
96 |
97 |
--------------------------------------------------------------------------------
/RageCoop-V.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31919.166
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Server", "RageCoop.Server\RageCoop.Server.csproj", "{84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "RageCoop.Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "RageCoop.Client\RageCoop.Client.csproj", "{EF56D109-1F22-43E0-9DFF-CFCFB94E0681}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Client.Installer", "RageCoop.Client.Installer\RageCoop.Client.Installer.csproj", "{576D8610-0C28-4B60-BE2B-8657EA7CEE1B}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Debug|x64 = Debug|x64
18 | Release|Any CPU = Release|Any CPU
19 | Release|x64 = Release|x64
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|x64.ActiveCfg = Debug|Any CPU
25 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Debug|x64.Build.0 = Debug|Any CPU
26 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|x64.ActiveCfg = Release|Any CPU
29 | {84AB99D9-5E00-4CA2-B1DD-56B8419AAD24}.Release|x64.Build.0 = Release|Any CPU
30 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|x64.ActiveCfg = Debug|Any CPU
33 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Debug|x64.Build.0 = Debug|Any CPU
34 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.ActiveCfg = Release|Any CPU
37 | {CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.Build.0 = Release|Any CPU
38 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|x64.ActiveCfg = Debug|Any CPU
41 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Debug|x64.Build.0 = Debug|Any CPU
42 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.ActiveCfg = Release|Any CPU
45 | {EF56D109-1F22-43E0-9DFF-CFCFB94E0681}.Release|x64.Build.0 = Release|Any CPU
46 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
48 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.ActiveCfg = Debug|Any CPU
49 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Debug|x64.Build.0 = Debug|Any CPU
50 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.ActiveCfg = Release|Any CPU
53 | {576D8610-0C28-4B60-BE2B-8657EA7CEE1B}.Release|x64.Build.0 = Release|Any CPU
54 | EndGlobalSection
55 | GlobalSection(SolutionProperties) = preSolution
56 | HideSolutionNode = FALSE
57 | EndGlobalSection
58 | GlobalSection(ExtensibilityGlobals) = postSolution
59 | SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
60 | EndGlobalSection
61 | EndGlobal
62 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace RageCoop.Client.Installer
4 | {
5 | ///
6 | /// Interaction logic for App.xaml
7 | ///
8 | public partial class App : Application
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/RageCoop.Client.Installer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net48
6 | true
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | True
24 | True
25 | Resource.resx
26 |
27 |
28 |
29 |
30 |
31 | ResXFileCodeGenerator
32 | Resource.Designer.cs
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/RageCoop.Client.Installer_ki4xybf5_wpftmp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | RageCoop.Client.Installer
4 | obj\Debug\
5 | obj\
6 | M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Client.Installer\obj\
7 | <_TargetAssemblyProjectName>RageCoop.Client.Installer
8 |
9 |
10 |
11 | WinExe
12 | net48
13 | true
14 | true
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | True
28 | True
29 | Resource.resx
30 |
31 |
32 |
33 |
34 | ResXFileCodeGenerator
35 | Resource.Designer.cs
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/Resource.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace RageCoop.Client.Installer {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resource {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resource() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RageCoop.Client.Installer.Resource", typeof(Resource).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Byte[].
65 | ///
66 | internal static byte[] LemonUI_SHVDN3 {
67 | get {
68 | object obj = ResourceManager.GetObject("LemonUI_SHVDN3", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/Resource.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | Resources\LemonUI.SHVDN3.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/Resources/LemonUI.SHVDN3.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/RageCoop.Client.Installer/Resources/LemonUI.SHVDN3.dll
--------------------------------------------------------------------------------
/RageCoop.Client.Installer/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/RageCoop.Client.Installer/bg.png
--------------------------------------------------------------------------------
/RageCoop.Client/Debug.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace RageCoop.Client
5 | {
6 | internal enum TimeStamp
7 | {
8 | AddPeds,
9 | PedTotal,
10 | AddVehicles,
11 | VehicleTotal,
12 | SendPed,
13 | SendPedState,
14 | SendVehicle,
15 | SendVehicleState,
16 | UpdatePed,
17 | UpdateVehicle,
18 | CheckProjectiles,
19 | GetAllEntities,
20 | Receive,
21 | ProjectilesTotal,
22 | }
23 | internal static class Debug
24 | {
25 | public static Dictionary TimeStamps = new Dictionary();
26 | private static int _lastNfHandle;
27 | static Debug()
28 | {
29 | foreach (TimeStamp t in Enum.GetValues(typeof(TimeStamp)))
30 | {
31 | TimeStamps.Add(t, 0);
32 | }
33 | }
34 | public static string Dump(this Dictionary d)
35 | {
36 | string s = "";
37 | foreach (KeyValuePair kvp in d)
38 | {
39 | s += kvp.Key + ":" + kvp.Value + "\n";
40 | }
41 | return s;
42 | }
43 | public static void ShowTimeStamps()
44 | {
45 | GTA.UI.Notification.Hide(_lastNfHandle);
46 | _lastNfHandle = GTA.UI.Notification.Show(Debug.TimeStamps.Dump());
47 |
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RageCoop.Client/DevTools/DevTool.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using System;
4 | using System.Drawing;
5 | using System.Threading;
6 | using System.Windows.Forms;
7 |
8 | namespace RageCoop.Client
9 | {
10 | internal class DevTool : Script
11 | {
12 | public static Vehicle ToMark;
13 | public static bool UseSecondary = false;
14 | public static int Current = 0;
15 | public static int Secondary = 0;
16 | public static MuzzleDir Direction = MuzzleDir.Forward;
17 | public DevTool()
18 | {
19 | Tick += OnTick;
20 | KeyDown += OnKeyDown;
21 | }
22 |
23 | private void OnKeyDown(object sender, KeyEventArgs e)
24 | {
25 | if (ToMark == null || (!ToMark.Exists())) { return; }
26 | if (DevToolMenu.Menu.SelectedItem == DevToolMenu.boneIndexItem)
27 | {
28 |
29 | switch (e.KeyCode)
30 | {
31 | case Keys.Right:
32 | Current++;
33 | break;
34 | case Keys.Left:
35 | Current--;
36 | break;
37 | }
38 | }
39 | else if (DevToolMenu.Menu.SelectedItem == DevToolMenu.secondaryBoneIndexItem)
40 | {
41 |
42 | switch (e.KeyCode)
43 | {
44 | case Keys.Right:
45 | Secondary++;
46 | break;
47 | case Keys.Left:
48 | Secondary--;
49 | break;
50 | }
51 | }
52 | Update();
53 | }
54 | private static void Update()
55 | {
56 |
57 | if (Current > ToMark.Bones.Count - 1)
58 | {
59 | Current = 0;
60 | }
61 | else if (Current < 0)
62 | {
63 | Current = ToMark.Bones.Count - 1;
64 | }
65 | DevToolMenu.boneIndexItem.AltTitle = Current.ToString();
66 | if (Secondary > ToMark.Bones.Count - 1)
67 | {
68 | Secondary = 0;
69 | }
70 | else if (Secondary < 0)
71 | {
72 | Secondary = ToMark.Bones.Count - 1;
73 | }
74 | DevToolMenu.secondaryBoneIndexItem.AltTitle = Secondary.ToString();
75 | }
76 | private static void OnTick(object sender, EventArgs e)
77 | {
78 | if (ToMark == null || !ToMark.Exists()) { return; }
79 | Update();
80 | Draw(Current);
81 | if (UseSecondary)
82 | {
83 | Draw(Secondary);
84 | }
85 |
86 | }
87 | private static void Draw(int boneindex)
88 | {
89 | var bone = ToMark.Bones[boneindex];
90 | World.DrawLine(bone.Position, bone.Position + 2 * bone.ForwardVector, Color.Blue);
91 | World.DrawLine(bone.Position, bone.Position + 2 * bone.UpVector, Color.Green);
92 | World.DrawLine(bone.Position, bone.Position + 2 * bone.RightVector, Color.Yellow);
93 | Vector3 todraw = bone.ForwardVector;
94 | switch ((byte)Direction)
95 | {
96 | case 0:
97 | todraw = bone.ForwardVector;
98 | break;
99 | case 1:
100 | todraw = bone.RightVector;
101 | break;
102 | case 2:
103 | todraw = bone.UpVector;
104 | break;
105 | case 3:
106 | todraw = bone.ForwardVector * -1;
107 | break;
108 | case 4:
109 | todraw = bone.RightVector * -1;
110 | break;
111 | case 5:
112 | todraw = bone.UpVector * -1;
113 | break;
114 | }
115 | World.DrawLine(bone.Position, bone.Position + 10 * todraw, Color.Red);
116 | }
117 | public static void CopyToClipboard(MuzzleDir dir)
118 | {
119 |
120 | if (ToMark != null)
121 | {
122 | string s;
123 | if (UseSecondary)
124 | {
125 | if ((byte)dir < 3)
126 | {
127 | s = $@"
128 | // {ToMark.DisplayName}
129 | case {ToMark.Model.Hash}:
130 | return BulletsShot%2==0 ? {Current} : {Secondary};
131 | ";
132 | }
133 | else
134 | {
135 | s = $@"
136 | // {ToMark.DisplayName}
137 | case {ToMark.Model.Hash}:
138 | return BulletsShot%2==0 ? {Current} : {Secondary};
139 | ";
140 | }
141 | }
142 | else
143 | {
144 | if ((byte)dir < 3)
145 | {
146 | s = $@"
147 | // {ToMark.DisplayName}
148 | case {ToMark.Model.Hash}:
149 | return {Current};
150 | ";
151 | }
152 | else
153 | {
154 | s = $@"
155 | // {ToMark.DisplayName}
156 | case {ToMark.Model.Hash}:
157 | return {Current};
158 | ";
159 | }
160 | }
161 | Thread thread = new Thread(() => Clipboard.SetText(s));
162 | thread.SetApartmentState(ApartmentState.STA);
163 | thread.Start();
164 | thread.Join();
165 | GTA.UI.Notification.Show("Copied to clipboard, please paste it on the GitHub issue page!");
166 | }
167 | }
168 |
169 | }
170 | internal enum MuzzleDir : byte
171 | {
172 | Forward = 0,
173 | Right = 1,
174 | Up = 2,
175 | Backward = 3,
176 | Left = 4,
177 | Down = 5,
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/RageCoop.Client/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/RageCoop.Client/Menus/Sub/DebugMenu.cs:
--------------------------------------------------------------------------------
1 | #if DEBUG
2 | using GTA;
3 | using LemonUI.Menus;
4 | using System;
5 | using System.Drawing;
6 |
7 | namespace RageCoop.Client
8 | {
9 | internal static class DebugMenu
10 | {
11 | public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Debug", "Debug settings")
12 | {
13 | UseMouse = false,
14 | Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
15 | };
16 | public static NativeMenu DiagnosticMenu = new NativeMenu("RAGECOOP", "Diagnostic", "Performence and Diagnostic")
17 | {
18 | UseMouse = false,
19 | Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
20 | };
21 | public static NativeItem SimulatedLatencyItem = new NativeItem("Simulated network latency", "Simulated network latency in ms (one way)", "0");
22 | public static NativeCheckboxItem ShowOwnerItem = new NativeCheckboxItem("Show entity owner", "Show the owner name of the entity you're aiming at", false);
23 | private static readonly NativeCheckboxItem ShowNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
24 |
25 | static DebugMenu()
26 | {
27 | Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
28 | Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
29 |
30 |
31 | DiagnosticMenu.Opening += (sender, e) =>
32 | {
33 | DiagnosticMenu.Clear();
34 | DiagnosticMenu.Add(new NativeItem("EntityPool", EntityPool.DumpDebug()));
35 | foreach (var pair in Debug.TimeStamps)
36 | {
37 | DiagnosticMenu.Add(new NativeItem(pair.Key.ToString(), pair.Value.ToString(), pair.Value.ToString()));
38 | }
39 | };
40 | SimulatedLatencyItem.Activated += (s, e) =>
41 | {
42 | try
43 | {
44 | SimulatedLatencyItem.AltTitle = ((Networking.SimulatedLatency = int.Parse(Game.GetUserInput(SimulatedLatencyItem.AltTitle)) * 0.002f) * 500).ToString();
45 | }
46 | catch (Exception ex) { Main.Logger.Error(ex); }
47 | };
48 | ShowNetworkInfoItem.CheckboxChanged += (s, e) => { Networking.ShowNetworkInfo = ShowNetworkInfoItem.Checked; };
49 | ShowOwnerItem.CheckboxChanged += (s, e) => { Main.Settings.ShowEntityOwnerName = ShowOwnerItem.Checked; Util.SaveSettings(); };
50 | Menu.Add(SimulatedLatencyItem);
51 | Menu.Add(ShowNetworkInfoItem);
52 | Menu.Add(ShowOwnerItem);
53 | Menu.AddSubMenu(DiagnosticMenu);
54 |
55 | }
56 |
57 |
58 | }
59 | }
60 | #endif
61 |
--------------------------------------------------------------------------------
/RageCoop.Client/Menus/Sub/DevToolMenu.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using LemonUI.Menus;
3 | using System;
4 | using System.Drawing;
5 |
6 | namespace RageCoop.Client
7 | {
8 | internal static class DevToolMenu
9 | {
10 | public static NativeMenu Menu = new NativeMenu("RAGECOOP", "DevTool", "Help with the development")
11 | {
12 | UseMouse = false,
13 | Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
14 | };
15 | private static readonly NativeCheckboxItem enableItem = new NativeCheckboxItem("Enable");
16 |
17 | private static readonly NativeCheckboxItem enableSecondaryItem = new NativeCheckboxItem("Secondary", "Enable if this vehicle have two muzzles");
18 | public static NativeItem boneIndexItem = new NativeItem("Current bone index");
19 | public static NativeItem secondaryBoneIndexItem = new NativeItem("Secondary bone index");
20 | public static NativeItem clipboardItem = new NativeItem("Copy to clipboard");
21 | public static NativeListItem dirItem = new NativeListItem("Direction");
22 | static DevToolMenu()
23 | {
24 | Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
25 | Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
26 |
27 | enableItem.Activated += enableItem_Activated;
28 | enableItem.Checked = false;
29 | enableSecondaryItem.CheckboxChanged += EnableSecondaryItem_Changed;
30 |
31 | secondaryBoneIndexItem.Enabled = false;
32 | clipboardItem.Activated += ClipboardItem_Activated;
33 | dirItem.ItemChanged += DirItem_ItemChanged;
34 | foreach (var d in Enum.GetValues(typeof(MuzzleDir)))
35 | {
36 | dirItem.Items.Add((MuzzleDir)d);
37 | }
38 | dirItem.SelectedIndex = 0;
39 |
40 | Menu.Add(enableItem);
41 | Menu.Add(boneIndexItem);
42 | Menu.Add(enableSecondaryItem);
43 | Menu.Add(secondaryBoneIndexItem);
44 | Menu.Add(dirItem);
45 | Menu.Add(clipboardItem);
46 | }
47 |
48 | private static void EnableSecondaryItem_Changed(object sender, EventArgs e)
49 | {
50 | if (enableSecondaryItem.Checked)
51 | {
52 | DevTool.UseSecondary = true;
53 | secondaryBoneIndexItem.Enabled = true;
54 | }
55 | else
56 | {
57 | DevTool.UseSecondary = false;
58 | secondaryBoneIndexItem.Enabled = false;
59 | }
60 | }
61 |
62 | private static void DirItem_ItemChanged(object sender, ItemChangedEventArgs e)
63 | {
64 | DevTool.Direction = dirItem.SelectedItem;
65 | }
66 |
67 | private static void ClipboardItem_Activated(object sender, EventArgs e)
68 | {
69 | DevTool.CopyToClipboard(dirItem.SelectedItem);
70 | }
71 |
72 | private static void enableItem_Activated(object sender, EventArgs e)
73 | {
74 | if (enableItem.Checked)
75 | {
76 | DevTool.ToMark = Game.Player.Character.CurrentVehicle;
77 | }
78 | else
79 | {
80 | DevTool.ToMark = null;
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/RageCoop.Client/Menus/Sub/ServersMenu.cs:
--------------------------------------------------------------------------------
1 | using GTA.UI;
2 | using LemonUI.Menus;
3 | using Newtonsoft.Json;
4 | using RageCoop.Core;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Drawing;
8 | using System.Net;
9 | using System.Threading;
10 |
11 | namespace RageCoop.Client.Menus
12 | {
13 |
14 | ///
15 | /// Don't use it!
16 | ///
17 | internal static class ServersMenu
18 | {
19 | private static Thread GetServersThread;
20 | internal static NativeMenu Menu = new NativeMenu("RAGECOOP", "Servers", "Go to the server list")
21 | {
22 | UseMouse = false,
23 | Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
24 | };
25 | internal static NativeItem ResultItem = null;
26 |
27 | ///
28 | /// Don't use it!
29 | ///
30 | static ServersMenu()
31 | {
32 | Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
33 | Menu.BannerText.Color = Color.FromArgb(255, 165, 0);
34 |
35 | Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
36 | {
37 | CleanUpList();
38 | Menu.Add(ResultItem = new NativeItem("Loading..."));
39 |
40 | // Prevent freezing
41 | GetServersThread = new Thread(() => GetAllServers());
42 | GetServersThread.Start();
43 | };
44 | Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
45 | {
46 | CleanUpList();
47 | };
48 | }
49 |
50 | private static void CleanUpList()
51 | {
52 | Menu.Clear();
53 | ResultItem = null;
54 | }
55 |
56 | private static void GetAllServers()
57 | {
58 | List serverList = null;
59 | var realUrl = Main.Settings.MasterServer;
60 | serverList = JsonConvert.DeserializeObject>(DownloadString(realUrl));
61 |
62 | // Need to be processed in main thread
63 | Main.QueueAction(() =>
64 | {
65 | if (serverList == null)
66 | {
67 | ResultItem.Title = "Something went wrong!";
68 | return;
69 | }
70 | if (serverList.Count == 0)
71 | {
72 | ResultItem.Title = "No server was found!";
73 | return;
74 | }
75 | CleanUpList();
76 | foreach (ServerInfo server in serverList)
77 | {
78 | string address = $"{server.address}:{server.port}";
79 | NativeItem tmpItem = new NativeItem($"[{server.country}] {server.name}", $"~b~{address}~s~~n~~g~Version {server.version}~s~") { AltTitle = $"[{server.players}/{server.maxPlayers}]" };
80 | tmpItem.Activated += (object sender, EventArgs e) =>
81 | {
82 | try
83 | {
84 | Menu.Visible = false;
85 | if (server.useZT)
86 | {
87 | address = $"{server.ztAddress}:{server.port}";
88 | Notification.Show($"~y~Joining ZeroTier network... {server.ztID}");
89 | if (ZeroTierHelper.Join(server.ztID) == null)
90 | {
91 | throw new Exception("Failed to obtain ZeroTier network IP");
92 | }
93 | }
94 | Networking.ToggleConnection(address, null, null, PublicKey.FromServerInfo(server));
95 | #if !NON_INTERACTIVE
96 | CoopMenu.ServerIpItem.AltTitle = address;
97 |
98 | CoopMenu.Menu.Visible = true;
99 | #endif
100 | Main.Settings.LastServerAddress = address;
101 | Util.SaveSettings();
102 | }
103 | catch (Exception ex)
104 | {
105 | Notification.Show($"~r~{ex.Message}");
106 | if (server.useZT)
107 | {
108 | Notification.Show($"Make sure ZeroTier is correctly installed, download it from https://www.zerotier.com/");
109 | }
110 | }
111 | };
112 | Menu.Add(tmpItem);
113 | }
114 | });
115 | }
116 | private static string DownloadString(string url)
117 | {
118 | try
119 | {
120 | // TLS only
121 | ServicePointManager.Expect100Continue = true;
122 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
123 | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
124 |
125 | WebClient client = new WebClient();
126 | return client.DownloadString(url);
127 | }
128 | catch (Exception ex)
129 | {
130 | Main.QueueAction(() =>
131 | {
132 | ResultItem.Title = "Download failed!";
133 | ResultItem.Description = ex.Message;
134 | });
135 | return "";
136 | }
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/RageCoop.Client/Networking/Chat.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Native;
3 | using System;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 | using System.Windows.Forms;
7 |
8 | namespace RageCoop.Client
9 | {
10 | internal class Chat
11 | {
12 | private readonly Scaleform MainScaleForm;
13 |
14 | public string CurrentInput { get; set; }
15 |
16 | private bool CurrentFocused { get; set; }
17 | public bool Focused
18 | {
19 | get => CurrentFocused;
20 | set
21 | {
22 | if (value && Hidden)
23 | {
24 | Hidden = false;
25 | }
26 |
27 | MainScaleForm.CallFunction("SET_FOCUS", value ? 2 : 1, 2, "ALL");
28 |
29 | CurrentFocused = value;
30 | }
31 | }
32 |
33 | private ulong LastMessageTime { get; set; }
34 |
35 | private bool CurrentHidden { get; set; }
36 | private bool Hidden
37 | {
38 | get => CurrentHidden;
39 | set
40 | {
41 | if (value)
42 | {
43 | if (!CurrentHidden)
44 | {
45 | MainScaleForm.CallFunction("hide");
46 | }
47 | }
48 | else if (CurrentHidden)
49 | {
50 | MainScaleForm.CallFunction("showFeed");
51 | }
52 |
53 | CurrentHidden = value;
54 | }
55 | }
56 |
57 | public Chat()
58 | {
59 | MainScaleForm = new Scaleform("multiplayer_chat");
60 | }
61 |
62 | public void Init()
63 | {
64 | MainScaleForm.CallFunction("SET_FOCUS", 2, 2, "ALL");
65 | MainScaleForm.CallFunction("SET_FOCUS", 1, 2, "ALL");
66 | }
67 |
68 | public void Clear()
69 | {
70 | MainScaleForm.CallFunction("RESET");
71 | }
72 |
73 | public void Tick()
74 | {
75 | if ((Util.GetTickCount64() - LastMessageTime) > 15000 && !Focused && !Hidden)
76 | {
77 | Hidden = true;
78 | }
79 |
80 | if (!Hidden)
81 | {
82 | MainScaleForm.Render2D();
83 | }
84 |
85 | if (!CurrentFocused)
86 | {
87 | return;
88 | }
89 |
90 | Function.Call(Hash.DISABLE_ALL_CONTROL_ACTIONS, 0);
91 | }
92 |
93 | public void AddMessage(string sender, string msg)
94 | {
95 | MainScaleForm.CallFunction("ADD_MESSAGE", sender + ":", msg);
96 | LastMessageTime = Util.GetTickCount64();
97 | Hidden = false;
98 | }
99 |
100 | public void OnKeyDown(Keys key)
101 | {
102 | if (key == Keys.Escape)
103 | {
104 | Focused = false;
105 | CurrentInput = "";
106 | return;
107 | }
108 |
109 | if (key == Keys.PageUp)
110 | {
111 | MainScaleForm.CallFunction("PAGE_UP");
112 | }
113 | else if (key == Keys.PageDown)
114 | {
115 | MainScaleForm.CallFunction("PAGE_DOWN");
116 | }
117 |
118 | string keyChar = GetCharFromKey(key, Game.IsKeyPressed(Keys.ShiftKey), false);
119 |
120 | if (keyChar.Length == 0)
121 | {
122 | return;
123 | }
124 |
125 | switch (keyChar[0])
126 | {
127 | case (char)8:
128 | if (CurrentInput?.Length > 0)
129 | {
130 | CurrentInput = CurrentInput.Remove(CurrentInput.Length - 1);
131 | MainScaleForm.CallFunction("DELETE_TEXT");
132 | }
133 | return;
134 | case (char)13:
135 | MainScaleForm.CallFunction("ADD_TEXT", "ENTER");
136 |
137 | if (!string.IsNullOrWhiteSpace(CurrentInput))
138 | {
139 | Networking.SendChatMessage(CurrentInput);
140 | }
141 |
142 | Focused = false;
143 | CurrentInput = "";
144 | return;
145 | default:
146 | CurrentInput += keyChar;
147 | MainScaleForm.CallFunction("ADD_TEXT", keyChar);
148 | return;
149 | }
150 | }
151 |
152 | [DllImport("user32.dll")]
153 | public static extern int ToUnicodeEx(uint virtualKeyCode, uint scanCode, byte[] keyboardState,
154 | [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
155 | StringBuilder receivingBuffer,
156 | int bufferSize, uint flags, IntPtr kblayout);
157 |
158 | public static string GetCharFromKey(Keys key, bool shift, bool altGr)
159 | {
160 | StringBuilder buf = new StringBuilder(256);
161 | byte[] keyboardState = new byte[256];
162 |
163 | if (shift)
164 | {
165 | keyboardState[(int)Keys.ShiftKey] = 0xff;
166 | }
167 |
168 | if (altGr)
169 | {
170 | keyboardState[(int)Keys.ControlKey] = 0xff;
171 | keyboardState[(int)Keys.Menu] = 0xff;
172 | }
173 |
174 | ToUnicodeEx((uint)key, 0, keyboardState, buf, 256, 0, InputLanguage.CurrentInputLanguage.Handle);
175 | return buf.ToString();
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/RageCoop.Client/Networking/HolePunch.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using RageCoop.Core;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Timers;
8 |
9 | namespace RageCoop.Client
10 | {
11 | internal static partial class HolePunch
12 | {
13 | static HolePunch()
14 | {
15 | // Periodically send hole punch message as needed
16 | var timer = new Timer(1000);
17 | timer.Elapsed += DoPunch;
18 | timer.Enabled = true;
19 | }
20 |
21 | private static void DoPunch(object sender, ElapsedEventArgs e)
22 | {
23 | try
24 | {
25 | if (!Networking.IsOnServer) { return; }
26 | foreach (var p in PlayerList.Players.Values.ToArray())
27 | {
28 | if (p.InternalEndPoint != null && p.ExternalEndPoint != null && (p.Connection == null || p.Connection.Status == NetConnectionStatus.Disconnected))
29 | {
30 | Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.ID}");
31 | var msg = Networking.Peer.CreateMessage();
32 | new Packets.HolePunch
33 | {
34 | Puncher = Main.LocalPlayerID,
35 | Status = p.HolePunchStatus
36 | }.Pack(msg);
37 | Networking.Peer.SendUnconnectedMessage(msg, new List { p.InternalEndPoint, p.ExternalEndPoint });
38 | }
39 | }
40 | }
41 | catch (Exception ex)
42 | {
43 | Main.Logger.Error(ex);
44 | }
45 | }
46 |
47 | public static void Add(Packets.HolePunchInit p)
48 | {
49 | if (PlayerList.Players.TryGetValue(p.TargetID, out var player))
50 | {
51 | Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
52 | player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
53 | player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
54 | player.ConnectWhenPunched = p.Connect;
55 | }
56 | else
57 | {
58 | Main.Logger.Warning("No player with specified TargetID found for hole punching:" + p.TargetID);
59 | }
60 | }
61 | public static void Punched(Packets.HolePunch p, IPEndPoint from)
62 | {
63 | Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
64 | if (PlayerList.Players.TryGetValue(p.Puncher, out var puncher))
65 | {
66 | Main.Logger.Debug("Puncher identified as: " + puncher.Username);
67 | puncher.HolePunchStatus = (byte)(p.Status + 1);
68 | if (p.Status >= 3)
69 | {
70 | Main.Logger.Debug("HolePunch sucess: " + from + ", " + puncher.ID);
71 | if (puncher.ConnectWhenPunched && (puncher.Connection == null || puncher.Connection.Status == NetConnectionStatus.Disconnected))
72 | {
73 | Main.Logger.Debug("Connecting to peer: " + from);
74 | var msg = Networking.Peer.CreateMessage();
75 | new Packets.P2PConnect { ID = Main.LocalPlayerID }.Pack(msg);
76 | puncher.Connection = Networking.Peer.Connect(from, msg);
77 | Networking.Peer.FlushSendQueue();
78 | }
79 | }
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/RageCoop.Client/Networking/Statistics.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace RageCoop.Client
5 | {
6 | internal static class Statistics
7 | {
8 | public static int BytesDownPerSecond { get; private set; }
9 | public static int BytesUpPerSecond { get; private set; }
10 | static Statistics()
11 | {
12 | Task.Run(() =>
13 | {
14 | while (true)
15 | {
16 | var bu = Networking.Peer.Statistics.SentBytes;
17 | var bd = Networking.Peer.Statistics.ReceivedBytes;
18 | Thread.Sleep(1000);
19 | BytesUpPerSecond = Networking.Peer.Statistics.SentBytes - bu;
20 | BytesDownPerSecond = Networking.Peer.Statistics.ReceivedBytes - bd;
21 | }
22 | });
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/RageCoop.Client/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Resources;
6 |
7 | // General Information
8 | [assembly: AssemblyTitle("RageCoop.Client")]
9 | [assembly: AssemblyDescription("RageCoop.Client")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("RAGECOOP")]
12 | [assembly: AssemblyProduct("RageCoop.Client")]
13 | [assembly: AssemblyCopyright("Copyright © 2022")]
14 | [assembly: AssemblyTrademark("RAGECOOP")]
15 | [assembly: AssemblyCulture("")]
16 |
17 |
18 | // Version information
19 | [assembly: AssemblyVersion("1.5.4.7")]
20 | [assembly: AssemblyFileVersion("1.5.4.7")]
21 | [assembly: NeutralResourcesLanguageAttribute( "en-US" )]
22 |
23 |
--------------------------------------------------------------------------------
/RageCoop.Client/Properties/AssemblyInfo.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="true" hostspecific="true" language="C#" #>
2 | <#@ output extension=".cs" #>
3 | <#@ import namespace="System.IO" #>
4 | <#@ import namespace="System.Text.RegularExpressions" #>
5 | <#
6 | string output = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo.cs"));
7 | Regex pattern = new Regex("AssemblyVersion\\(\"(?\\d+)\\.(?\\d+)\\.(?\\d+)\\.(?\\d+)\"\\)");
8 | MatchCollection matches = pattern.Matches(output);
9 | if( matches.Count == 1 )
10 | {
11 | major = Convert.ToInt32(matches[0].Groups["major"].Value);
12 | minor = Convert.ToInt32(matches[0].Groups["minor"].Value);
13 | build = Convert.ToInt32(matches[0].Groups["build"].Value) + 1;
14 | revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
15 | if( this.Host.ResolveParameterValue("-","-","BuildConfiguration") == "Release" )
16 | revision++;
17 | }
18 | #>
19 |
20 | using System.Reflection;
21 | using System.Runtime.CompilerServices;
22 | using System.Runtime.InteropServices;
23 | using System.Resources;
24 |
25 | // General Information
26 | [assembly: AssemblyTitle("RageCoop.Client")]
27 | [assembly: AssemblyDescription("RageCoop.Client")]
28 | [assembly: AssemblyConfiguration("")]
29 | [assembly: AssemblyCompany("RAGECOOP")]
30 | [assembly: AssemblyProduct("RageCoop.Client")]
31 | [assembly: AssemblyCopyright("Copyright © 2022")]
32 | [assembly: AssemblyTrademark("RAGECOOP")]
33 | [assembly: AssemblyCulture("")]
34 |
35 |
36 | // Version informationr(
37 | [assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
38 | [assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
39 | [assembly: NeutralResourcesLanguageAttribute( "en-US" )]
40 |
41 | <#+
42 | int major = 1;
43 | int minor = 0;
44 | int revision = 0;
45 | int build = 0;
46 | #>
--------------------------------------------------------------------------------
/RageCoop.Client/Scripting/ClientScript.cs:
--------------------------------------------------------------------------------
1 | using RageCoop.Core.Scripting;
2 |
3 | namespace RageCoop.Client.Scripting
4 | {
5 | ///
6 | /// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded, you should use . to initiate your script.
7 | ///
8 | public abstract class ClientScript
9 | {
10 | ///
11 | /// This method would be called from background thread, call to dispatch it to main thread.
12 | ///
13 | public abstract void OnStart();
14 |
15 | ///
16 | /// This method would be called from background thread when the client disconnected from the server, you MUST terminate all background jobs/threads in this method.
17 | ///
18 | public abstract void OnStop();
19 |
20 | ///
21 | /// Get the instance where this script is loaded from.
22 | ///
23 | public ResourceFile CurrentFile { get; internal set; }
24 |
25 | ///
26 | /// Get the that this script belongs to.
27 | ///
28 | public ClientResource CurrentResource { get; internal set; }
29 |
30 | ///
31 | /// Eqivalent of in
32 | ///
33 | public Core.Logger Logger => CurrentResource.Logger;
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/RageCoop.Client/Security.cs:
--------------------------------------------------------------------------------
1 | using RageCoop.Core;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | namespace RageCoop.Client
5 | {
6 | internal class Security
7 | {
8 | public RSA ServerRSA { get; set; }
9 | public Aes ClientAes { get; set; } = Aes.Create();
10 | private readonly Logger Logger;
11 | public Security(Logger logger)
12 | {
13 | Logger = logger;
14 | ClientAes.GenerateKey();
15 | ClientAes.GenerateIV();
16 | }
17 | public void GetSymmetricKeysCrypted(out byte[] cryptedKey, out byte[] cryptedIV)
18 | {
19 | // Logger?.Debug($"Aes.Key:{ClientAes.Key.Dump()}, Aes.IV:{ClientAes.IV.Dump()}");
20 | cryptedKey = ServerRSA.Encrypt(ClientAes.Key, RSAEncryptionPadding.Pkcs1);
21 | cryptedIV = ServerRSA.Encrypt(ClientAes.IV, RSAEncryptionPadding.Pkcs1);
22 | }
23 | public byte[] Encrypt(byte[] data)
24 | {
25 | return new CryptoStream(new MemoryStream(data), ClientAes.CreateEncryptor(), CryptoStreamMode.Read).ReadToEnd();
26 | }
27 | public byte[] Decrypt(byte[] data)
28 | {
29 | return new CryptoStream(new MemoryStream(data), ClientAes.CreateDecryptor(), CryptoStreamMode.Read).ReadToEnd();
30 | }
31 | public void SetServerPublicKey(byte[] modulus, byte[] exponent)
32 | {
33 | var para = new RSAParameters();
34 | para.Modulus = modulus;
35 | para.Exponent = exponent;
36 | ServerRSA = RSA.Create(para);
37 | }
38 | public void Regen()
39 | {
40 | ClientAes = Aes.Create();
41 | ClientAes.GenerateKey();
42 | ClientAes.GenerateIV();
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/RageCoop.Client/Settings.cs:
--------------------------------------------------------------------------------
1 | #undef DEBUG
2 | using System.Windows.Forms;
3 | namespace RageCoop.Client
4 | {
5 | ///
6 | /// Don't use it!
7 | ///
8 | public class Settings
9 | {
10 | ///
11 | /// Don't use it!
12 | ///
13 | public string Username { get; set; } = "Player";
14 | ///
15 | /// The password used to authenticate when connecting to a server.
16 | ///
17 | public string Password { get; set; } = "";
18 | ///
19 | /// Don't use it!
20 | ///
21 | public string LastServerAddress { get; set; } = "127.0.0.1:4499";
22 | ///
23 | /// Don't use it!
24 | ///
25 | public string MasterServer { get; set; } = "https://masterserver.ragecoop.com/";
26 | ///
27 | /// Don't use it!
28 | ///
29 | public bool FlipMenu { get; set; } = false;
30 | ///
31 | /// Don't use it!
32 | ///
33 | public bool Voice { get; set; } = false;
34 |
35 | ///
36 | /// LogLevel for RageCoop.
37 | /// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
38 | ///
39 | public int LogLevel = 00;
40 |
41 | ///
42 | /// The key to open menu
43 | ///
44 | public Keys MenuKey { get; set; } = Keys.F9;
45 |
46 | ///
47 | /// The key to enter a vehicle as passenger.
48 | ///
49 | public Keys PassengerKey { get; set; } = Keys.G;
50 |
51 | ///
52 | /// Disable world NPC traffic, mission entities won't be affected
53 | ///
54 | public bool DisableTraffic { get; set; } = false;
55 |
56 | ///
57 | /// Bring up pause menu but don't freeze time when FrontEndPauseAlternate(Esc) is pressed.
58 | ///
59 | public bool DisableAlternatePause { get; set; } = true;
60 |
61 | ///
62 | /// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
63 | ///
64 | public int WorldVehicleSoftLimit { get; set; } = 20;
65 |
66 | ///
67 | /// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
68 | ///
69 | public int WorldPedSoftLimit { get; set; } = 30;
70 |
71 | ///
72 | /// The directory where log and resources downloaded from server will be placed.
73 | ///
74 | public string DataDirectory { get; set; } = "Scripts\\RageCoop\\Data";
75 |
76 | ///
77 | /// Show the owner name of the entity you're aiming at
78 | ///
79 | public bool ShowEntityOwnerName { get; set; } = false;
80 |
81 | ///
82 | /// Show other player's nametag on your screen
83 | ///
84 | public bool ShowPlayerNameTag { get; set; } = true;
85 |
86 | ///
87 | /// Show other player's blip on map
88 | ///
89 | public bool ShowPlayerBlip { get; set; } = true;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/Ped/SyncedPed.Animations.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Native;
3 |
4 | namespace RageCoop.Client
5 | {
6 | public partial class SyncedPed
7 | {
8 | private void DisplaySpeaking(bool speaking)
9 | {
10 | if (!MainPed.IsHuman)
11 | return;
12 |
13 | if (speaking)
14 | {
15 | Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mic_chatter", "mp_facial");
16 | return;
17 | }
18 |
19 | switch (MainPed.Gender)
20 | {
21 | case Gender.Male:
22 | Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@gen_male@variations@normal");
23 | break;
24 | case Gender.Female:
25 | Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@gen_female@variations@normal");
26 | break;
27 | default:
28 | Function.Call(Hash.PLAY_FACIAL_ANIM, MainPed.Handle, "mood_normal_1", "facials@mime@variations@normal");
29 | break;
30 | }
31 | }
32 |
33 | private void DisplayInCover()
34 | {
35 | var ourAnim = GetCoverAnim();
36 | var animDict = GetCoverIdleAnimDict();
37 |
38 | if (ourAnim != null && animDict != null)
39 | {
40 | var flag = AnimationFlags.Loop;
41 | if (!Function.Call(Hash.IS_ENTITY_PLAYING_ANIM, MainPed, animDict, ourAnim, 3))
42 | {
43 | MainPed.Task.ClearAll();
44 | Function.Call(Hash.TASK_PLAY_ANIM, MainPed, LoadAnim(animDict), ourAnim, 8f, 10f, -1, flag, -8f, 1, 1, 1);
45 | }
46 | }
47 | }
48 |
49 | internal string GetCoverAnim()
50 | {
51 | if (IsInCover)
52 | {
53 | if (IsBlindFiring)
54 | {
55 | if (IsInCover)
56 | return IsInCoverFacingLeft ? "blindfire_low_l_aim_med" : "blindfire_low_r_aim_med";
57 | return IsInCoverFacingLeft ? "blindfire_hi_l_aim_med" : "blindfire_hi_r_aim_med";
58 | }
59 |
60 | return IsInCoverFacingLeft ? "idle_l_corner" : "idle_r_corner";
61 | }
62 | return null;
63 | }
64 |
65 | internal string GetCoverIdleAnimDict()
66 | {
67 | if (!IsInCover) return "";
68 | var altitude = IsInLowCover ? "low" : "high";
69 |
70 | var hands = GetWeaponHandsHeld(CurrentWeaponHash);
71 | if (IsBlindFiring)
72 | {
73 | if (hands == 1) return "cover@weapon@1h";
74 | if (hands == 2 || hands == 5) return "cover@weapon@2h";
75 | }
76 |
77 | if (hands == 1) return "cover@idles@1h@" + altitude + "@_a";
78 | if (hands == 2 || hands == 5) return "cover@idles@2h@" + altitude + "@_a";
79 | if (hands == 3 || hands == 4 || hands == 0) return "cover@idles@unarmed@" + altitude + "@_a";
80 | return "";
81 | }
82 |
83 | internal int GetWeaponHandsHeld(uint weapon)
84 | {
85 | switch (weapon)
86 | {
87 | case unchecked((uint)WeaponHash.Unarmed):
88 | return 0;
89 |
90 | case unchecked((uint)WeaponHash.RPG):
91 | case unchecked((uint)WeaponHash.HomingLauncher):
92 | case unchecked((uint)WeaponHash.Firework):
93 | return 5;
94 |
95 | case unchecked((uint)WeaponHash.Minigun):
96 | return 5;
97 |
98 | case unchecked((uint)WeaponHash.GolfClub):
99 | case unchecked((uint)WeaponHash.PoolCue):
100 | case unchecked((uint)WeaponHash.Bat):
101 | return 4;
102 |
103 | case unchecked((uint)WeaponHash.Knife):
104 | case unchecked((uint)WeaponHash.Nightstick):
105 | case unchecked((uint)WeaponHash.Hammer):
106 | case unchecked((uint)WeaponHash.Crowbar):
107 | case unchecked((uint)WeaponHash.Wrench):
108 | case unchecked((uint)WeaponHash.BattleAxe):
109 | case unchecked((uint)WeaponHash.Dagger):
110 | case unchecked((uint)WeaponHash.Hatchet):
111 | case unchecked((uint)WeaponHash.KnuckleDuster):
112 | case unchecked((uint)-581044007):
113 | case unchecked((uint)-102323637):
114 | case unchecked((uint)-538741184):
115 | return 3;
116 |
117 | case unchecked((uint)-1357824103):
118 | case unchecked((uint)-1074790547):
119 | case unchecked(2132975508):
120 | case unchecked((uint)-2084633992):
121 | case unchecked((uint)-952879014):
122 | case unchecked(100416529):
123 | case unchecked((uint)WeaponHash.Gusenberg):
124 | case unchecked((uint)WeaponHash.MG):
125 | case unchecked((uint)WeaponHash.CombatMG):
126 | case unchecked((uint)WeaponHash.CombatPDW):
127 | case unchecked((uint)WeaponHash.AssaultSMG):
128 | case unchecked((uint)WeaponHash.SMG):
129 | case unchecked((uint)WeaponHash.HeavySniper):
130 | case unchecked((uint)WeaponHash.PumpShotgun):
131 | case unchecked((uint)WeaponHash.HeavyShotgun):
132 | case unchecked((uint)WeaponHash.Musket):
133 | case unchecked((uint)WeaponHash.AssaultShotgun):
134 | case unchecked((uint)WeaponHash.BullpupShotgun):
135 | case unchecked((uint)WeaponHash.SawnOffShotgun):
136 | case unchecked((uint)WeaponHash.SweeperShotgun):
137 | case unchecked((uint)WeaponHash.CompactRifle):
138 | return 2;
139 | }
140 |
141 | return 1;
142 | }
143 |
144 | private string LoadAnim(string anim)
145 | {
146 | ulong startTime = Util.GetTickCount64();
147 |
148 | while (!Function.Call(Hash.HAS_ANIM_DICT_LOADED, anim))
149 | {
150 | Script.Yield();
151 | Function.Call(Hash.REQUEST_ANIM_DICT, anim);
152 | if (Util.GetTickCount64() - startTime >= 1000)
153 | {
154 | break;
155 | }
156 | }
157 |
158 | return anim;
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/Ped/SyncedPed.Members.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using RageCoop.Core;
4 | using System.Collections.Generic;
5 |
6 | namespace RageCoop.Client
7 | {
8 | ///
9 | /// ?
10 | ///
11 | public partial class SyncedPed : SyncedEntity
12 | {
13 | internal Blip PedBlip = null;
14 | internal BlipColor BlipColor = (BlipColor)255;
15 | internal BlipSprite BlipSprite = 0;
16 | internal float BlipScale = 1;
17 | internal int VehicleID
18 | {
19 | get => CurrentVehicle?.ID ?? 0;
20 | set
21 | {
22 | if (CurrentVehicle == null || value != CurrentVehicle?.ID)
23 | {
24 | CurrentVehicle = EntityPool.GetVehicleByID(value);
25 | }
26 | }
27 | }
28 | internal SyncedVehicle CurrentVehicle { get; private set; }
29 | internal VehicleSeat Seat;
30 | public bool IsPlayer { get => OwnerID == ID && ID != 0; }
31 | public Ped MainPed { get; internal set; }
32 | internal int Health { get; set; }
33 |
34 | internal Vector3 HeadPosition { get; set; }
35 | internal Vector3 RightFootPosition { get; set; }
36 | internal Vector3 LeftFootPosition { get; set; }
37 |
38 | internal byte WeaponTint { get; set; }
39 | private bool _lastRagdoll = false;
40 | private ulong _lastRagdollTime = 0;
41 | private bool _lastInCover = false;
42 | private byte[] _lastClothes = null;
43 | internal byte[] Clothes { get; set; }
44 |
45 | internal float Heading { get; set; }
46 |
47 | internal ulong LastSpeakingTime { get; set; } = 0;
48 | internal bool IsSpeaking { get; set; } = false;
49 | public byte Speed { get; set; }
50 | private bool _lastIsJumping = false;
51 | internal PedDataFlags Flags;
52 |
53 | internal bool IsAiming => Flags.HasPedFlag(PedDataFlags.IsAiming);
54 | internal bool _lastDriveBy;
55 | internal bool IsReloading => Flags.HasPedFlag(PedDataFlags.IsReloading);
56 | internal bool IsJumping => Flags.HasPedFlag(PedDataFlags.IsJumping);
57 | internal bool IsRagdoll => Flags.HasPedFlag(PedDataFlags.IsRagdoll);
58 | internal bool IsOnFire => Flags.HasPedFlag(PedDataFlags.IsOnFire);
59 | internal bool IsInParachuteFreeFall => Flags.HasPedFlag(PedDataFlags.IsInParachuteFreeFall);
60 | internal bool IsParachuteOpen => Flags.HasPedFlag(PedDataFlags.IsParachuteOpen);
61 | internal bool IsOnLadder => Flags.HasPedFlag(PedDataFlags.IsOnLadder);
62 | internal bool IsVaulting => Flags.HasPedFlag(PedDataFlags.IsVaulting);
63 | internal bool IsInCover => Flags.HasPedFlag(PedDataFlags.IsInCover);
64 | internal bool IsInLowCover => Flags.HasPedFlag(PedDataFlags.IsInLowCover);
65 | internal bool IsInCoverFacingLeft => Flags.HasPedFlag(PedDataFlags.IsInCoverFacingLeft);
66 | internal bool IsBlindFiring => Flags.HasPedFlag(PedDataFlags.IsBlindFiring);
67 | internal bool IsInStealthMode => Flags.HasPedFlag(PedDataFlags.IsInStealthMode);
68 | internal Prop ParachuteProp { get; set; } = null;
69 | internal uint CurrentWeaponHash { get; set; }
70 | private Dictionary _lastWeaponComponents = null;
71 | internal Dictionary WeaponComponents { get; set; } = null;
72 | private Entity _weaponObj;
73 | internal Vector3 AimCoords { get; set; }
74 |
75 |
76 | private readonly string[] _currentAnimation = new string[2] { "", "" };
77 |
78 | private bool LastMoving;
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/SyncedEntity.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using System.Diagnostics;
4 |
5 | namespace RageCoop.Client
6 | {
7 | ///
8 | ///
9 | ///
10 | public abstract class SyncedEntity
11 | {
12 |
13 | ///
14 | /// Indicates whether the current player is responsible for syncing this entity.
15 | ///
16 | public bool IsLocal
17 | {
18 | get => OwnerID == Main.LocalPlayerID;
19 | }
20 |
21 | ///
22 | /// Network ID for this entity
23 | ///
24 | public int ID { get; internal set; }
25 |
26 |
27 | private int _ownerID;
28 | ///
29 | ///
30 | ///
31 | public int OwnerID
32 | {
33 | get => _ownerID;
34 | internal set
35 | {
36 | if (value == _ownerID && Owner != null) { return; }
37 | _ownerID = value;
38 | Owner = PlayerList.GetPlayer(value);
39 | if (this is SyncedPed && Owner != null)
40 | {
41 | Owner.Character = ((SyncedPed)this);
42 | }
43 | }
44 | }
45 |
46 | internal virtual Player Owner { get; private set; }
47 | ///
48 | ///
49 | ///
50 | public bool IsOutOfSync
51 | {
52 | get => Main.Ticked - LastSynced > 200 && ID != 0;
53 | }
54 | internal bool IsReady
55 | {
56 | get => LastSynced > 0 || LastFullSynced == 0;
57 | }
58 | internal bool IsInvincible { get; set; } = false;
59 | internal bool NeedUpdate
60 | {
61 | get => LastSynced >= LastUpdated;
62 | }
63 | #region LAST STATE
64 | ///
65 | /// Last time a new sync message arrived.
66 | ///
67 | public ulong LastSynced { get; set; } = 0;
68 | ///
69 | /// Last time a new sync message arrived.
70 | ///
71 | public ulong LastFullSynced { get; internal set; } = 0;
72 | ///
73 | /// Last time the local entity has been updated,
74 | ///
75 | public ulong LastUpdated { get; set; } = 0;
76 |
77 |
78 | internal Stopwatch LastSentStopWatch { get; set; } = Stopwatch.StartNew();
79 | #endregion
80 |
81 | public bool SendNextFrame { get; set; } = false;
82 | public bool SendFullNextFrame { get; set; } = false;
83 |
84 | ///
85 | ///
86 | ///
87 | protected internal bool _lastFrozen = false;
88 | internal Model Model { get; set; }
89 | internal Vector3 Position { get; set; }
90 | internal Vector3 Rotation { get; set; }
91 | internal Quaternion Quaternion { get; set; }
92 | internal Vector3 Velocity { get; set; }
93 | public Stopwatch LastSyncedStopWatch = new Stopwatch();
94 | internal abstract void Update();
95 | internal void PauseUpdate(ulong frames)
96 | {
97 | LastUpdated = Main.Ticked + frames;
98 | }
99 | protected Vector3 Predict(Vector3 input)
100 | {
101 | return (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Velocity + input;
102 | }
103 | private float _accumulatedOff = 0;
104 | protected bool IsOff(float thisOff, float tolerance = 3, float limit = 30)
105 | {
106 | _accumulatedOff += thisOff - tolerance;
107 | if (_accumulatedOff < 0) { _accumulatedOff = 0; }
108 | else if (_accumulatedOff >= limit)
109 | {
110 | _accumulatedOff = 0;
111 | return true;
112 | }
113 | return false;
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/SyncedProjectile.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using RageCoop.Core;
5 |
6 | namespace RageCoop.Client
7 | {
8 | internal class SyncedProjectile : SyncedEntity
9 | {
10 | public ProjectileDataFlags Flags { private get; set; } = ProjectileDataFlags.None;
11 |
12 | public readonly Vector3 Origin;
13 | private bool _firstSend = false;
14 |
15 |
16 | public bool IsValid { get; private set; } = true;
17 | public new bool IsLocal { get; private set; } = false;
18 | public Projectile MainProjectile { get; set; }
19 | public SyncedEntity Shooter { get; set; }
20 | public bool Exploded => Flags.HasProjDataFlag(ProjectileDataFlags.Exploded);
21 |
22 | internal override Player Owner => Shooter.Owner;
23 | ///
24 | /// Invalid property for projectile.
25 | ///
26 | private new int OwnerID { set { } }
27 | public WeaponHash WeaponHash { get; set; }
28 | private WeaponAsset Asset { get; set; }
29 | public void ExtractData(ref Packets.ProjectileSync p)
30 | {
31 | p.Position = MainProjectile.Position;
32 | p.Velocity = MainProjectile.Velocity;
33 | p.Rotation = MainProjectile.Rotation;
34 | p.ID = ID;
35 | p.ShooterID = Shooter.ID;
36 | p.WeaponHash = (uint)MainProjectile.WeaponHash;
37 | p.Flags = ProjectileDataFlags.None;
38 | if (MainProjectile.IsDead)
39 | {
40 | p.Flags |= ProjectileDataFlags.Exploded;
41 | }
42 | if (MainProjectile.AttachedEntity != null)
43 | {
44 | p.Flags |= ProjectileDataFlags.IsAttached;
45 | }
46 | if (Shooter is SyncedVehicle)
47 | {
48 | p.Flags |= ProjectileDataFlags.IsShotByVehicle;
49 | }
50 | if (_firstSend)
51 | {
52 | p.Flags |= ProjectileDataFlags.IsAttached;
53 | _firstSend = false;
54 | }
55 |
56 | }
57 | public SyncedProjectile(Projectile p)
58 | {
59 | var owner = p.OwnerEntity;
60 | if (owner == null) { IsValid = false; return; }
61 | ID = EntityPool.RequestNewID();
62 | MainProjectile = p;
63 | Origin = p.Position;
64 | if (EntityPool.PedsByHandle.TryGetValue(owner.Handle, out var shooter))
65 | {
66 | if (shooter.MainPed != null
67 | && (p.AttachedEntity == shooter.MainPed.Weapons.CurrentWeaponObject
68 | || p.AttachedEntity == shooter.MainPed))
69 | {
70 | // Reloading
71 | IsValid = false;
72 | return;
73 | }
74 | Shooter = shooter;
75 | IsLocal = shooter.IsLocal;
76 | }
77 | else if (EntityPool.VehiclesByHandle.TryGetValue(owner.Handle, out var shooterVeh))
78 | {
79 | Shooter = shooterVeh;
80 | IsLocal = shooterVeh.IsLocal;
81 | }
82 | else
83 | {
84 | IsValid = false;
85 | }
86 | }
87 | public SyncedProjectile(int id)
88 | {
89 | ID = id;
90 | IsLocal = false;
91 | }
92 | internal override void Update()
93 | {
94 |
95 | // Skip update if no new sync message has arrived.
96 | if (!NeedUpdate) { return; }
97 |
98 | if (MainProjectile == null || !MainProjectile.Exists())
99 | {
100 | CreateProjectile();
101 | return;
102 | }
103 | MainProjectile.Velocity = Velocity + 10 * (Predict(Position) - MainProjectile.Position);
104 | MainProjectile.Rotation = Rotation;
105 | LastUpdated = Main.Ticked;
106 | }
107 |
108 | private void CreateProjectile()
109 | {
110 | Asset = new WeaponAsset(WeaponHash);
111 | if (!Asset.IsLoaded) { Asset.Request(); return; }
112 | if (Shooter == null) { return; }
113 | Entity owner;
114 | owner = (Shooter as SyncedPed)?.MainPed ?? (Entity)(Shooter as SyncedVehicle)?.MainVehicle;
115 | Position = (Owner.PacketTravelTime + 0.001f * LastSyncedStopWatch.ElapsedMilliseconds) * Shooter.Velocity + Position;
116 | var end = Position + Velocity;
117 | 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);
118 | var ps = World.GetAllProjectiles();
119 | MainProjectile = ps[ps.Length - 1];
120 | MainProjectile.Position = Position;
121 | MainProjectile.Rotation = Rotation;
122 | MainProjectile.Velocity = Velocity;
123 | EntityPool.Add(this);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/SyncedProp.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 |
3 | namespace RageCoop.Client
4 | {
5 | ///
6 | /// Synchronized prop, mostly owned by server
7 | ///
8 | public class SyncedProp : SyncedEntity
9 | {
10 | internal SyncedProp(int id)
11 | {
12 | ID = id;
13 | }
14 | ///
15 | /// The real entity
16 | ///
17 | public Prop MainProp { get; set; }
18 | internal new int OwnerID
19 | {
20 | get
21 | {
22 | // alwayse owned by server
23 | return 0;
24 | }
25 | }
26 | internal override void Update()
27 | {
28 |
29 | if (!NeedUpdate) { return; }
30 | if (MainProp == null || !MainProp.Exists())
31 | {
32 | MainProp = World.CreateProp(Model, Position, Rotation, false, false);
33 | MainProp.IsInvincible = true;
34 | }
35 | MainProp.Position = Position;
36 | MainProp.Rotation = Rotation;
37 | MainProp.SetFrozen(true);
38 | LastUpdated = Main.Ticked;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Entities/Vehicle/SyncedVehicle.Members.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using RageCoop.Core;
4 | using System.Collections.Generic;
5 |
6 | namespace RageCoop.Client
7 | {
8 | public partial class SyncedVehicle
9 | {
10 | public Vehicle MainVehicle { get; internal set; }
11 |
12 |
13 | #region -- SYNC DATA --
14 | internal Vector3 RotationVelocity { get; set; }
15 | internal float SteeringAngle { get; set; }
16 | internal float ThrottlePower { get; set; }
17 | internal float BrakePower { get; set; }
18 | internal float DeluxoWingRatio { get; set; } = -1;
19 |
20 |
21 | internal byte LandingGear { get; set; }
22 | internal VehicleRoofState RoofState { get; set; }
23 | internal VehicleDamageModel DamageModel { get; set; }
24 | internal byte[] Colors { get; set; }
25 | internal Dictionary Mods { get; set; }
26 | internal float EngineHealth { get; set; }
27 | internal VehicleLockStatus LockStatus { get; set; }
28 | internal byte RadioStation = 255;
29 | internal string LicensePlate { get; set; }
30 | internal int Livery { get; set; } = -1;
31 | internal VehicleDataFlags Flags { get; set; }
32 |
33 | #endregion
34 |
35 | #region FLAGS
36 |
37 | internal bool EngineRunning { get => Flags.HasVehFlag(VehicleDataFlags.IsEngineRunning); }
38 | internal bool Transformed { get => Flags.HasVehFlag(VehicleDataFlags.IsTransformed); }
39 | internal bool HornActive { get => Flags.HasVehFlag(VehicleDataFlags.IsHornActive); }
40 | internal bool LightsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreLightsOn); }
41 | internal bool BrakeLightsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreBrakeLightsOn); }
42 | internal bool HighBeamsOn { get => Flags.HasVehFlag(VehicleDataFlags.AreHighBeamsOn); }
43 | internal bool SireneActive { get => Flags.HasVehFlag(VehicleDataFlags.IsSirenActive); }
44 | internal bool IsDead { get => Flags.HasVehFlag(VehicleDataFlags.IsDead); }
45 | internal bool IsDeluxoHovering { get => Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering); }
46 | #endregion
47 |
48 | #region FIXED-DATA
49 |
50 | internal bool IsFlipped
51 | {
52 | get => IsMotorcycle || ((Quaternion * Vector3.RelativeTop).Z - (Quaternion * Vector3.RelativeBottom).Z) < 0.5;
53 | }
54 | internal bool IsMotorcycle;
55 | internal bool IsAircraft;
56 | internal bool HasRocketBoost;
57 | internal bool HasParachute;
58 | internal bool HasRoof;
59 | internal bool IsSubmarineCar;
60 | internal bool IsDeluxo;
61 |
62 | #endregion
63 |
64 | #region PRIVATE
65 | private byte[] _lastVehicleColors = new byte[] { 0, 0 };
66 | private Dictionary _lastVehicleMods = new Dictionary();
67 | private bool _lastHornActive = false;
68 | private bool _lastTransformed = false;
69 | internal int _lastLivery = -1;
70 | private readonly List _predictedTrace = new List();
71 | private readonly List _orgTrace = new List();
72 | private Vector3 _predictedPosition;
73 | #endregion
74 |
75 | #region OUTGOING
76 | internal float LastNozzleAngle { get; set; }
77 |
78 | internal float LastEngineHealth { get; set; }
79 | internal Vector3 LastVelocity { get; set; }
80 | #endregion
81 | }
82 | }
--------------------------------------------------------------------------------
/RageCoop.Client/Sync/Voice.cs:
--------------------------------------------------------------------------------
1 | using NAudio.Wave;
2 | using System.Threading;
3 |
4 | namespace RageCoop.Client
5 | {
6 | internal static class Voice
7 | {
8 | private static WaveInEvent _waveIn;
9 | private static readonly BufferedWaveProvider _waveProvider = new BufferedWaveProvider(new WaveFormat(16000, 16, 1));
10 |
11 | private static Thread _thread;
12 |
13 | public static bool WasInitialized() => _thread != null;
14 | public static bool IsRecording() => _waveIn != null;
15 | public static void ClearAll()
16 | {
17 | _waveProvider.ClearBuffer();
18 |
19 | StopRecording();
20 |
21 | if (_thread != null && _thread.IsAlive)
22 | {
23 | _thread.Abort();
24 | _thread = null;
25 | }
26 | }
27 |
28 | public static void StopRecording()
29 | {
30 | if (!IsRecording())
31 | return;
32 |
33 | _waveIn.StopRecording();
34 | _waveIn.Dispose();
35 | _waveIn = null;
36 | }
37 |
38 | public static void Init()
39 | {
40 | if (WasInitialized())
41 | return;
42 |
43 | // I tried without thread but the game will lag without
44 | _thread = new Thread(new ThreadStart(() =>
45 | {
46 | while (true)
47 | {
48 | using (var wo = new WaveOutEvent())
49 | {
50 | wo.Init(_waveProvider);
51 | wo.Play();
52 |
53 | while (wo.PlaybackState == PlaybackState.Playing)
54 | {
55 | Thread.Sleep(100);
56 | }
57 | }
58 | }
59 | }));
60 | _thread.Start();
61 | }
62 |
63 | public static void StartRecording()
64 | {
65 | if (IsRecording())
66 | return;
67 |
68 | _waveIn = new WaveInEvent
69 | {
70 | DeviceNumber = 0,
71 | BufferMilliseconds = 20,
72 | NumberOfBuffers = 1,
73 | WaveFormat = _waveProvider.WaveFormat
74 | };
75 | _waveIn.DataAvailable += WaveInDataAvailable;
76 |
77 | _waveIn.StartRecording();
78 | }
79 |
80 | public static void AddVoiceData(byte[] buffer, int recorded)
81 | {
82 | _waveProvider.AddSamples(buffer, 0, recorded);
83 | }
84 |
85 | private static void WaveInDataAvailable(object sender, WaveInEventArgs e)
86 | {
87 | if (!IsRecording())
88 | return;
89 |
90 | Networking.SendVoiceMessage(e.Buffer, e.BytesRecorded);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/RageCoop.Client/Util/AddOnDataProvider.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace RageCoop.Client
9 | {
10 | ///
11 | /// Class providing support for addon mods
12 | ///
13 | internal class AddOnDataProvider
14 | {
15 | public static int GetMuzzleIndex(Model model)
16 | {
17 | switch (model.Hash)
18 | {
19 | // f14a2
20 | case -848721350:
21 | return 48;
22 |
23 | // f15e
24 | case 881261972:
25 | return 32;
26 |
27 | // f16c
28 | case -2051171080:
29 | return 25;
30 |
31 | // F22A
32 | case 2061630439:
33 | return 14;
34 |
35 | // f35c
36 | case -343547392:
37 | return 44;
38 |
39 | // mig29a
40 | case 513887552:
41 | return 18;
42 |
43 | // su30sm
44 | case -733985185:
45 | return 34;
46 |
47 | // su33
48 | case -722216722:
49 | return 34;
50 |
51 | // su35s
52 | case -268602544:
53 | return 28;
54 |
55 | // su57
56 | case 1490050781:
57 | return 21;
58 |
59 | default:
60 | return -1;
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/RageCoop.Client/Util/Memory.cs:
--------------------------------------------------------------------------------
1 |
2 | using GTA;
3 | using GTA.Math;
4 | using RageCoop.Core;
5 | using SHVDN;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Runtime.InteropServices;
9 |
10 | namespace RageCoop.Client
11 | {
12 | internal unsafe class MemPatch
13 | {
14 | private readonly byte[] _data;
15 | private readonly byte[] _orginal;
16 | private readonly IntPtr _address;
17 | public MemPatch(byte* address, byte[] data)
18 | {
19 | _data = data;
20 | _orginal = new byte[data.Length];
21 | _address = (IntPtr)address;
22 | Marshal.Copy((IntPtr)address, _orginal, 0, data.Length);
23 | }
24 | public void Install()
25 | {
26 | Marshal.Copy(_data, 0, _address, _data.Length);
27 | }
28 | public void Uninstall()
29 | {
30 | Marshal.Copy(_orginal, 0, _address, _orginal.Length);
31 | }
32 | }
33 |
34 | internal static unsafe class Memory
35 | {
36 | public static MemPatch VignettingPatch;
37 | public static MemPatch VignettingCallPatch;
38 | public static MemPatch TimeScalePatch;
39 | static Memory()
40 | {
41 | // Weapon/radio wheel slow-mo patch
42 | // Thanks @CamxxCore, https://github.com/CamxxCore/GTAVWeaponWheelMod
43 | var result = NativeMemory.FindPattern("\x38\x51\x64\x74\x19", "xxxxx");
44 | if (result == null) { throw new NotSupportedException("Can't find memory pattern to patch weapon/radio slow-mo"); }
45 | var address = result + 26;
46 | address = address + *(int*)address + 4u;
47 | VignettingPatch = new MemPatch(address, new byte[] { RET, 0x90, 0x90, 0x90, 0x90 });
48 | VignettingCallPatch = new MemPatch(result + 8, new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90 });
49 | TimeScalePatch = new MemPatch(result + 34, new byte[] { XOR_32_64, 0xD2 });
50 |
51 | }
52 | public static void ApplyPatches()
53 | {
54 | VignettingPatch.Install();
55 | VignettingCallPatch.Install();
56 | TimeScalePatch.Install();
57 | }
58 | public static void RestorePatches()
59 | {
60 | VignettingPatch.Uninstall();
61 | VignettingCallPatch.Uninstall();
62 | TimeScalePatch.Uninstall();
63 | }
64 | #region PATCHES
65 | #endregion
66 | #region OFFSET-CONST
67 | public const int PositionOffset = 144;
68 | public const int VelocityOffset = 800;
69 | public const int MatrixOffset = 96;
70 | #endregion
71 | #region OPCODE
72 | private const byte XOR_32_64 = 0x31;
73 | private const byte RET = 0xC3;
74 | #endregion
75 | public static Vector3 ReadPosition(this Entity e) => ReadVector3(e.MemoryAddress + PositionOffset);
76 | public static Quaternion ReadQuaternion(this Entity e) => Quaternion.RotationMatrix(e.Matrix);
77 | public static Vector3 ReadRotation(this Entity e) => e.ReadQuaternion().ToEulerDegrees();
78 | public static Vector3 ReadVelocity(this Ped e) => ReadVector3(e.MemoryAddress + VelocityOffset);
79 | public static Vector3 ReadVector3(IntPtr address)
80 | {
81 | float* ptr = (float*)address.ToPointer();
82 | return new Vector3()
83 | {
84 | X = *ptr,
85 | Y = ptr[1],
86 | Z = ptr[2]
87 | };
88 | }
89 | public static List FindOffset(float toSearch, IntPtr start, int range = 1000, float tolerance = 0.01f)
90 | {
91 | var foundOffsets = new List(100);
92 | for (int i = 0; i <= range; i++)
93 | {
94 | var val = NativeMemory.ReadFloat(start + i);
95 | if (Math.Abs(val - toSearch) < tolerance)
96 | {
97 | foundOffsets.Add(i);
98 | }
99 | }
100 | return foundOffsets;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/RageCoop.Client/Util/NativeCaller.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using GTA.Native;
3 | using System;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace RageCoop.Client
7 | {
8 | [StructLayout(LayoutKind.Explicit, Size = 80)]
9 | public struct HeadBlendData
10 | {
11 | [FieldOffset(0)]
12 | public int ShapeFirst;
13 |
14 | [FieldOffset(8)]
15 | public int ShapeSecond;
16 |
17 | [FieldOffset(16)]
18 | public int ShapeThird;
19 |
20 | [FieldOffset(24)]
21 | public int SkinFirst;
22 |
23 | [FieldOffset(32)]
24 | public int SkinSecond;
25 |
26 | [FieldOffset(40)]
27 | public int SkinThird;
28 |
29 | [FieldOffset(48)]
30 | public float ShapeMix;
31 |
32 | [FieldOffset(56)]
33 | public float SkinMix;
34 |
35 | [FieldOffset(64)]
36 | public float ThirdMix;
37 | }
38 |
39 | [StructLayout(LayoutKind.Explicit, Size = 24)]
40 | public struct NativeVector3
41 | {
42 | [FieldOffset(0)]
43 | public float X;
44 |
45 | [FieldOffset(8)]
46 | public float Y;
47 |
48 | [FieldOffset(16)]
49 | public float Z;
50 |
51 | public static implicit operator Vector3(NativeVector3 vec)
52 | {
53 | return new Vector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
54 | }
55 | public static implicit operator NativeVector3(Vector3 vec)
56 | {
57 | return new NativeVector3() { X = vec.X, Y = vec.Y, Z = vec.Z };
58 | }
59 | }
60 | public static class NativeCaller
61 | {
62 | // These are borrowed from ScriptHookVDotNet's
63 | [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeInit@@YAX_K@Z")]
64 | private static extern void NativeInit(ulong hash);
65 |
66 | [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativePush64@@YAX_K@Z")]
67 | private static extern void NativePush64(ulong val);
68 |
69 | [DllImport("ScriptHookV.dll", ExactSpelling = true, EntryPoint = "?nativeCall@@YAPEA_KXZ")]
70 | private static extern unsafe ulong* NativeCall();
71 |
72 | // These are from ScriptHookV's nativeCaller.h
73 | private static unsafe void NativePush(T val) where T : unmanaged
74 | {
75 | ulong val64 = 0;
76 | *(T*)(&val64) = val;
77 | NativePush64(val64);
78 | }
79 |
80 | public static unsafe R Invoke(ulong hash) where R : unmanaged
81 | {
82 | NativeInit(hash);
83 | return *(R*)(NativeCall());
84 | }
85 | public static unsafe R Invoke(Hash hash, params object[] args)
86 | where R : unmanaged
87 | {
88 | NativeInit((ulong)hash);
89 | var arguments = ConvertPrimitiveArguments(args);
90 | foreach (var arg in arguments)
91 | NativePush(arg);
92 |
93 | return *(R*)(NativeCall());
94 | }
95 |
96 |
97 | ///
98 | /// Helper function that converts an array of primitive values to a native stack.
99 | ///
100 | ///
101 | ///
102 | private static unsafe ulong[] ConvertPrimitiveArguments(object[] args)
103 | {
104 | var result = new ulong[args.Length];
105 | for (int i = 0; i < args.Length; ++i)
106 | {
107 | if (args[i] is bool valueBool)
108 | {
109 | result[i] = valueBool ? 1ul : 0ul;
110 | continue;
111 | }
112 | if (args[i] is byte valueByte)
113 | {
114 | result[i] = valueByte;
115 | continue;
116 | }
117 | if (args[i] is int valueInt32)
118 | {
119 | result[i] = (ulong)valueInt32;
120 | continue;
121 | }
122 | if (args[i] is ulong valueUInt64)
123 | {
124 | result[i] = valueUInt64;
125 | continue;
126 | }
127 | if (args[i] is float valueFloat)
128 | {
129 | result[i] = *(ulong*)&valueFloat;
130 | continue;
131 | }
132 | if (args[i] is IntPtr valueIntPtr)
133 | {
134 | result[i] = (ulong)valueIntPtr.ToInt64();
135 | continue;
136 | }
137 |
138 | throw new ArgumentException("Unknown primitive type in native argument list", nameof(args));
139 | }
140 |
141 | return result;
142 | }
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/RageCoop.Client/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/RageCoop.Core/BitReader.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace RageCoop.Core
6 | {
7 | internal class BitReader : BinaryReader
8 | {
9 |
10 | public BitReader(byte[] array) : base(new MemoryStream(array))
11 | {
12 | }
13 |
14 | ~BitReader()
15 | {
16 | Close();
17 | Dispose();
18 | }
19 |
20 | public byte[] ReadByteArray()
21 | {
22 | return base.ReadBytes(ReadInt32());
23 | }
24 | public override string ReadString()
25 | {
26 | return Encoding.UTF8.GetString(ReadBytes(ReadInt32()));
27 | }
28 |
29 | public Vector3 ReadVector3()
30 | {
31 | return new Vector3()
32 | {
33 | X = ReadSingle(),
34 | Y = ReadSingle(),
35 | Z = ReadSingle()
36 | };
37 | }
38 | public Vector2 ReadVector2()
39 | {
40 | return new Vector2()
41 | {
42 | X = ReadSingle(),
43 | Y = ReadSingle()
44 | };
45 | }
46 | public Quaternion ReadQuaternion()
47 | {
48 | return new Quaternion()
49 | {
50 | X = ReadSingle(),
51 | Y = ReadSingle(),
52 | Z = ReadSingle(),
53 | W = ReadSingle()
54 | };
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/RageCoop.Core/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/RageCoop.Core/MathExtensions.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using System;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal static class MathExtensions
7 | {
8 | public const float Deg2Rad = (float)(Math.PI * 2) / 360;
9 | public const float Rad2Deg = 360 / (float)(Math.PI * 2);
10 |
11 | public static Vector3 ToDirection(this Vector3 rotation)
12 | {
13 | double z = DegToRad(rotation.Z);
14 | double x = DegToRad(rotation.X);
15 | double num = Math.Abs(Math.Cos(x));
16 |
17 | return new Vector3
18 | {
19 | X = (float)(-Math.Sin(z) * num),
20 | Y = (float)(Math.Cos(z) * num),
21 | Z = (float)Math.Sin(x)
22 | };
23 | }
24 |
25 | ///
26 | ///
27 | ///
28 | public static Vector3 ToVector(this Quaternion vec)
29 | {
30 | return new Vector3()
31 | {
32 | X = vec.X,
33 | Y = vec.Y,
34 | Z = vec.Z
35 | };
36 | }
37 |
38 | ///
39 | ///
40 | ///
41 | public static Quaternion ToQuaternion(this Vector3 vec, float vW = 0.0f)
42 | {
43 | return new Quaternion()
44 | {
45 | X = vec.X,
46 | Y = vec.Y,
47 | Z = vec.Z,
48 | W = vW
49 | };
50 | }
51 |
52 | public static float Denormalize(this float h)
53 | {
54 | return h < 0f ? h + 360f : h;
55 | }
56 |
57 | public static float ToRadians(this float val)
58 | {
59 | return (float)(Math.PI / 180) * val;
60 | }
61 |
62 | public static Vector3 ToRadians(this Vector3 i)
63 | {
64 | return new Vector3()
65 | {
66 | X = ToRadians(i.X),
67 | Y = ToRadians(i.Y),
68 | Z = ToRadians(i.Z),
69 | };
70 | }
71 |
72 | public static Quaternion ToQuaternion(this Vector3 vect)
73 | {
74 | vect = new Vector3()
75 | {
76 | X = vect.X.Denormalize() * -1,
77 | Y = vect.Y.Denormalize() - 180f,
78 | Z = vect.Z.Denormalize() - 180f,
79 | };
80 |
81 | vect = vect.ToRadians();
82 |
83 | float rollOver2 = vect.Z * 0.5f;
84 | float sinRollOver2 = (float)Math.Sin(rollOver2);
85 | float cosRollOver2 = (float)Math.Cos(rollOver2);
86 | float pitchOver2 = vect.Y * 0.5f;
87 | float sinPitchOver2 = (float)Math.Sin(pitchOver2);
88 | float cosPitchOver2 = (float)Math.Cos(pitchOver2);
89 | float yawOver2 = vect.X * 0.5f; // pitch
90 | float sinYawOver2 = (float)Math.Sin(yawOver2);
91 | float cosYawOver2 = (float)Math.Cos(yawOver2);
92 | Quaternion result = new Quaternion()
93 | {
94 | X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
95 | Y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2,
96 | Z = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2,
97 | W = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2
98 | };
99 | return result;
100 | }
101 | public static double DegToRad(double deg)
102 | {
103 | return deg * Math.PI / 180.0;
104 | }
105 | public static Vector3 ToEulerRotation(this Vector3 dir, Vector3 up)
106 | {
107 | var rot = Quaternion.LookRotation(dir.Normalized, up).ToEulerAngles().ToDegree();
108 | return rot;
109 |
110 | }
111 | public static Vector3 ToDegree(this Vector3 radian)
112 | {
113 | return radian * (float)(180 / Math.PI);
114 | }
115 | public static Vector3 ToEulerDegrees(this Quaternion q)
116 | {
117 | return q.ToEulerAngles().ToDegree();
118 | }
119 | public static Vector3 ToEulerAngles(this Quaternion q)
120 | {
121 | Vector3 angles = new Vector3();
122 |
123 | // roll / x
124 | double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z);
125 | double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y);
126 | angles.X = (float)Math.Atan2(sinr_cosp, cosr_cosp);
127 |
128 | // pitch / y
129 | double sinp = 2 * (q.W * q.Y - q.Z * q.X);
130 | if (Math.Abs(sinp) >= 1)
131 | {
132 | angles.Y = CopySign(Math.PI / 2, sinp);
133 | }
134 | else
135 | {
136 | angles.Y = (float)Math.Asin(sinp);
137 | }
138 |
139 | // yaw / z
140 | double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y);
141 | double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z);
142 | angles.Z = (float)Math.Atan2(siny_cosp, cosy_cosp);
143 |
144 | return angles;
145 | }
146 | private static float CopySign(double x, double y)
147 | {
148 | if (y >= 0)
149 | {
150 | return x >= 0 ? (float)x : (float)-x;
151 | }
152 |
153 | return x >= 0 ? (float)-x : (float)x;
154 | }
155 | public static double AngelTo(this Vector3 v1, Vector3 v2)
156 | {
157 | return Math.Acos(v1.GetCosTheta(v2));
158 | }
159 | public static float GetCosTheta(this Vector3 v1, Vector3 v2)
160 | {
161 | return Vector3.Dot(v1, v2) / (v1.Length() * v2.Length());
162 | }
163 |
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/RageCoop.Core/Networking/CoopPeer.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Threading;
5 |
6 | namespace RageCoop.Core
7 | {
8 | internal class CoopPeer : NetPeer, IDisposable
9 | {
10 | public EventHandler OnMessageReceived;
11 | private readonly Thread ListenerThread;
12 | private bool _stopping = false;
13 | public CoopPeer(NetPeerConfiguration config) : base(config)
14 | {
15 | Start();
16 | NetIncomingMessage msg;
17 | ListenerThread = new Thread(() =>
18 | {
19 | while (!_stopping)
20 | {
21 | msg = WaitMessage(200);
22 | if (msg != null)
23 | {
24 | OnMessageReceived?.Invoke(this, msg);
25 | }
26 | }
27 | });
28 | ListenerThread.Start();
29 | }
30 |
31 | ///
32 | /// Terminate all connections and background thread
33 | ///
34 | public void Dispose()
35 | {
36 | _stopping = true;
37 | Shutdown("Bye!");
38 | ListenerThread.Join();
39 | }
40 | public void SendTo(Packet p, NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
41 | {
42 | NetOutgoingMessage outgoingMessage = CreateMessage();
43 | p.Pack(outgoingMessage);
44 | SendMessage(outgoingMessage, connection, method, (int)channel);
45 | }
46 | public void SendTo(Packet p, IList connections, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
47 | {
48 |
49 | NetOutgoingMessage outgoingMessage = CreateMessage();
50 | p.Pack(outgoingMessage);
51 | SendMessage(outgoingMessage, connections, method, (int)channel);
52 | }
53 | public void Send(Packet p, IList cons, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
54 | {
55 | NetOutgoingMessage outgoingMessage = CreateMessage();
56 | p.Pack(outgoingMessage);
57 | SendMessage(outgoingMessage, cons, method, (int)channel);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RageCoop.Core/Networking/HttpHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Threading;
5 |
6 | namespace RageCoop.Core
7 | {
8 | internal static class HttpHelper
9 | {
10 | public static void DownloadFile(string url, string destination, Action progressCallback)
11 | {
12 | if (File.Exists(destination)) { File.Delete(destination); }
13 | AutoResetEvent ae = new AutoResetEvent(false);
14 | WebClient client = new WebClient();
15 |
16 | // TLS only
17 | ServicePointManager.Expect100Continue = true;
18 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
19 | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
20 |
21 | client.DownloadProgressChanged += (s, e1) => progressCallback?.Invoke(e1.ProgressPercentage);
22 | client.DownloadFileCompleted += (s, e2) =>
23 | {
24 | ae.Set();
25 | };
26 | client.DownloadFileAsync(new Uri(url), destination);
27 | ae.WaitOne();
28 | }
29 | public static string DownloadString(string url)
30 | {
31 | // TLS only
32 | ServicePointManager.Expect100Continue = true;
33 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 |
34 | SecurityProtocolType.Tls11 |
35 | SecurityProtocolType.Tls;
36 | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
37 |
38 | WebClient client = new WebClient();
39 | return client.DownloadString(url);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/RageCoop.Core/Networking/PublicKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RageCoop.Core
4 | {
5 | internal class PublicKey
6 | {
7 | public PublicKey()
8 | {
9 |
10 | }
11 | public static PublicKey FromServerInfo(ServerInfo info)
12 | {
13 | return new PublicKey
14 | {
15 | Modulus = Convert.FromBase64String(info.publicKeyModulus),
16 | Exponent = Convert.FromBase64String(info.publicKeyExponent)
17 | };
18 | }
19 | public byte[] Modulus;
20 | public byte[] Exponent;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RageCoop.Core/Networking/ServerInfo.cs:
--------------------------------------------------------------------------------
1 | namespace RageCoop.Core
2 | {
3 | ///
4 | /// A json object representing a server's information as annouced to master server.
5 | ///
6 | public class ServerInfo
7 | {
8 | #pragma warning disable 1591
9 | public string address { get; set; }
10 | public string port { get; set; }
11 | public string name { get; set; }
12 | public string version { get; set; }
13 | public string players { get; set; }
14 | public string maxPlayers { get; set; }
15 | public string country { get; set; }
16 | public string description { get; set; }
17 | public string website { get; set; }
18 | public string gameMode { get; set; }
19 | public string language { get; set; }
20 |
21 | public bool useP2P { get; set; }
22 |
23 | public bool useZT { get; set; }
24 |
25 | public string ztID { get; set; }
26 |
27 | public string ztAddress { get; set; }
28 | public string publicKeyModulus { get; set; }
29 | public string publicKeyExponent { get; set; }
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/RageCoop.Core/Networking/ZeroTierHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Runtime.InteropServices;
7 | using System.Text.RegularExpressions;
8 |
9 | namespace RageCoop.Core
10 | {
11 | internal class ZeroTierNetwork
12 | {
13 | public ZeroTierNetwork(string line)
14 | {
15 | //
16 | var v = Regex.Split(line, " ").Skip(2).ToArray();
17 | ID = v[0];
18 | Name = v[1];
19 | Mac = v[2];
20 | Status = v[3];
21 | Type = v[4];
22 | Device = v[5];
23 | foreach (var i in v[6].Split(','))
24 | {
25 | Addresses.Add(i.Split('/')[0]);
26 | }
27 | }
28 | public string ID;
29 | public string Name;
30 | public string Mac;
31 | public string Status;
32 | public string Type;
33 | public string Device;
34 | public List Addresses = new List();
35 |
36 | }
37 | internal static class ZeroTierHelper
38 | {
39 | private static readonly string _path = "zerotier-cli";
40 | private static readonly string _arg = "";
41 | static ZeroTierHelper()
42 | {
43 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
44 | {
45 | var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat");
46 | _arg = $"/c \"{batpath}\" ";
47 | _path = "cmd.exe";
48 | }
49 | var status = RunCommand("status");
50 | if (!status.StartsWith("200"))
51 | {
52 | throw new Exception("ZeroTier not ready: " + status);
53 | }
54 | }
55 | public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
56 | {
57 | var p = Run("join " + networkId);
58 | var o = p.StandardOutput.ReadToEnd();
59 | if (!o.StartsWith("200 join OK"))
60 | {
61 | throw new Exception(o + p.StandardError.ReadToEnd());
62 | }
63 | if (timeout == 0) { return Networks[networkId]; }
64 | int i = 0;
65 | while (i <= timeout)
66 | {
67 | i += 100;
68 | if (Networks.TryGetValue(networkId, out var n))
69 | {
70 | if (n.Addresses.Count != 0 && (!n.Addresses.Where(x => x == "-").Any()))
71 | {
72 | return n;
73 | }
74 | System.Threading.Thread.Sleep(100);
75 | }
76 | else
77 | {
78 | break;
79 | }
80 | }
81 | return null;
82 | }
83 | public static void Leave(string networkId)
84 | {
85 | var p = Run("leave " + networkId);
86 | var o = p.StandardOutput.ReadToEnd();
87 | if (!o.StartsWith("200 leave OK"))
88 | {
89 | throw new Exception(o + p.StandardError.ReadToEnd());
90 | }
91 | }
92 | public static Dictionary Networks
93 | {
94 | get
95 | {
96 | Dictionary networks = new Dictionary();
97 | var p = Run("listnetworks");
98 | var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
99 |
100 | foreach (var line in lines)
101 | {
102 | var l = line.Replace("\r", "");
103 | if (!string.IsNullOrWhiteSpace(l))
104 | {
105 | var n = new ZeroTierNetwork(l);
106 | networks.Add(n.ID, n);
107 | }
108 | }
109 | return networks;
110 | }
111 | }
112 | private static Process Run(string args)
113 | {
114 | var p = new Process();
115 | p.StartInfo = new ProcessStartInfo()
116 | {
117 | FileName = _path,
118 | Arguments = _arg + args,
119 | UseShellExecute = false,
120 | RedirectStandardOutput = true,
121 | RedirectStandardError = true,
122 | CreateNoWindow = true,
123 | };
124 | p.Start();
125 | p.WaitForExit();
126 | return p;
127 | }
128 | private static string RunCommand(string command)
129 | {
130 | var p = Run(command);
131 | return p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
132 | }
133 | public static void Check()
134 | {
135 |
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/RageCoop.Core/PacketExtensions.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using Lidgren.Network;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace RageCoop.Core
8 | {
9 | internal static class PacketExtensions
10 | {
11 | #region MESSAGE-READ
12 | public static Vector3 ReadVector3(this NetIncomingMessage m)
13 | {
14 | return new Vector3
15 | {
16 | X = m.ReadFloat(),
17 | Y = m.ReadFloat(),
18 | Z = m.ReadFloat(),
19 | };
20 | }
21 | public static Vector2 ReadVector2(this NetIncomingMessage m)
22 | {
23 | return new Vector2
24 | {
25 | X = m.ReadFloat(),
26 | Y = m.ReadFloat(),
27 | };
28 | }
29 | public static Quaternion ReadQuaternion(this NetIncomingMessage m)
30 | {
31 | return new Quaternion
32 | {
33 | X = m.ReadFloat(),
34 | Y = m.ReadFloat(),
35 | Z = m.ReadFloat(),
36 | W = m.ReadFloat(),
37 | };
38 | }
39 | public static byte[] ReadByteArray(this NetIncomingMessage m)
40 | {
41 | return m.ReadBytes(m.ReadInt32());
42 | }
43 | #endregion
44 |
45 | #region MESSAGE-WRITE
46 | public static void Write(this NetOutgoingMessage m, Vector3 v)
47 | {
48 | m.Write(v.X);
49 | m.Write(v.Y);
50 | m.Write(v.Z);
51 | }
52 | public static void Write(this NetOutgoingMessage m, Quaternion q)
53 | {
54 | m.Write(q.X);
55 | m.Write(q.Y);
56 | m.Write(q.Z);
57 | m.Write(q.W);
58 | }
59 | public static void WriteByteArray(this NetOutgoingMessage m, byte[] b)
60 | {
61 | m.Write(b.Length);
62 | m.Write(b);
63 | }
64 | #endregion
65 |
66 | internal static bool IsSyncEvent(this PacketType p)
67 | {
68 | return (30 <= (byte)p) && ((byte)p <= 40);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/ChatMessage.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using System;
3 |
4 | namespace RageCoop.Core
5 | {
6 |
7 | internal partial class Packets
8 | {
9 |
10 | internal class ChatMessage : Packet
11 | {
12 | public override PacketType Type => PacketType.ChatMessage;
13 | private readonly Func crypt;
14 | private readonly Func decrypt;
15 | public ChatMessage(Func crypter)
16 | {
17 | crypt = crypter;
18 | }
19 | public ChatMessage(Func decrypter)
20 | {
21 | decrypt = decrypter;
22 | }
23 | public string Username { get; set; }
24 |
25 | public string Message { get; set; }
26 |
27 | protected override void Serialize(NetOutgoingMessage m)
28 | {
29 |
30 |
31 |
32 |
33 |
34 | // Write Username
35 | m.Write(Username);
36 |
37 |
38 | // Write Message
39 | m.WriteByteArray(crypt(Message));
40 |
41 | }
42 |
43 | public override void Deserialize(NetIncomingMessage m)
44 | {
45 | #region NetIncomingMessageToPacket
46 |
47 |
48 | // Read username
49 | Username = m.ReadString();
50 |
51 | Message = decrypt(m.ReadByteArray()).GetString();
52 | #endregion
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/CustomEvent.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using System;
3 | namespace RageCoop.Core
4 | {
5 | internal partial class Packets
6 | {
7 |
8 | internal class CustomEvent : Packet
9 | {
10 | public override PacketType Type => (_queued ? PacketType.CustomEventQueued : PacketType.CustomEvent);
11 | public CustomEvent(Func onResolve = null, bool queued = false)
12 | {
13 | _resolve = onResolve;
14 | _queued = queued;
15 | }
16 | private readonly bool _queued;
17 | private Func _resolve { get; set; }
18 | public int Hash { get; set; }
19 | public object[] Args { get; set; }
20 |
21 | protected override void Serialize(NetOutgoingMessage m)
22 | {
23 | Args = Args ?? new object[] { };
24 |
25 | m.Write(Hash);
26 | m.Write(Args.Length);
27 | foreach (var arg in Args)
28 | {
29 | CoreUtils.GetBytesFromObject(arg, m);
30 | }
31 | }
32 |
33 | public override void Deserialize(NetIncomingMessage m)
34 | {
35 |
36 |
37 | Hash = m.ReadInt32();
38 | var len = m.ReadInt32();
39 | Args = new object[len];
40 | for (int i = 0; i < len; i++)
41 | {
42 | byte type = m.ReadByte();
43 | switch (type)
44 | {
45 | case 0x01:
46 | Args[i] = m.ReadByte(); break;
47 | case 0x02:
48 | Args[i] = m.ReadInt32(); break;
49 | case 0x03:
50 | Args[i] = m.ReadUInt16(); break;
51 | case 0x04:
52 | Args[i] = m.ReadInt32(); break;
53 | case 0x05:
54 | Args[i] = m.ReadUInt32(); break;
55 | case 0x06:
56 | Args[i] = m.ReadInt64(); break;
57 | case 0x07:
58 | Args[i] = m.ReadUInt64(); break;
59 | case 0x08:
60 | Args[i] = m.ReadFloat(); break;
61 | case 0x09:
62 | Args[i] = m.ReadBoolean(); break;
63 | case 0x10:
64 | Args[i] = m.ReadString(); break;
65 | case 0x11:
66 | Args[i] = m.ReadVector3(); break;
67 | case 0x12:
68 | Args[i] = m.ReadQuaternion(); break;
69 | case 0x13:
70 | Args[i] = (GTA.Model)m.ReadInt32(); break;
71 | case 0x14:
72 | Args[i] = m.ReadVector2(); break;
73 | case 0x15:
74 | Args[i] = m.ReadByteArray(); break;
75 | default:
76 | if (_resolve == null)
77 | {
78 | throw new InvalidOperationException($"Unexpected type: {type}");
79 | }
80 | else
81 | {
82 | Args[i] = _resolve(type, m); break;
83 | }
84 | }
85 | }
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/FilePackets.cs:
--------------------------------------------------------------------------------
1 |
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal enum FileResponse : byte
7 | {
8 | NeedToDownload = 0,
9 | AlreadyExists = 1,
10 | Completed = 2,
11 | Loaded = 3,
12 | LoadFailed = 4,
13 | }
14 | internal partial class Packets
15 | {
16 | internal class FileTransferRequest : Packet
17 | {
18 | public override PacketType Type => PacketType.FileTransferRequest;
19 | public int ID { get; set; }
20 |
21 | public string Name { get; set; }
22 |
23 | public long FileLength { get; set; }
24 |
25 | protected override void Serialize(NetOutgoingMessage m)
26 | {
27 |
28 |
29 |
30 | // The ID from the download
31 | m.Write(ID);
32 |
33 |
34 | // The name of the file
35 | m.Write(Name);
36 |
37 | // The length of the file
38 | m.Write(FileLength);
39 |
40 | }
41 |
42 | public override void Deserialize(NetIncomingMessage m)
43 | {
44 |
45 |
46 | ID = m.ReadInt32();
47 | Name = m.ReadString();
48 | FileLength = m.ReadInt64();
49 | }
50 | }
51 |
52 | internal class FileTransferResponse : Packet
53 | {
54 | public override PacketType Type => PacketType.FileTransferResponse;
55 | public int ID { get; set; }
56 | public FileResponse Response { get; set; }
57 | protected override void Serialize(NetOutgoingMessage m)
58 | {
59 |
60 | // The ID from the download
61 | m.Write(ID);
62 |
63 | m.Write((byte)Response);
64 |
65 | }
66 |
67 | public override void Deserialize(NetIncomingMessage m)
68 | {
69 |
70 | ID = m.ReadInt32();
71 | Response = (FileResponse)m.ReadByte();
72 | }
73 | }
74 |
75 | internal class FileTransferChunk : Packet
76 | {
77 | public override PacketType Type => PacketType.FileTransferChunk;
78 | public int ID { get; set; }
79 |
80 | public byte[] FileChunk { get; set; }
81 |
82 | protected override void Serialize(NetOutgoingMessage m)
83 | {
84 |
85 |
86 | // The ID from the download
87 | m.Write(ID);
88 | m.WriteByteArray(FileChunk);
89 |
90 | }
91 |
92 | public override void Deserialize(NetIncomingMessage m)
93 | {
94 |
95 | ID = m.ReadInt32();
96 | FileChunk = m.ReadByteArray();
97 | }
98 | }
99 |
100 | internal class FileTransferComplete : Packet
101 | {
102 | public override PacketType Type => PacketType.FileTransferComplete;
103 | public int ID { get; set; }
104 |
105 | protected override void Serialize(NetOutgoingMessage m)
106 | {
107 |
108 |
109 | // The ID for the download
110 | m.Write(ID);
111 |
112 | }
113 |
114 | public override void Deserialize(NetIncomingMessage m)
115 | {
116 |
117 |
118 | ID = m.ReadInt32();
119 | }
120 | }
121 | internal class AllResourcesSent : Packet
122 | {
123 |
124 | public override PacketType Type => PacketType.AllResourcesSent;
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/HolePunch.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 |
3 | namespace RageCoop.Core
4 | {
5 | internal partial class Packets
6 | {
7 |
8 | internal class HolePunchInit : Packet
9 | {
10 | public override PacketType Type => PacketType.HolePunchInit;
11 | public int TargetID { get; set; }
12 | public string TargetInternal { get; set; }
13 | public string TargetExternal { get; set; }
14 | public bool Connect { get; set; }
15 | protected override void Serialize(NetOutgoingMessage m)
16 | {
17 |
18 |
19 | m.Write(TargetID);
20 | m.Write(TargetInternal);
21 | m.Write(TargetExternal);
22 | m.Write(Connect);
23 |
24 |
25 | }
26 |
27 | public override void Deserialize(NetIncomingMessage m)
28 | {
29 | #region NetIncomingMessageToPacket
30 |
31 | TargetID = m.ReadInt32();
32 | TargetInternal = m.ReadString();
33 | TargetExternal = m.ReadString();
34 | Connect = m.ReadBoolean();
35 | #endregion
36 | }
37 | }
38 | internal class HolePunch : Packet
39 | {
40 | public override PacketType Type => PacketType.HolePunch;
41 | public int Puncher { get; set; }
42 |
43 | ///
44 | /// 1:initial, 2:acknowledged, 3:confirmed
45 | ///
46 | public byte Status { get; set; }
47 | protected override void Serialize(NetOutgoingMessage m)
48 | {
49 |
50 |
51 | m.Write(Puncher);
52 | m.Write(Status);
53 |
54 |
55 | }
56 |
57 | public override void Deserialize(NetIncomingMessage m)
58 | {
59 | #region NetIncomingMessageToPacket
60 |
61 | Puncher = m.ReadInt32();
62 | Status = m.ReadByte();
63 | #endregion
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/Misc.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using System.Collections.Generic;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 | ///
9 | /// Request direct connection to another client
10 | ///
11 | internal class ConnectionRequest : Packet
12 | {
13 | public int TargetID { get; set; }
14 | public override PacketType Type => PacketType.ConnectionRequest;
15 | protected override void Serialize(NetOutgoingMessage m)
16 | {
17 | var data = new List(10);
18 | m.Write(TargetID);
19 | }
20 | public override void Deserialize(NetIncomingMessage m)
21 | {
22 |
23 | TargetID = m.ReadInt32();
24 | }
25 | }
26 |
27 |
28 | ///
29 | /// Sent to the host when a direct connection has been established
30 | ///
31 | internal class P2PConnect : Packet
32 | {
33 | public int ID { get; set; }
34 | public override PacketType Type => PacketType.P2PConnect;
35 | protected override void Serialize(NetOutgoingMessage m)
36 | {
37 | var data = new List(10);
38 | m.Write(ID);
39 |
40 | }
41 | public override void Deserialize(NetIncomingMessage m)
42 | {
43 |
44 | ID = m.ReadInt32();
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/Packets.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using System;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal enum PacketType : byte
7 | {
8 | Handshake = 0,
9 | PlayerConnect = 1,
10 | PlayerDisconnect = 2,
11 | PlayerInfoUpdate = 3,
12 | PublicKeyRequest = 4,
13 | PublicKeyResponse = 5,
14 | Request = 6,
15 | Response = 7,
16 | PingPong = 8,
17 | HandshakeSuccess = 9,
18 | ChatMessage = 10,
19 |
20 | FileTransferChunk = 11,
21 | FileTransferRequest = 12,
22 | FileTransferResponse = 13,
23 | FileTransferComplete = 14,
24 | AllResourcesSent = 15,
25 |
26 | CustomEvent = 16,
27 | CustomEventQueued = 17,
28 |
29 | ConnectionRequest = 18,
30 | P2PConnect = 19,
31 | HolePunchInit = 20,
32 | HolePunch = 21,
33 |
34 | Voice = 22,
35 |
36 | #region Sync
37 | PedSync = 23,
38 | VehicleSync = 24,
39 | ProjectileSync = 25,
40 | #endregion
41 |
42 | #region EVENT
43 |
44 | PedKilled = 30,
45 | BulletShot = 31,
46 | VehicleBulletShot = 32,
47 | OwnerChanged = 35,
48 | NozzleTransform = 37,
49 |
50 | #endregion
51 |
52 | Unknown = 255
53 | }
54 | internal enum ConnectionChannel
55 | {
56 | Default = 0,
57 | Chat = 1,
58 | Voice = 2,
59 | Native = 3,
60 | Mod = 4,
61 | File = 5,
62 | Event = 6,
63 | RequestResponse = 7,
64 | PingPong = 8,
65 | VehicleSync = 9,
66 | PedSync = 10,
67 | ProjectileSync = 11,
68 | SyncEvents = 12,
69 | }
70 |
71 | [Flags]
72 | internal enum PedDataFlags : ushort
73 | {
74 | None = 0,
75 | IsAiming = 1 << 0,
76 | IsInStealthMode = 1 << 1,
77 | IsReloading = 1 << 2,
78 | IsJumping = 1 << 3,
79 | IsRagdoll = 1 << 4,
80 | IsOnFire = 1 << 5,
81 | IsInParachuteFreeFall = 1 << 6,
82 | IsParachuteOpen = 1 << 7,
83 | IsOnLadder = 1 << 8,
84 | IsVaulting = 1 << 9,
85 | IsInCover = 1 << 10,
86 | IsInLowCover = 1 << 11,
87 | IsInCoverFacingLeft = 1 << 12,
88 | IsBlindFiring = 1 << 13,
89 | IsInvincible = 1 << 14,
90 | IsFullSync = 1 << 15,
91 | }
92 |
93 | internal enum ProjectileDataFlags : byte
94 | {
95 | None = 0,
96 | Exploded = 1 << 0,
97 | IsAttached = 1 << 1,
98 | IsOrgin = 1 << 2,
99 | IsShotByVehicle = 1 << 3,
100 | }
101 | #region ===== VEHICLE DATA =====
102 | internal enum VehicleDataFlags : ushort
103 | {
104 | None = 0,
105 | IsEngineRunning = 1 << 0,
106 | AreLightsOn = 1 << 1,
107 | AreBrakeLightsOn = 1 << 2,
108 | AreHighBeamsOn = 1 << 3,
109 | IsSirenActive = 1 << 4,
110 | IsDead = 1 << 5,
111 | IsHornActive = 1 << 6,
112 | IsTransformed = 1 << 7,
113 | IsParachuteActive = 1 << 8,
114 | IsRocketBoostActive = 1 << 9,
115 | IsAircraft = 1 << 10,
116 | IsDeluxoHovering = 1 << 11,
117 | HasRoof = 1 << 12,
118 | IsFullSync = 1 << 13,
119 | IsOnFire = 1 << 14,
120 | Repaired = 1 << 15,
121 | }
122 |
123 | internal enum PlayerConfigFlags : byte
124 | {
125 | None = 0,
126 | ShowBlip = 1 << 0,
127 | ShowNameTag = 1 << 1
128 | }
129 |
130 | internal struct VehicleDamageModel
131 | {
132 | public byte BrokenDoors { get; set; }
133 | public byte OpenedDoors { get; set; }
134 | public byte BrokenWindows { get; set; }
135 | public short BurstedTires { get; set; }
136 | public byte LeftHeadLightBroken { get; set; }
137 | public byte RightHeadLightBroken { get; set; }
138 | }
139 | #endregion
140 |
141 | internal interface IPacket
142 | {
143 | PacketType Type { get; }
144 |
145 | void Deserialize(NetIncomingMessage m);
146 | }
147 |
148 | internal abstract class Packet : IPacket
149 | {
150 | public abstract PacketType Type { get; }
151 | public void Pack(NetOutgoingMessage m)
152 | {
153 | m.Write((byte)Type);
154 | Serialize(m);
155 | }
156 | protected virtual void Serialize(NetOutgoingMessage m) { }
157 | public virtual void Deserialize(NetIncomingMessage m) { }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/ProjectileSync.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 | internal class ProjectileSync : Packet
9 | {
10 | public override PacketType Type => PacketType.ProjectileSync;
11 | public int ID { get; set; }
12 |
13 | public int ShooterID { get; set; }
14 | public uint WeaponHash { get; set; }
15 |
16 | public Vector3 Position { get; set; }
17 |
18 | public Vector3 Rotation { get; set; }
19 |
20 | public Vector3 Velocity { get; set; }
21 |
22 | public ProjectileDataFlags Flags { get; set; }
23 |
24 |
25 |
26 | protected override void Serialize(NetOutgoingMessage m)
27 | {
28 |
29 |
30 |
31 | // Write id
32 | m.Write(ID);
33 |
34 | // Write ShooterID
35 | m.Write(ShooterID);
36 |
37 | m.Write(WeaponHash);
38 |
39 | // Write position
40 | m.Write(Position);
41 |
42 |
43 | // Write rotation
44 | m.Write(Rotation);
45 |
46 | // Write velocity
47 | m.Write(Velocity);
48 | m.Write((byte)Flags);
49 |
50 |
51 |
52 | }
53 |
54 | public override void Deserialize(NetIncomingMessage m)
55 | {
56 | #region NetIncomingMessageToPacket
57 |
58 |
59 | // Read id
60 | ID = m.ReadInt32();
61 |
62 | // Read ShooterID
63 | ShooterID = m.ReadInt32();
64 |
65 | WeaponHash = m.ReadUInt32();
66 |
67 | // Read position
68 | Position = m.ReadVector3();
69 |
70 | // Read rotation
71 | Rotation = m.ReadVector3();
72 |
73 | // Read velocity
74 | Velocity = m.ReadVector3();
75 |
76 | Flags = (ProjectileDataFlags)m.ReadByte();
77 |
78 | #endregion
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/SyncEvents/BulletShot.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 |
9 | internal class BulletShot : Packet
10 | {
11 | public override PacketType Type => PacketType.BulletShot;
12 | public int OwnerID { get; set; }
13 |
14 | public uint WeaponHash { get; set; }
15 |
16 | public Vector3 StartPosition { get; set; }
17 | public Vector3 EndPosition { get; set; }
18 |
19 | protected override void Serialize(NetOutgoingMessage m)
20 | {
21 |
22 |
23 |
24 | // Write OwnerID
25 | m.Write(OwnerID);
26 |
27 | // Write weapon hash
28 | m.Write(WeaponHash);
29 |
30 | // Write StartPosition
31 | m.Write(StartPosition);
32 |
33 | // Write EndPosition
34 | m.Write(EndPosition);
35 |
36 |
37 |
38 |
39 | }
40 |
41 | public override void Deserialize(NetIncomingMessage m)
42 | {
43 | #region NetIncomingMessageToPacket
44 |
45 |
46 | // Read OwnerID
47 | OwnerID = m.ReadInt32();
48 |
49 | // Read WeponHash
50 | WeaponHash = m.ReadUInt32();
51 |
52 | // Read StartPosition
53 | StartPosition = m.ReadVector3();
54 |
55 | // Read EndPosition
56 | EndPosition = m.ReadVector3();
57 | #endregion
58 | }
59 | }
60 |
61 |
62 |
63 |
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/SyncEvents/NozzleTransform.cs:
--------------------------------------------------------------------------------
1 |
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 | internal class NozzleTransform : Packet
9 | {
10 | public override PacketType Type => PacketType.NozzleTransform;
11 | public int VehicleID { get; set; }
12 |
13 | public bool Hover { get; set; }
14 |
15 | protected override void Serialize(NetOutgoingMessage m)
16 | {
17 |
18 |
19 |
20 | m.Write(VehicleID);
21 | m.Write(Hover);
22 |
23 |
24 |
25 | }
26 |
27 | public override void Deserialize(NetIncomingMessage m)
28 | {
29 | #region NetIncomingMessageToPacket
30 |
31 | VehicleID = m.ReadInt32();
32 | Hover = m.ReadBoolean();
33 |
34 | #endregion
35 | }
36 | }
37 |
38 |
39 |
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/SyncEvents/OwnerChanged.cs:
--------------------------------------------------------------------------------
1 |
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 |
9 | internal class OwnerChanged : Packet
10 | {
11 | public override PacketType Type => PacketType.OwnerChanged;
12 | public int ID { get; set; }
13 |
14 | public int NewOwnerID { get; set; }
15 |
16 | protected override void Serialize(NetOutgoingMessage m)
17 | {
18 | m.Write(ID);
19 | m.Write(NewOwnerID);
20 | }
21 |
22 | public override void Deserialize(NetIncomingMessage m)
23 | {
24 | #region NetIncomingMessageToPacket
25 |
26 |
27 | ID = m.ReadInt32();
28 | NewOwnerID = m.ReadInt32();
29 |
30 | #endregion
31 | }
32 | }
33 |
34 |
35 |
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/SyncEvents/PedKilled.cs:
--------------------------------------------------------------------------------
1 |
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 |
9 | internal class PedKilled : Packet
10 | {
11 | public override PacketType Type => PacketType.PedKilled;
12 | public int VictimID { get; set; }
13 |
14 | protected override void Serialize(NetOutgoingMessage m)
15 | {
16 |
17 |
18 |
19 | m.Write(VictimID);
20 |
21 |
22 | }
23 |
24 | public override void Deserialize(NetIncomingMessage m)
25 | {
26 | #region NetIncomingMessageToPacket
27 |
28 |
29 | VictimID = m.ReadInt32();
30 |
31 | #endregion
32 | }
33 | }
34 |
35 |
36 |
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/SyncEvents/VehicleBulletShot.cs:
--------------------------------------------------------------------------------
1 | using GTA.Math;
2 | using Lidgren.Network;
3 |
4 | namespace RageCoop.Core
5 | {
6 | internal partial class Packets
7 | {
8 |
9 | internal class VehicleBulletShot : Packet
10 | {
11 | public override PacketType Type => PacketType.VehicleBulletShot;
12 | public int OwnerID { get; set; }
13 | public ushort Bone { get; set; }
14 | public uint WeaponHash { get; set; }
15 |
16 | public Vector3 StartPosition { get; set; }
17 | public Vector3 EndPosition { get; set; }
18 |
19 | protected override void Serialize(NetOutgoingMessage m)
20 | {
21 |
22 |
23 |
24 | m.Write(OwnerID);
25 | m.Write(Bone);
26 | m.Write(WeaponHash);
27 | m.Write(StartPosition);
28 | m.Write(EndPosition);
29 |
30 |
31 |
32 | }
33 |
34 | public override void Deserialize(NetIncomingMessage m)
35 | {
36 | #region NetIncomingMessageToPacket
37 |
38 |
39 | OwnerID = m.ReadInt32();
40 | Bone = m.ReadUInt16();
41 | WeaponHash = m.ReadUInt32();
42 | StartPosition = m.ReadVector3();
43 | EndPosition = m.ReadVector3();
44 | #endregion
45 | }
46 | }
47 |
48 |
49 |
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RageCoop.Core/Packets/Voice.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 |
3 | namespace RageCoop.Core
4 | {
5 | internal partial class Packets
6 | {
7 | internal class Voice : Packet
8 | {
9 | public int ID { get; set; }
10 | public byte[] Buffer { get; set; }
11 | public int Recorded { get; set; }
12 | public override PacketType Type => PacketType.Voice;
13 | protected override void Serialize(NetOutgoingMessage m)
14 | {
15 | m.Write(ID);
16 | m.Write(Buffer);
17 | m.Write(Recorded);
18 |
19 | }
20 | public override void Deserialize(NetIncomingMessage m)
21 | {
22 |
23 | ID = m.ReadInt32();
24 | Buffer = m.ReadByteArray();
25 | Recorded = m.ReadInt32();
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RageCoop.Core/RageCoop.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | True
6 | 0.1
7 | 0.1
8 | 0.1
9 | embedded
10 | True
11 | True
12 |
13 |
14 | ..\bin\Debug\Core
15 |
16 |
17 | ..\bin\Release\Core
18 |
19 |
20 | 4
21 |
22 |
23 |
24 | 4
25 |
26 |
27 |
28 |
29 | all
30 | runtime; build; native; contentfiles; analyzers; buildtransitive
31 |
32 |
33 | all
34 | runtime; build; native; contentfiles; analyzers; buildtransitive
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | ..\libs\Lidgren.Network.dll
44 |
45 |
46 | ..\libs\Newtonsoft.Json.dll
47 |
48 |
49 | ..\libs\ScriptHookVDotNet3.dll
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/RageCoop.Core/Scripting/CustomEvents.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 |
6 | namespace RageCoop.Core.Scripting
7 | {
8 | ///
9 | ///
10 | ///
11 | public static class CustomEvents
12 | {
13 | private static readonly MD5 Hasher = MD5.Create();
14 | private static readonly Dictionary Hashed = new Dictionary();
15 | internal static readonly int OnPlayerDied = Hash("RageCoop.OnPlayerDied");
16 | internal static readonly int SetWeather = Hash("RageCoop.SetWeather");
17 | internal static readonly int OnPedDeleted = Hash("RageCoop.OnPedDeleted");
18 | internal static readonly int OnVehicleDeleted = Hash("RageCoop.OnVehicleDeleted");
19 | internal static readonly int SetAutoRespawn = Hash("RageCoop.SetAutoRespawn");
20 | internal static readonly int SetDisplayNameTag = Hash("RageCoop.SetDisplayNameTag");
21 | internal static readonly int NativeCall = Hash("RageCoop.NativeCall");
22 | internal static readonly int NativeResponse = Hash("RageCoop.NativeResponse");
23 | internal static readonly int AllResourcesSent = Hash("RageCoop.AllResourcesSent");
24 | internal static readonly int ServerPropSync = Hash("RageCoop.ServerPropSync");
25 | internal static readonly int ServerBlipSync = Hash("RageCoop.ServerBlipSync");
26 | internal static readonly int SetEntity = Hash("RageCoop.SetEntity");
27 | internal static readonly int DeleteServerProp = Hash("RageCoop.DeleteServerProp");
28 | internal static readonly int UpdatePedBlip = Hash("RageCoop.UpdatePedBlip");
29 | internal static readonly int DeleteEntity = Hash("RageCoop.DeleteEntity");
30 | internal static readonly int DeleteServerBlip = Hash("RageCoop.DeleteServerBlip");
31 | internal static readonly int CreateVehicle = Hash("RageCoop.CreateVehicle");
32 | internal static readonly int WeatherTimeSync = Hash("RageCoop.WeatherTimeSync");
33 | internal static readonly int IsHost = Hash("RageCoop.IsHost");
34 | ///
35 | /// Get a Int32 hash of a string.
36 | ///
37 | ///
38 | ///
39 | /// The exception is thrown when the name did not match a previously computed one and the hash was the same.
40 | public static int Hash(string s)
41 | {
42 | var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
43 | lock (Hashed)
44 | {
45 | if (Hashed.TryGetValue(hash, out string name))
46 | {
47 | if (name != s)
48 | {
49 | throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}");
50 | }
51 |
52 | return hash;
53 | }
54 |
55 | Hashed.Add(hash, s);
56 | return hash;
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RageCoop.Core/Scripting/ResourceFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace RageCoop.Core.Scripting
5 | {
6 | ///
7 | ///
8 | ///
9 | public class ResourceFile
10 | {
11 | ///
12 | /// Full name with relative path of this file
13 | ///
14 | public string Name { get; internal set; }
15 | ///
16 | /// Whether this is a directory
17 | ///
18 | public bool IsDirectory { get; internal set; }
19 | ///
20 | /// Get a stream that can be used to read file content.
21 | ///
22 | public Func GetStream { get; internal set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/RageCoop.Core/Worker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Threading;
4 |
5 | namespace RageCoop.Core
6 | {
7 | ///
8 | /// A worker that constantly execute jobs in a background thread.
9 | ///
10 | public class Worker : IDisposable
11 | {
12 | private readonly SemaphoreSlim _semaphoreSlim;
13 | private readonly Thread _workerThread;
14 | private bool _stopping = false;
15 | ///
16 | /// Name of the worker
17 | ///
18 | public string Name { get; set; }
19 | ///
20 | /// Whether this worker is busy executing job(s).
21 | ///
22 | public bool IsBusy { get; private set; }
23 | internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue)
24 | {
25 | Name = name;
26 | _semaphoreSlim = new SemaphoreSlim(0, maxJobs);
27 | _workerThread = new Thread(() =>
28 | {
29 | while (!_stopping)
30 | {
31 | IsBusy = false;
32 | _semaphoreSlim.Wait();
33 | if (Jobs.TryDequeue(out var job))
34 | {
35 | IsBusy = true;
36 | try
37 | {
38 | job.Invoke();
39 | }
40 | catch (Exception ex)
41 | {
42 | logger.Error("Error occurred when executing queued job:");
43 | logger.Error(ex);
44 | }
45 | }
46 | else
47 | {
48 | throw new InvalidOperationException("Hmm... that's unexpected.");
49 | }
50 | }
51 | IsBusy = false;
52 | });
53 | _workerThread.Start();
54 | }
55 | ///
56 | /// Queue a job to be executed
57 | ///
58 | ///
59 | public void QueueJob(Action work)
60 | {
61 | Jobs.Enqueue(work);
62 | _semaphoreSlim.Release();
63 | }
64 | ///
65 | /// Finish current job and stop the worker.
66 | ///
67 | public void Stop()
68 | {
69 | _stopping = true;
70 | QueueJob(() => { });
71 | if (_workerThread.IsAlive)
72 | {
73 | _workerThread.Join();
74 | }
75 | }
76 | ///
77 | /// Finish current job and stop the worker.
78 | ///
79 | public void Dispose()
80 | {
81 | Stop();
82 | _semaphoreSlim.Dispose();
83 | }
84 | private readonly ConcurrentQueue Jobs = new ConcurrentQueue();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/RageCoop.Server/FileTransfer.cs:
--------------------------------------------------------------------------------
1 | namespace RageCoop.Server
2 | {
3 | internal class FileTransfer
4 | {
5 | public int ID { get; set; }
6 | public float Progress { get; set; }
7 | public string Name { get; set; }
8 | public bool Cancel { get; set; } = false;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/RageCoop.Server/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/RageCoop.Server/HolePunch.cs:
--------------------------------------------------------------------------------
1 | namespace RageCoop.Server
2 | {
3 | internal class HolePunch
4 | {
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/RageCoop.Server/Networking/Server.EntitySync.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using RageCoop.Core;
3 | using RageCoop.Server.Scripting;
4 |
5 | namespace RageCoop.Server
6 | {
7 | public partial class Server
8 | {
9 | private void PedSync(Packets.PedSync packet, Client client)
10 | {
11 | QueueJob(() => Entities.Update(packet, client));
12 |
13 | bool isPlayer = packet.ID == client.Player.ID;
14 | if (isPlayer)
15 | {
16 | QueueJob(() => API.Events.InvokePlayerUpdate(client));
17 | }
18 |
19 | if (Settings.UseP2P) { return; }
20 | foreach (var c in ClientsByNetHandle.Values)
21 | {
22 |
23 | // Don't send data back
24 | if (c.NetHandle == client.NetHandle) { continue; }
25 |
26 | // Check streaming distance
27 | if (isPlayer)
28 | {
29 | if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
30 | {
31 | continue;
32 | }
33 | }
34 | else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
35 | {
36 | continue;
37 | }
38 |
39 | NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
40 | packet.Pack(outgoingMessage);
41 | MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
42 | }
43 | }
44 | private void VehicleSync(Packets.VehicleSync packet, Client client)
45 | {
46 | QueueJob(() => Entities.Update(packet, client));
47 | bool isPlayer = packet.ID == client.Player?.LastVehicle?.ID;
48 |
49 |
50 | if (Settings.UseP2P) { return; }
51 | foreach (var c in ClientsByNetHandle.Values)
52 | {
53 | if (c.NetHandle == client.NetHandle) { continue; }
54 | if (isPlayer)
55 | {
56 | // Player's vehicle
57 | if ((Settings.PlayerStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.PlayerStreamingDistance))
58 | {
59 | continue;
60 | }
61 | }
62 | else if ((Settings.NpcStreamingDistance != -1) && (packet.Position.DistanceTo(c.Player.Position) > Settings.NpcStreamingDistance))
63 | {
64 | continue;
65 | }
66 | NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
67 | packet.Pack(outgoingMessage);
68 | MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.VehicleSync);
69 | }
70 | }
71 | private void ProjectileSync(Packets.ProjectileSync packet, Client client)
72 | {
73 | if (Settings.UseP2P) { return; }
74 | Forward(packet, client, ConnectionChannel.ProjectileSync);
75 | }
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/RageCoop.Server/Networking/Server.HolePunch.cs:
--------------------------------------------------------------------------------
1 | using Lidgren.Network;
2 | using RageCoop.Core;
3 |
4 | namespace RageCoop.Server
5 | {
6 | public partial class Server
7 | {
8 | private void HolePunch(Client host, Client client)
9 | {
10 | // Send to host
11 | Send(new Packets.HolePunchInit
12 | {
13 | Connect = false,
14 | TargetID = client.Player.ID,
15 | TargetInternal = client.InternalEndPoint.ToString(),
16 | TargetExternal = client.EndPoint.ToString()
17 | }, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
18 |
19 | // Send to client
20 | Send(new Packets.HolePunchInit
21 | {
22 | Connect = true,
23 | TargetID = host.Player.ID,
24 | TargetInternal = host.InternalEndPoint.ToString(),
25 | TargetExternal = host.EndPoint.ToString()
26 | }, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RageCoop.Server/Program.cs:
--------------------------------------------------------------------------------
1 | using RageCoop.Core;
2 | using System;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Threading;
6 |
7 | namespace RageCoop.Server
8 | {
9 | internal class Program
10 | {
11 | private static bool Stopping = false;
12 | private static Logger mainLogger;
13 |
14 | private static void Main(string[] args)
15 | {
16 | if (args.Length >= 2 && args[0] == "update")
17 | {
18 | var target = args[1];
19 | int i = 0;
20 | while (i++ < 10)
21 | {
22 | try
23 | {
24 | Console.WriteLine("Applying update to " + target);
25 |
26 | CoreUtils.CopyFilesRecursively(new(AppDomain.CurrentDomain.BaseDirectory), new(target));
27 | Process.Start(Path.Combine(target, "RageCoop.Server"));
28 | Environment.Exit(0);
29 | }
30 | catch (Exception ex)
31 | {
32 | Console.WriteLine(ex.ToString());
33 | Thread.Sleep(3000);
34 | }
35 | }
36 | Environment.Exit(i);
37 | }
38 | AppDomain.CurrentDomain.UnhandledException += UnhandledException;
39 | mainLogger = new Logger()
40 | {
41 | LogPath = "RageCoop.Server.log",
42 | UseConsole = true,
43 | Name = "Server"
44 | };
45 | try
46 | {
47 | Console.Title = "RAGECOOP";
48 | var setting = Util.Read("Settings.xml");
49 | #if DEBUG
50 | setting.LogLevel = 0;
51 | #endif
52 | var server = new Server(setting, mainLogger);
53 | Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e)
54 | {
55 | mainLogger.Info("Initiating shutdown sequence...");
56 | mainLogger.Info("Press Ctrl+C again to commence an emergency shutdown.");
57 | if (e.SpecialKey == ConsoleSpecialKey.ControlC)
58 | {
59 | if (!Stopping)
60 | {
61 | e.Cancel = true;
62 | Stopping = true;
63 | server.Stop();
64 | mainLogger.Info("Server stopped.");
65 | mainLogger.Dispose();
66 | Thread.Sleep(1000);
67 | Environment.Exit(0);
68 | }
69 | else
70 | {
71 | mainLogger.Flush();
72 | Environment.Exit(1);
73 | }
74 | }
75 | };
76 | server.Start();
77 | mainLogger?.Info("Please use CTRL + C if you want to stop the server!");
78 | mainLogger?.Info("Type here to send chat messages or execute commands");
79 | mainLogger?.Flush();
80 | while (true)
81 | {
82 |
83 | var s = Console.ReadLine();
84 | if (!Stopping && s != null)
85 | {
86 | server.ChatMessageReceived("Server", s, null);
87 | }
88 | Thread.Sleep(20);
89 | }
90 | }
91 | catch (Exception e)
92 | {
93 | Fatal(e);
94 | }
95 | }
96 |
97 | private static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
98 | {
99 | mainLogger.Error($"Unhandled exception thrown from user thread", e.ExceptionObject as Exception);
100 | mainLogger.Flush();
101 | }
102 |
103 | private static void Fatal(Exception e)
104 | {
105 | mainLogger.Error(e);
106 | mainLogger.Error($"Fatal error occurred, server shutting down.");
107 | mainLogger.Flush();
108 | Thread.Sleep(5000);
109 | Environment.Exit(1);
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/RageCoop.Server/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Resources;
6 |
7 | // General Information
8 | [assembly: AssemblyTitle("RageCoop.Server")]
9 | [assembly: AssemblyDescription("RageCoop.Server")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("RAGECOOP")]
12 | [assembly: AssemblyProduct("RageCoop.Server")]
13 | [assembly: AssemblyCopyright("Copyright © 2022")]
14 | [assembly: AssemblyTrademark("RAGECOOP")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Version information
18 | [assembly: AssemblyVersion("1.5.4.5")]
19 | [assembly: AssemblyFileVersion("1.5.4.5")]
20 | [assembly: NeutralResourcesLanguageAttribute( "en-US" )]
21 |
22 |
--------------------------------------------------------------------------------
/RageCoop.Server/Properties/AssemblyInfo.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="true" hostspecific="true" language="C#" #>
2 | <#@ output extension=".cs" #>
3 | <#@ import namespace="System.IO" #>
4 | <#@ import namespace="System.Text.RegularExpressions" #>
5 | <#
6 | string output = File.ReadAllText(this.Host.ResolvePath("AssemblyInfo.cs"));
7 | Regex pattern = new Regex("AssemblyVersion\\(\"(?\\d+)\\.(?\\d+)\\.(?\\d+)\\.(?\\d+)\"\\)");
8 | MatchCollection matches = pattern.Matches(output);
9 | if( matches.Count == 1 )
10 | {
11 | major = Convert.ToInt32(matches[0].Groups["major"].Value);
12 | minor = Convert.ToInt32(matches[0].Groups["minor"].Value);
13 | build = Convert.ToInt32(matches[0].Groups["build"].Value) + 1;
14 | revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
15 | if( this.Host.ResolveParameterValue("-","-","BuildConfiguration") == "Release" )
16 | revision++;
17 | }
18 | #>
19 |
20 | using System.Reflection;
21 | using System.Runtime.CompilerServices;
22 | using System.Runtime.InteropServices;
23 | using System.Resources;
24 |
25 | // General Information
26 | [assembly: AssemblyTitle("RageCoop.Server")]
27 | [assembly: AssemblyDescription("RageCoop.Server")]
28 | [assembly: AssemblyConfiguration("")]
29 | [assembly: AssemblyCompany("RAGECOOP")]
30 | [assembly: AssemblyProduct("RageCoop.Server")]
31 | [assembly: AssemblyCopyright("Copyright © 2022")]
32 | [assembly: AssemblyTrademark("RAGECOOP")]
33 | [assembly: AssemblyCulture("")]
34 |
35 | // Version information
36 | [assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
37 | [assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
38 | [assembly: NeutralResourcesLanguageAttribute( "en-US" )]
39 |
40 | <#+
41 | int major = 1;
42 | int minor = 0;
43 | int revision = 0;
44 | int build = 0;
45 | #>
--------------------------------------------------------------------------------
/RageCoop.Server/RageCoop.Server.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | Exe
6 | net6.0
7 |
8 |
9 | https://github.com/RAGECOOP/RAGECOOP-V
10 | https://ragecoop.online/
11 | True
12 | MIT
13 | $(AssemblyName)
14 | RageCoop.Server
15 | RAGECOOP
16 |
17 | embedded
18 | True
19 | An library for hosting a RAGECOOP server or API reference for developing a resource.
20 | icon.ico
21 | icon.png
22 | True
23 |
24 |
25 |
26 | ..\bin\Debug\Server
27 |
28 |
29 | ..\bin\Release\Server
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | True
38 | \
39 |
40 |
41 | True
42 | True
43 | AssemblyInfo.tt
44 |
45 |
46 |
47 |
48 |
49 | all
50 | runtime; build; native; contentfiles; analyzers; buildtransitive
51 |
52 |
53 | all
54 | runtime; build; native; contentfiles; analyzers; buildtransitive
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | ..\libs\Lidgren.Network.dll
67 |
68 |
69 | ..\libs\McMaster.NETCore.Plugins.dll
70 |
71 |
72 | ..\libs\Newtonsoft.Json.dll
73 |
74 |
75 | ..\libs\ScriptHookVDotNet3.dll
76 |
77 |
78 |
79 |
80 |
81 | TextTemplatingFileGenerator
82 | AssemblyInfo.cs
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | True
93 | True
94 | AssemblyInfo.tt
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/RageCoop.Server/RageCoop.Server.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <_LastSelectedProfileId>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml
5 |
6 |
--------------------------------------------------------------------------------
/RageCoop.Server/Scripting/BaseScript.cs:
--------------------------------------------------------------------------------
1 | using RageCoop.Core.Scripting;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace RageCoop.Server.Scripting
7 | {
8 | internal class BaseScript : ServerScript
9 | {
10 | private readonly Server Server;
11 | public BaseScript(Server server) { Server = server; }
12 | public override void OnStart()
13 | {
14 | API.RegisterCustomEventHandler(CustomEvents.NativeResponse, NativeResponse);
15 | API.RegisterCustomEventHandler(CustomEvents.OnVehicleDeleted, (e) =>
16 | {
17 | API.Entities.RemoveVehicle((int)e.Args[0]);
18 | });
19 | API.RegisterCustomEventHandler(CustomEvents.OnPedDeleted, (e) =>
20 | {
21 | API.Entities.RemovePed((int)e.Args[0]);
22 | });
23 | API.RegisterCustomEventHandler(CustomEvents.WeatherTimeSync, (e) =>
24 | {
25 | if (Server.Settings.WeatherTimeSync)
26 | {
27 | if (e.Client != API.Host) { e.Client.SendCustomEvent(CustomEvents.IsHost, false); return; }
28 |
29 | foreach (var c in API.GetAllClients().Values)
30 | {
31 | if (c == e.Client)
32 | {
33 | continue;
34 | }
35 | c.SendCustomEventQueued(CustomEvents.WeatherTimeSync, e.Args);
36 | }
37 | }
38 | });
39 | API.RegisterCustomEventHandler(CustomEvents.OnPlayerDied, (e) =>
40 | {
41 | API.SendCustomEventQueued(API.GetAllClients().Values.Where(x => x != e.Client).ToList(), CustomEvents.OnPlayerDied, e.Args);
42 | });
43 | API.Events.OnChatMessage += (s, e) =>
44 | Server.Logger?.Info((e.Client?.Username ?? e.ClaimedSender ?? "Unknown") + ": " + e.Message);
45 | }
46 | public override void OnStop()
47 | {
48 | }
49 | public static void SetAutoRespawn(Client c, bool toggle)
50 | {
51 | c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle);
52 | }
53 | public void SetNameTag(Client c, bool toggle)
54 | {
55 | foreach (var other in API.GetAllClients().Values)
56 | {
57 | if (c == other) { continue; }
58 | other.SendCustomEvent(CustomEvents.SetDisplayNameTag, c.Player.ID, toggle);
59 | }
60 | }
61 | public void SendServerPropsTo(List objects, List clients = null)
62 | {
63 | foreach (var obj in objects)
64 | {
65 | API.SendCustomEventQueued(clients, CustomEvents.ServerPropSync, obj.ID, obj.Model, obj.Position, obj.Rotation);
66 | }
67 | }
68 | public void SendServerBlipsTo(List objects, List clients = null)
69 | {
70 | foreach (var obj in objects)
71 | {
72 | API.SendCustomEventQueued(clients, CustomEvents.ServerBlipSync, obj.ID, (ushort)obj.Sprite, (byte)obj.Color, obj.Scale, obj.Position, obj.Rotation, obj.Name);
73 | }
74 | }
75 |
76 | private void NativeResponse(CustomEventReceivedArgs e)
77 | {
78 | try
79 | {
80 | int id = (int)e.Args[0];
81 | lock (e.Client.Callbacks)
82 | {
83 | if (e.Client.Callbacks.TryGetValue(id, out Action