├── .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 | 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 callback)) 84 | { 85 | callback(e.Args[1]); 86 | e.Client.Callbacks.Remove(id); 87 | } 88 | } 89 | } 90 | catch (Exception ex) 91 | { 92 | API.Logger.Error("Failed to parse NativeResponse"); 93 | API.Logger.Error(ex); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /RageCoop.Server/Scripting/EventArgs/EventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | 5 | namespace RageCoop.Server.Scripting 6 | { 7 | /// 8 | /// 9 | /// 10 | public class ChatEventArgs : EventArgs 11 | { 12 | /// 13 | /// The client that sent this message, will be null if sent from server 14 | /// 15 | public Client Client { get; set; } 16 | /// 17 | /// Message 18 | /// 19 | public string Message { get; set; } 20 | 21 | /// 22 | /// Only used when sending a message via 23 | /// 24 | public string ClaimedSender { get; set; } 25 | } 26 | /// 27 | /// 28 | /// 29 | public class CustomEventReceivedArgs : EventArgs 30 | { 31 | /// 32 | /// The that triggered this event 33 | /// 34 | public Client Client { get; set; } 35 | 36 | /// 37 | /// The event hash 38 | /// 39 | public int Hash { get; set; } 40 | 41 | /// 42 | /// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion, Vector2 43 | /// 44 | public object[] Args { get; set; } 45 | } 46 | /// 47 | /// 48 | /// 49 | public class OnCommandEventArgs : EventArgs 50 | { 51 | /// 52 | /// The that executed this command, will be null if sent from server. 53 | /// 54 | public Client Client { get; set; } 55 | /// 56 | /// The name of executed command 57 | /// 58 | public string Name { get; set; } 59 | /// 60 | /// Arguments 61 | /// 62 | public string[] Args { get; set; } 63 | /// 64 | /// If this value was set to true, corresponding handler registered with will not be invoked. 65 | /// 66 | public bool Cancel { get; set; } = false; 67 | } 68 | /// 69 | /// 70 | /// 71 | public class HandshakeEventArgs : EventArgs 72 | { 73 | /// 74 | /// The player's ID 75 | /// 76 | public int ID { get; set; } 77 | /// 78 | /// The claimed username 79 | /// 80 | public string Username { get; set; } 81 | 82 | /// 83 | /// The client password hashed with SHA256 algorithm. 84 | /// 85 | public string PasswordHash { get; set; } 86 | 87 | /// 88 | /// The that sent the handshake request. 89 | /// 90 | public IPEndPoint EndPoint { get; set; } 91 | /// 92 | /// Deny the connection attempt 93 | /// 94 | /// 95 | public void Deny(string reason) 96 | { 97 | DenyReason = reason; 98 | Cancel = true; 99 | } 100 | internal string DenyReason { get; set; } 101 | internal bool Cancel { get; set; } = false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /RageCoop.Server/Scripting/ServerScript.cs: -------------------------------------------------------------------------------- 1 | using RageCoop.Core.Scripting; 2 | using System; 3 | 4 | namespace RageCoop.Server.Scripting 5 | { 6 | /// 7 | /// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded and will be null, you should use . to initiate your script. 8 | /// 9 | public abstract class ServerScript 10 | { 11 | /// 12 | /// This method would be called from listener thread after all scripts have been loaded. 13 | /// 14 | public abstract void OnStart(); 15 | 16 | /// 17 | /// This method would be called from listener thread when the server is shutting down, you MUST terminate all background jobs/threads in this method. 18 | /// 19 | public abstract void OnStop(); 20 | 21 | /// 22 | /// Get the instance that can be used to control the server. 23 | /// 24 | public API API { get; set; } 25 | 26 | /// 27 | /// Get the this script belongs to, this property won't be initiated before . 28 | /// 29 | public ServerResource CurrentResource { get; internal set; } 30 | /// 31 | /// Get the that the script belongs to. 32 | /// 33 | public ResourceFile CurrentFile { get; internal set; } 34 | 35 | /// 36 | /// Eqivalent of in 37 | /// 38 | public Core.Logger Logger => CurrentResource.Logger; 39 | } 40 | /// 41 | /// Decorate your method with this attribute and use or to register commands. 42 | /// 43 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 44 | public class Command : Attribute 45 | { 46 | /// 47 | /// Sets name of the command 48 | /// 49 | public string Name { get; set; } 50 | 51 | /// 52 | /// Set the Usage (Example: "Please use "/help"". ArgsLength required!) 53 | /// 54 | public string Usage { get; set; } 55 | 56 | /// 57 | /// Set the length of arguments (Example: 2 for "/message USERNAME MESSAGE". Usage required!) 58 | /// 59 | public short ArgsLength { get; set; } 60 | 61 | /// 62 | /// 63 | /// 64 | /// Name of the command 65 | public Command(string name) 66 | { 67 | Name = name; 68 | } 69 | } 70 | 71 | /// 72 | /// The context containg command information. 73 | /// 74 | public class CommandContext 75 | { 76 | /// 77 | /// Gets the client which executed the command 78 | /// 79 | public Client Client { get; internal set; } 80 | 81 | /// 82 | /// Gets the arguments (Example: "/message USERNAME MESSAGE", Args[0] for USERNAME) 83 | /// 84 | public string[] Args { get; internal set; } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /RageCoop.Server/Security.cs: -------------------------------------------------------------------------------- 1 | using RageCoop.Core; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Security.Cryptography; 6 | namespace RageCoop.Server 7 | { 8 | internal class Security 9 | { 10 | private readonly Logger Logger; 11 | public Security(Logger logger) 12 | { 13 | Logger = logger; 14 | } 15 | public RSA RSA = RSA.Create(2048); 16 | private readonly Dictionary SecuredConnections = new Dictionary(); 17 | 18 | public bool HasSecuredConnection(IPEndPoint target) 19 | { 20 | return SecuredConnections.ContainsKey(target); 21 | } 22 | 23 | public byte[] Encrypt(byte[] data, IPEndPoint target) 24 | { 25 | var ms = new MemoryStream(); 26 | using (var cs = new CryptoStream(ms, SecuredConnections[target].CreateEncryptor(), CryptoStreamMode.Write)) 27 | { 28 | cs.Write(data, 0, data.Length); 29 | } 30 | return ms.ToArray(); 31 | } 32 | public byte[] Decrypt(byte[] data, IPEndPoint target) 33 | { 34 | return new CryptoStream(new MemoryStream(data), SecuredConnections[target].CreateDecryptor(), CryptoStreamMode.Read).ReadToEnd(); 35 | } 36 | 37 | public void AddConnection(IPEndPoint endpoint, byte[] cryptedKey, byte[] cryptedIV) 38 | { 39 | var key = RSA.Decrypt(cryptedKey, RSAEncryptionPadding.Pkcs1); 40 | var iv = RSA.Decrypt(cryptedIV, RSAEncryptionPadding.Pkcs1); 41 | // Logger?.Debug($"key:{key.Dump()}, iv:{iv.Dump()}"); 42 | var conAes = Aes.Create(); 43 | conAes.Key = key; 44 | conAes.IV = iv; 45 | if (!SecuredConnections.ContainsKey(endpoint)) 46 | { 47 | SecuredConnections.Add(endpoint, conAes); 48 | } 49 | else 50 | { 51 | SecuredConnections[endpoint] = conAes; 52 | } 53 | } 54 | public void RemoveConnection(IPEndPoint ep) 55 | { 56 | if (SecuredConnections.ContainsKey(ep)) 57 | { 58 | SecuredConnections.Remove(ep); 59 | } 60 | } 61 | public void GetPublicKey(out byte[] modulus, out byte[] exponent) 62 | { 63 | var key = RSA.ExportParameters(false); 64 | modulus = key.Modulus; 65 | exponent = key.Exponent; 66 | } 67 | public void ClearConnections() 68 | { 69 | SecuredConnections.Clear(); 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /RageCoop.Server/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace RageCoop.Server 2 | { 3 | /// 4 | /// Settings for RageCoop Server 5 | /// 6 | public class Settings 7 | { 8 | /// 9 | /// Port to listen for incoming connections 10 | /// 11 | public int Port { get; set; } = 4499; 12 | 13 | /// 14 | /// Maximum number of players on this server 15 | /// 16 | public int MaxPlayers { get; set; } = 32; 17 | 18 | /// 19 | /// Maximum latency allowed for a client, a client will be kicked if it's latency it's higher than this value 20 | /// 21 | public int MaxLatency { get; set; } = 500; 22 | 23 | /// 24 | /// The server name to be shown on master server 25 | /// 26 | public string Name { get; set; } = "RAGECOOP server"; 27 | 28 | /// 29 | /// The website address to be shown on master server 30 | /// 31 | public string Website { get; set; } = "https://ragecoop.com/"; 32 | 33 | /// 34 | /// The description to be shown on master server 35 | /// 36 | public string Description { get; set; } = "RAGECOOP server"; 37 | 38 | /// 39 | /// The game mode to be shown on master server 40 | /// 41 | public string GameMode { get; set; } = "FreeRoam"; 42 | 43 | /// 44 | /// The language to be shown on master server 45 | /// 46 | public string Language { get; set; } = "English"; 47 | 48 | /// 49 | /// The message to send when a client connected (not visible to others) 50 | /// 51 | public string WelcomeMessage { get; set; } = "Welcome on this server :)"; 52 | 53 | /// 54 | /// Whether or not to announce this server so it'll appear on server list. 55 | /// 56 | public bool AnnounceSelf { get; set; } = false; 57 | 58 | /// 59 | /// Master server address, mostly doesn't need to be changed. 60 | /// 61 | public string MasterServer { get; set; } = "https://masterserver.ragecoop.com/"; 62 | 63 | /// 64 | /// See . 65 | /// 66 | public int LogLevel { get; set; } = 0; 67 | 68 | /// 69 | /// NPC data won't be sent to a player if their distance is greater than this value. -1 for unlimited. 70 | /// 71 | public float NpcStreamingDistance { get; set; } = 500; 72 | 73 | /// 74 | /// Player's data won't be sent to another player if their distance is greater than this value. -1 for unlimited. 75 | /// 76 | public float PlayerStreamingDistance { get; set; } = -1; 77 | 78 | /// 79 | /// If enabled, all clients will have same weather and time as host 80 | /// 81 | public bool WeatherTimeSync { get; set; } = true; 82 | 83 | /// 84 | /// List of all allowed username characters 85 | /// 86 | public string AllowedUsernameChars { get; set; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_"; 87 | 88 | /// 89 | /// Whether to use direct connection between players to send entity information, needs to be enabled if on WAN for this feature to function properly. 90 | /// 91 | public bool UseP2P { get; set; } = false; 92 | 93 | /// 94 | /// Whether to enable zerotier VLAN functionality, allowing you to host a server behind NAT firewall, no port forward required. 95 | /// 96 | public bool UseZeroTier { get; set; } = false; 97 | 98 | /// 99 | /// Use in-game voice chat to communicate with other players 100 | /// 101 | public bool UseVoice { get; set; } = false; 102 | 103 | /// 104 | /// The zerotier network id to join, default value is zerotier's public Earth network. 105 | /// 106 | public string ZeroTierNetworkID { get; set; } = "8056c2e21c000001"; 107 | 108 | /// 109 | /// Automatically update to nightly build when an update is avalible, check is performed every 10 minutes. 110 | /// 111 | public bool AutoUpdate { get; set; } = false; 112 | 113 | /// 114 | /// Kick godmode assholes 115 | /// 116 | public bool KickGodMode { get; set; } = false; 117 | 118 | /// 119 | /// Kick spamming assholes 120 | /// 121 | public bool KickSpamming { get; set; } = true; 122 | 123 | /// 124 | /// Player that spawned entities more than this amount will be kicked if is enabled. 125 | /// 126 | public int SpamLimit { get; set; } = 100; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /RageCoop.Server/Util.cs: -------------------------------------------------------------------------------- 1 | global using System.Collections.Generic; 2 | using Lidgren.Network; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Xml; 9 | using System.Xml.Serialization; 10 | namespace RageCoop.Server 11 | { 12 | internal static partial class Util 13 | { 14 | 15 | public static string DownloadString(string url) 16 | { 17 | try 18 | { 19 | // TLS only 20 | ServicePointManager.Expect100Continue = true; 21 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12; 22 | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; 23 | 24 | HttpClient client = new(); 25 | HttpRequestMessage request = new(HttpMethod.Get, url); 26 | HttpResponseMessage response = client.Send(request); 27 | using var reader = new StreamReader(response.Content.ReadAsStream()); 28 | string responseBody = reader.ReadToEnd(); 29 | 30 | return responseBody; 31 | } 32 | catch 33 | { 34 | return ""; 35 | } 36 | } 37 | public static List Exclude(this IEnumerable connections, NetConnection toExclude) 38 | { 39 | return new(connections.Where(e => e != toExclude)); 40 | } 41 | 42 | public static T Read(string file) where T : new() 43 | { 44 | XmlSerializer ser = new(typeof(T)); 45 | 46 | XmlWriterSettings settings = new() 47 | { 48 | Indent = true, 49 | IndentChars = ("\t"), 50 | OmitXmlDeclaration = true 51 | }; 52 | 53 | string path = AppContext.BaseDirectory + file; 54 | T data; 55 | 56 | if (File.Exists(path)) 57 | { 58 | try 59 | { 60 | using (XmlReader stream = XmlReader.Create(path)) 61 | { 62 | data = (T)ser.Deserialize(stream); 63 | } 64 | 65 | using (XmlWriter stream = XmlWriter.Create(path, settings)) 66 | { 67 | ser.Serialize(stream, data); 68 | } 69 | } 70 | catch 71 | { 72 | using (XmlWriter stream = XmlWriter.Create(path, settings)) 73 | { 74 | ser.Serialize(stream, data = new T()); 75 | } 76 | } 77 | } 78 | else 79 | { 80 | using (XmlWriter stream = XmlWriter.Create(path, settings)) 81 | { 82 | ser.Serialize(stream, data = new T()); 83 | } 84 | } 85 | 86 | return data; 87 | } 88 | 89 | public static T Next(this T[] values) 90 | { 91 | return values[new Random().Next(values.Length - 1)]; 92 | } 93 | 94 | public static string GetFinalRedirect(string url) 95 | { 96 | if (string.IsNullOrWhiteSpace(url)) 97 | return url; 98 | 99 | int maxRedirCount = 8; // prevent infinite loops 100 | string newUrl = url; 101 | do 102 | { 103 | try 104 | { 105 | HttpClientHandler handler = new() 106 | { 107 | AllowAutoRedirect = false 108 | }; 109 | HttpClient client = new(handler); 110 | HttpRequestMessage request = new(HttpMethod.Head, url); 111 | HttpResponseMessage response = client.Send(request); 112 | 113 | switch (response.StatusCode) 114 | { 115 | case HttpStatusCode.OK: 116 | return newUrl; 117 | case HttpStatusCode.Redirect: 118 | case HttpStatusCode.MovedPermanently: 119 | case HttpStatusCode.RedirectKeepVerb: 120 | case HttpStatusCode.RedirectMethod: 121 | newUrl = response.Headers.Location.ToString(); 122 | if (newUrl == null) 123 | return url; 124 | 125 | string newUrlString = newUrl; 126 | 127 | if (!newUrlString.Contains("://")) 128 | { 129 | // Doesn't have a URL Schema, meaning it's a relative or absolute URL 130 | Uri u = new Uri(new Uri(url), newUrl); 131 | newUrl = u.ToString(); 132 | } 133 | break; 134 | default: 135 | return newUrl; 136 | } 137 | 138 | url = newUrl; 139 | } 140 | catch (WebException) 141 | { 142 | // Return the last known good URL 143 | return newUrl; 144 | } 145 | catch 146 | { 147 | return null; 148 | } 149 | } while (maxRedirCount-- > 0); 150 | 151 | return newUrl; 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /RageCoop.Server/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/RageCoop.Server/icon.ico -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | ragecoop-v: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | ports: 9 | - "4499:4499/udp" 10 | stdin_open: true 11 | tty: true 12 | volumes: 13 | - ./Settings.xml:/app/Settings.xml 14 | -------------------------------------------------------------------------------- /images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/images/icon.ico -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/images/icon.png -------------------------------------------------------------------------------- /libs/ClearScript.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/ClearScript.Core.dll -------------------------------------------------------------------------------- /libs/ClearScript.V8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/ClearScript.V8.dll -------------------------------------------------------------------------------- /libs/ClearScriptV8.win-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/ClearScriptV8.win-x64.dll -------------------------------------------------------------------------------- /libs/LemonUI.SHVDN3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/LemonUI.SHVDN3.dll -------------------------------------------------------------------------------- /libs/Lidgren.Network.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETStandard,Version=v2.0/", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETStandard,Version=v2.0": {}, 9 | ".NETStandard,Version=v2.0/": { 10 | "Lidgren.Network/1.0.0": { 11 | "dependencies": { 12 | "Microsoft.CSharp": "4.7.0", 13 | "Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers": "0.4.336902", 14 | "NETStandard.Library": "2.0.3" 15 | }, 16 | "runtime": { 17 | "Lidgren.Network.dll": {} 18 | } 19 | }, 20 | "Microsoft.CSharp/4.7.0": { 21 | "runtime": { 22 | "lib/netstandard2.0/Microsoft.CSharp.dll": { 23 | "assemblyVersion": "4.0.5.0", 24 | "fileVersion": "4.700.19.56404" 25 | } 26 | } 27 | }, 28 | "Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/0.4.336902": {}, 29 | "Microsoft.NETCore.Platforms/1.1.0": {}, 30 | "NETStandard.Library/2.0.3": { 31 | "dependencies": { 32 | "Microsoft.NETCore.Platforms": "1.1.0" 33 | } 34 | } 35 | } 36 | }, 37 | "libraries": { 38 | "Lidgren.Network/1.0.0": { 39 | "type": "project", 40 | "serviceable": false, 41 | "sha512": "" 42 | }, 43 | "Microsoft.CSharp/4.7.0": { 44 | "type": "package", 45 | "serviceable": true, 46 | "sha512": "sha512-pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA==", 47 | "path": "microsoft.csharp/4.7.0", 48 | "hashPath": "microsoft.csharp.4.7.0.nupkg.sha512" 49 | }, 50 | "Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers/0.4.336902": { 51 | "type": "package", 52 | "serviceable": true, 53 | "sha512": "sha512-urQDGvwM6vNBClOu8dChtE7fELONWafH5rQOwO2A9YmBgEcbM8qfeaUqALLM4DKnxIu3J2Vm0GkBsApTSwWDog==", 54 | "path": "microsoft.dotnet.upgradeassistant.extensions.default.analyzers/0.4.336902", 55 | "hashPath": "microsoft.dotnet.upgradeassistant.extensions.default.analyzers.0.4.336902.nupkg.sha512" 56 | }, 57 | "Microsoft.NETCore.Platforms/1.1.0": { 58 | "type": "package", 59 | "serviceable": true, 60 | "sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==", 61 | "path": "microsoft.netcore.platforms/1.1.0", 62 | "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" 63 | }, 64 | "NETStandard.Library/2.0.3": { 65 | "type": "package", 66 | "serviceable": true, 67 | "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", 68 | "path": "netstandard.library/2.0.3", 69 | "hashPath": "netstandard.library.2.0.3.nupkg.sha512" 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /libs/Lidgren.Network.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/Lidgren.Network.dll -------------------------------------------------------------------------------- /libs/McMaster.NETCore.Plugins.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/McMaster.NETCore.Plugins.dll -------------------------------------------------------------------------------- /libs/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /libs/ScriptHookVDotNet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/ScriptHookVDotNet.dll -------------------------------------------------------------------------------- /libs/ScriptHookVDotNet3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/fcd7e18d9b14c7cda95783e5a7ade4b4a20f97d2/libs/ScriptHookVDotNet3.dll --------------------------------------------------------------------------------