mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-20 01:46:11 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1ffdb95e1 | ||
|
|
4d11cf91ad | ||
|
|
84aba52198 | ||
|
|
6e4bb71289 | ||
|
|
8fec1a211e | ||
|
|
85a2fce6c1 | ||
|
|
224fdd1d5b | ||
|
|
380ba46f11 | ||
|
|
aeefc40ef1 | ||
|
|
743b8ed5cb | ||
|
|
ed008872f6 | ||
|
|
4797d899af | ||
|
|
c834013bd3 | ||
|
|
70c180fb19 | ||
|
|
729368d777 | ||
|
|
5c06d1dd4d | ||
|
|
446e5b8705 | ||
|
|
ae00ff9295 | ||
|
|
400592b99c | ||
|
|
9906c409da | ||
|
|
c26d03a8bc | ||
|
|
ca48d672d5 | ||
|
|
0b431dc5fa | ||
|
|
0b0c8274aa | ||
|
|
36f8255e26 | ||
|
|
919b094018 | ||
|
|
6125fd7006 | ||
|
|
a3c0c8cacf | ||
|
|
b252bf2ee7 | ||
|
|
9b5a63e030 | ||
|
|
6a8f089576 | ||
|
|
d994323083 | ||
|
|
3f374da44b | ||
|
|
bfd2efa716 | ||
|
|
6867dec7ee | ||
|
|
7132dc2d38 | ||
|
|
682bfac558 | ||
|
|
cc8d5d2a45 | ||
|
|
e6a35344c3 | ||
|
|
db52b48cd6 | ||
|
|
50a526a02d | ||
|
|
608414326d | ||
|
|
d8bcb52c90 | ||
|
|
d7d7547182 | ||
|
|
64e27c3e21 | ||
|
|
a0b433500c | ||
|
|
c51a1f9c1f | ||
|
|
e15ac51dcf | ||
|
|
bde3a22f6e | ||
|
|
6118e6b7b8 | ||
|
|
32ecfedfba | ||
|
|
b6fe79cc8b | ||
|
|
aeaffc849a | ||
|
|
a15f1b6a4c | ||
|
|
afb7051218 | ||
|
|
8a0e9e876e | ||
|
|
22f1d74cdf | ||
|
|
e6d38f4b72 | ||
|
|
93c80c1412 | ||
|
|
c3e0157209 | ||
|
|
0769752407 | ||
|
|
fba1a894cc | ||
|
|
4d1aec9070 | ||
|
|
e0c7086973 | ||
|
|
125da11d61 |
No files matched your search
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"cake.tool": {
|
||||
"version": "1.1.0",
|
||||
"version": "2.2.0",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
]
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:5.0 AS build
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
|
||||
# See for all possible platforms
|
||||
# https://github.com/containerd/containerd/blob/master/platforms/platforms.go#L17
|
||||
@@ -12,6 +12,7 @@ WORKDIR /source
|
||||
COPY src/Impostor.Server/Impostor.Server.csproj ./src/Impostor.Server/Impostor.Server.csproj
|
||||
COPY src/Impostor.Api/Impostor.Api.csproj ./src/Impostor.Api/Impostor.Api.csproj
|
||||
COPY src/Impostor.Hazel/Hazel/Hazel.csproj ./src/Impostor.Hazel/Hazel/Hazel.csproj
|
||||
COPY src/Directory.Build.props ./src/Directory.Build.props
|
||||
|
||||
RUN case "$TARGETARCH" in \
|
||||
amd64) NETCORE_PLATFORM='linux-x64';; \
|
||||
@@ -35,8 +36,8 @@ RUN case "$TARGETARCH" in \
|
||||
dotnet publish -c release -o /app -r "$NETCORE_PLATFORM" -p:VersionSuffix="$VERSIONSUFFIX" --no-restore ./src/Impostor.Server/Impostor.Server.csproj
|
||||
|
||||
# Final image.
|
||||
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime:5.0
|
||||
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime:6.0
|
||||
WORKDIR /app
|
||||
COPY --from=build /app ./
|
||||
EXPOSE 22023/udp
|
||||
ENTRYPOINT ["./Impostor.Server"]
|
||||
ENTRYPOINT ["./Impostor.Server"]
|
||||
+22
-10
@@ -20,11 +20,12 @@ image: Ubuntu2004
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
- ps: dotnet tool restore
|
||||
- dotnet tool restore
|
||||
|
||||
build_script:
|
||||
- ps: dotnet cake build.cake --bootstrap
|
||||
- ps: dotnet cake build.cake --pack
|
||||
- pwsh: if ($env:APPVEYOR_REPO_TAG_NAME) { Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_TAG_NAME -replace "v")" }
|
||||
- dotnet cake build.cake --bootstrap
|
||||
- dotnet cake build.cake --pack
|
||||
|
||||
test: off
|
||||
|
||||
@@ -34,13 +35,24 @@ artifacts:
|
||||
- path: ./build/*.nupkg
|
||||
- path: ./build/*.snupkg
|
||||
|
||||
# deploy:
|
||||
# - provider: NuGet
|
||||
# artifact: /.*(\.|\.s)nupkg/
|
||||
# on:
|
||||
# branch: dev
|
||||
# api_key:
|
||||
# secure: 4OHXl+m1KVi60hB1j54MpdP8tVv0UNfkdF4rVP+2AhAjN4a2MVT+Bl90gJZ96xHF
|
||||
deploy:
|
||||
# - provider: NuGet
|
||||
# artifact: /.*(\.|\.s)nupkg/
|
||||
# on:
|
||||
# branch: master
|
||||
# api_key:
|
||||
# secure: 4OHXl+m1KVi60hB1j54MpdP8tVv0UNfkdF4rVP+2AhAjN4a2MVT+Bl90gJZ96xHF
|
||||
|
||||
- provider: GitHub
|
||||
tag: $(APPVEYOR_REPO_TAG_NAME)
|
||||
release: $(APPVEYOR_PROJECT_NAME) $(APPVEYOR_BUILD_VERSION)
|
||||
description: 'DRAFT'
|
||||
auth_token:
|
||||
secure: vyQ+zijv9g99wEgIdSC8uPSlnFvH3KZiD0yE7mNA3r3z2OY4lTEF6bpuUvtjoaM3
|
||||
artifact: /.*/
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
||||
only_commits:
|
||||
files:
|
||||
|
||||
+13
-15
@@ -1,6 +1,6 @@
|
||||
#addin "nuget:?package=SharpZipLib&Version=1.3.1"
|
||||
#addin "nuget:?package=Cake.Compression&Version=0.2.6"
|
||||
#addin "nuget:?package=Cake.FileHelpers&Version=4.0.1"
|
||||
#addin "nuget:?package=SharpZipLib&Version=1.3.3"
|
||||
#addin "nuget:?package=Cake.Compression&Version=0.3.0"
|
||||
#addin "nuget:?package=Cake.FileHelpers&Version=5.0.0"
|
||||
|
||||
var buildId = EnvironmentVariable("GITHUB_RUN_NUMBER") ?? EnvironmentVariable("APPVEYOR_BUILD_VERSION");
|
||||
var buildRelease = EnvironmentVariable("APPVEYOR_REPO_TAG") == "true";
|
||||
@@ -10,7 +10,7 @@ var buildDir = MakeAbsolute(Directory("./build"));
|
||||
var target = Argument("target", "Deploy");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
|
||||
var msbuildSettings = new DotNetCoreMSBuildSettings();
|
||||
var msbuildSettings = new DotNetMSBuildSettings();
|
||||
|
||||
if (buildRelease)
|
||||
{
|
||||
@@ -31,10 +31,9 @@ private void ImpostorPublish(string name, string project, string runtime, bool i
|
||||
var projBuildDir = buildDir.Combine(name + "_" + runtime);
|
||||
var projBuildName = name + "_" + buildVersion + "_" + runtime;
|
||||
|
||||
DotNetCorePublish(project, new DotNetCorePublishSettings {
|
||||
DotNetPublish(project, new DotNetPublishSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
Framework = "net5.0",
|
||||
Runtime = runtime,
|
||||
SelfContained = false,
|
||||
PublishSingleFile = true,
|
||||
@@ -64,7 +63,7 @@ private void ImpostorPublishNF(string name, string project) {
|
||||
var projBuildDir = buildDir.Combine(name + "_" + runtime);
|
||||
var projBuildZip = buildDir.CombineWithFilePath(name + "_" + buildVersion + "_" + runtime + ".zip");
|
||||
|
||||
DotNetCorePublish(project, new DotNetCorePublishSettings {
|
||||
DotNetPublish(project, new DotNetPublishSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
Framework = "net472",
|
||||
@@ -90,18 +89,17 @@ Task("Clean")
|
||||
|
||||
Task("Restore")
|
||||
.Does(() => {
|
||||
DotNetCoreRestore("./src/Impostor.sln");
|
||||
DotNetRestore("./src/Impostor.sln");
|
||||
});
|
||||
|
||||
Task("Replay")
|
||||
.Does(() => {
|
||||
// D:\Projects\GitHub\Impostor\Impostor\src\Impostor.Tools.ServerReplay\sessions
|
||||
DotNetCoreRun(
|
||||
DotNetRun(
|
||||
"./src/Impostor.Tools.ServerReplay/Impostor.Tools.ServerReplay.csproj",
|
||||
"./src/Impostor.Tools.ServerReplay/sessions", new DotNetCoreRunSettings {
|
||||
"./src/Impostor.Tools.ServerReplay/sessions", new DotNetRunSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
Framework = "net5.0"
|
||||
});
|
||||
});
|
||||
|
||||
@@ -111,7 +109,7 @@ Task("Build")
|
||||
.IsDependentOn("Replay")
|
||||
.Does(() => {
|
||||
// Tests.
|
||||
DotNetCoreBuild("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetCoreBuildSettings {
|
||||
DotNetBuild("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetBuildSettings {
|
||||
Configuration = configuration,
|
||||
});
|
||||
|
||||
@@ -123,7 +121,7 @@ Task("Build")
|
||||
ImpostorPublish("Impostor-Server", "./src/Impostor.Server/Impostor.Server.csproj", "linux-arm64", true);
|
||||
|
||||
// API.
|
||||
DotNetCorePack("./src/Impostor.Api/Impostor.Api.csproj", new DotNetCorePackSettings {
|
||||
DotNetPack("./src/Impostor.Api/Impostor.Api.csproj", new DotNetPackSettings {
|
||||
Configuration = configuration,
|
||||
OutputDirectory = buildDir,
|
||||
IncludeSource = true,
|
||||
@@ -135,7 +133,7 @@ Task("Build")
|
||||
Task("Test")
|
||||
.IsDependentOn("Build")
|
||||
.Does(() => {
|
||||
DotNetCoreTest("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetCoreTestSettings {
|
||||
DotNetTest("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetTestSettings {
|
||||
Configuration = configuration,
|
||||
NoBuild = true
|
||||
});
|
||||
@@ -151,4 +149,4 @@ Task("Deploy")
|
||||
// EXECUTION
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
RunTarget(target);
|
||||
RunTarget(target);
|
||||
@@ -1,6 +1,6 @@
|
||||
# Building from source
|
||||
|
||||
The solution contains the Impostor server and its dependencies, like Hazel and the plugin API. The server is built using [.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0).
|
||||
The solution contains the Impostor server and its dependencies, like Hazel and the plugin API. The server is built using [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0).
|
||||
|
||||
## Cloning Impostor
|
||||
|
||||
@@ -14,7 +14,7 @@ git clone https://github.com/Impostor/Impostor.git
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
|
||||
- [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0)
|
||||
- [Rider](https://www.jetbrains.com/rider/) or [Visual Studio](https://visualstudio.microsoft.com/vs/) (Optional, only if you want the full IDE experience)
|
||||
|
||||
### Build using the CLI
|
||||
|
||||
@@ -6,11 +6,11 @@ There are currently two modes to run the Impostor server. The first way, Single
|
||||
|
||||
### Without Docker
|
||||
|
||||
1. Install the [.NET 5.0 runtime](https://dotnet.microsoft.com/download). Installing the SDK also works, but is not necessary unless you plan on developing Impostor or Impostor plugins. If you're asked to pick between a console, desktop or server runtime, the console runtime is enough.
|
||||
1. Install the [.NET 6.0 runtime](https://dotnet.microsoft.com/download/6.0). Installing the SDK also works, but is not necessary unless you plan on developing Impostor or Impostor plugins. If you're asked to pick between a console, desktop or server runtime, the console runtime is enough.
|
||||
2. Find the [latest release](https://github.com/Impostor/Impostor/releases) or the [latest CI build](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts).
|
||||
3. Download the version for your OS (linux/win/osx). Impostor is built for multiple CPU-architectures, you most likely want the x64 version, unless you are running on a Raspberry Pi or another device with an ARM processor.
|
||||
4. Extract the zip.
|
||||
5. Modify `config.json` to your liking. Documentation can be found [here](Server-configuration.md) *(this step is mandatory if you want to expose this server to other devices)*
|
||||
5. Modify `config.json` to your liking. Documentation can be found [here](Server-configuration.md) _(this step is mandatory if you want to expose this server to other devices)_
|
||||
6. Run `Impostor.Server` (Linux/macOS) or `Impostor.Server.exe` (Windows)
|
||||
|
||||
### Using Docker
|
||||
|
||||
@@ -22,30 +22,6 @@ Impostor has an Anticheat that makes it possible to kick cheaters from games aut
|
||||
| **Enabled** | `true` | Whether the anticheat should be enabled. |
|
||||
| **BanIpFromGame** | `true` | When anticheat is enabled and a player is caught hacking, they will be kicked from the server. If this value is set to `true`, the player will be banned instead and will not be able to rejoin that specific game. |
|
||||
|
||||
### AnnouncementsServer
|
||||
|
||||
The Announcement Server is used to show update announcements to clients. Using the Plugin API you can create a plugin that serves custom announcements.
|
||||
|
||||
| Key | Default | Value |
|
||||
| -------------- | --------- | --------------------------------------------------- |
|
||||
| **Enabled** | `false` | Whether the announcements server should be enabled. |
|
||||
| **ListenIp** | `0.0.0.0` | See Required Server Configuration. |
|
||||
| **ListenPort** | `22025` | See Required Server Configuration. |
|
||||
|
||||
### AuthServer
|
||||
|
||||
The Authentication Server is used since Among Us version 2021.3.31 and is used by clients to identify themselves. If you're playing with modded Among Us clients, you can enable this server to skip the 5 second wait to join.
|
||||
|
||||
| Key | Default | Value |
|
||||
| --------------- | ---------------------- | ---------------------------------------------------- |
|
||||
| **Enabled** | `false` | Whether the authentication server should be enabled. |
|
||||
| **ListenIp** | `0.0.0.0` | See Required Server Configuration. |
|
||||
| **ListenPort** | `22025` | See Required Server Configuration. |
|
||||
| **Certificate** | `dtls/certificate.pem` | The public certificate used to authenticate with. |
|
||||
| **PrivateKey** | `dtls/key.pem` | The corresponding private key. |
|
||||
|
||||
On Linux you can generate a certificate by running `openssl req -x509 -newkey rsa:2048 -keyout dtls/key.pem -out dtls/certificate.pem -days 3650 -nodes`. It does not need to be signed.
|
||||
|
||||
### Debug
|
||||
|
||||
The Debug configuration is used to enable the game recorder. This is mostly useful when developing Impostor.
|
||||
@@ -53,7 +29,7 @@ The Debug configuration is used to enable the game recorder. This is mostly usef
|
||||
| Key | Default | Value |
|
||||
| ----------------------- | ------- | -------------------------------------------- |
|
||||
| **GameRecorderEnabled** | `false` | Enables the Game Recorder. |
|
||||
| **GameRecorderPath** | *empty* | Path where the recorded games will be saved. |
|
||||
| **GameRecorderPath** | _empty_ | Path where the recorded games will be saved. |
|
||||
|
||||
### ServerRedirector
|
||||
|
||||
@@ -68,6 +44,41 @@ In a multi-node setup these values need to be specified. Note that most people d
|
||||
| **>UdpMasterEndpoint** | | On the master, this value acts as a listen ip and port. On a node, this should be the public ip and port of the master. Format `127.0.0.1:32320`. |
|
||||
| **Nodes** | | An array containing public ips and ports of nodes. Only needs to be set on the master. See above for an example. |
|
||||
|
||||
### Serilog (Logging)
|
||||
|
||||
Impostor's log framework, Serilog, can be configured in the config file. You can change its default log level and you can add additional sinks.
|
||||
|
||||
| Key | Default | Value |
|
||||
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **MinimumLevel** | `Information` | Minimum log level for a message to be printed. If a log entry is as severe or more severe than this level, it will be printed. From most severe to least severe: `Fatal`,`Error`, `Warning`, `Information`, `Debug`, `Verbose` |
|
||||
| **Using** | `[]` | List of additional Serilog Sinks assemblies to load. |
|
||||
| **WriteTo** | `[]` | Additional logging sinks. See the Serilog documentation or the example in this section. Serilog |
|
||||
|
||||
For more information, check the [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) documentation.
|
||||
|
||||
For example, to add logging to a file, you should add the following snippet to your configuration:
|
||||
|
||||
```json
|
||||
"Serilog": {
|
||||
"Using": [
|
||||
"Serilog.Sinks.File"
|
||||
],
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": {
|
||||
"path": "logs/log.txt",
|
||||
"rollingInterval": "Day"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Next to that, you also need to copy over Serilog.Sinks.File.dll from [NuGet](https://www.nuget.org/packages/Serilog.Sinks.File/). See the [Serilog.Sinks.File documentation](https://github.com/serilog/serilog-sinks-file#json-appsettingsjson-configuration) for a list of parameters that can be configured.
|
||||
|
||||
Other Serilog sinks are also supported, but are out of scope for this documentation.
|
||||
|
||||
## Config providers
|
||||
|
||||
### File
|
||||
|
||||
@@ -19,23 +19,24 @@ Impostor has support for plugins. This document will help you to setup a develop
|
||||
- [Impostor versions](#impostor-versions)
|
||||
- [9. Missing/invalid data or want more functions?](#9-missinginvalid-data-or-want-more-functions)
|
||||
|
||||
## 1. Install .NET Core SDK
|
||||
## 1. Install .NET SDK
|
||||
|
||||
Download and install the latest .NET Core SDK.
|
||||
Download and install the latest .NET SDK.
|
||||
|
||||
https://dotnet.microsoft.com/download
|
||||
|
||||
## 2. Create a C# project
|
||||
|
||||
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 5, which includes:
|
||||
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 6, which includes:
|
||||
|
||||
- .NET Standard 2.0
|
||||
- .NET Standard 2.1
|
||||
- .NET 5
|
||||
- .NET 6
|
||||
|
||||
For more information about compatibility, see https://docs.microsoft.com/en-us/dotnet/standard/net-standard.
|
||||
|
||||
> At the moment of writing this document, I recommend you to use **.NET Standard 2.1**. This should give you enough functionality. If not, upgrade to .NET 5.
|
||||
> At the moment of writing this document, I recommend you to use **.NET Standard 2.1**. This should give you enough functionality. If not, upgrade to .NET 6.
|
||||
|
||||
When the project has been created, you should have `Class.cs` and `Project.csproj` files. Your `Project.csproj` should look something like this.
|
||||
|
||||
@@ -336,4 +337,4 @@ Create an issue:
|
||||
- [Suggest a function](https://github.com/Impostor/Impostor/issues/new?template=3--api-suggestion.md)
|
||||
- [Data is invalid](https://github.com/Impostor/Impostor/issues/new?template=4--api-invalid.md)
|
||||
- [Data is unavailable](https://github.com/Impostor/Impostor/issues/new?template=5--api-missing.md)
|
||||
- [Other](https://github.com/Impostor/Impostor/issues/new?template=6--api-other.md)
|
||||
- [Other](https://github.com/Impostor/Impostor/issues/new?template=6--api-other.md)
|
||||
@@ -1,6 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.6.0</VersionPrefix>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<VersionPrefix>1.7.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Events.Announcements
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired after client requests a announcement.
|
||||
/// </summary>
|
||||
public interface IAnnouncementRequestEvent : IEvent
|
||||
{
|
||||
public interface IResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether announcement should be loaded from client's cache, can save some bytes.
|
||||
/// </summary>
|
||||
public bool UseCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets announcement, should be null when <see cref="UseCached" /> is set to true.
|
||||
/// </summary>
|
||||
public Announcement? Announcement { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets client's last announcement id.
|
||||
/// </summary>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets client's language.
|
||||
/// </summary>
|
||||
public Language Language { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets plugin made response.
|
||||
/// </summary>
|
||||
public IResponse Response { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,13 @@ namespace Impostor.Api
|
||||
return original[0];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static short ReadInt16(this ref ReadOnlySpan<byte> input)
|
||||
{
|
||||
var original = Advance<short>(ref input);
|
||||
return BinaryPrimitives.ReadInt16LittleEndian(original);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ReadInt32(this ref ReadOnlySpan<byte> input)
|
||||
{
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Nullable" Version="1.3.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public readonly struct Announcement
|
||||
{
|
||||
public readonly int Id;
|
||||
public readonly string Message;
|
||||
|
||||
public Announcement(int id, string message)
|
||||
{
|
||||
Id = id;
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public enum HatType : uint
|
||||
{
|
||||
NoHat = 0,
|
||||
Astronaut = 1,
|
||||
BaseballCap = 2,
|
||||
BrainSlug = 3,
|
||||
BushHat = 4,
|
||||
CaptainsHat = 5,
|
||||
DoubleTopHat = 6,
|
||||
Flowerpot = 7,
|
||||
Goggles = 8,
|
||||
HardHat = 9,
|
||||
Military = 10,
|
||||
PaperHat = 11,
|
||||
PartyHat = 12,
|
||||
Police = 13,
|
||||
Stethescope = 14,
|
||||
TopHat = 15,
|
||||
TowelWizard = 16,
|
||||
Ushanka = 17,
|
||||
Viking = 18,
|
||||
WallCap = 19,
|
||||
Snowman = 20,
|
||||
Reindeer = 21,
|
||||
Lights = 22,
|
||||
Santa = 23,
|
||||
Tree = 24,
|
||||
Present = 25,
|
||||
Candycanes = 26,
|
||||
ElfHat = 27,
|
||||
NewYears2018 = 28,
|
||||
WhiteHat = 29,
|
||||
Crown = 30,
|
||||
Eyebrows = 31,
|
||||
HaloHat = 32,
|
||||
HeroCap = 33,
|
||||
PipCap = 34,
|
||||
PlungerHat = 35,
|
||||
ScubaHat = 36,
|
||||
StickminHat = 37,
|
||||
StrawHat = 38,
|
||||
TenGallonHat = 39,
|
||||
ThirdEyeHat = 40,
|
||||
ToiletPaperHat = 41,
|
||||
Toppat = 42,
|
||||
BlackFedora = 43,
|
||||
SkiGoggles = 44,
|
||||
Headphones = 45,
|
||||
MaskHat = 46,
|
||||
PaperMask = 47,
|
||||
Security = 48,
|
||||
StrapHat = 49,
|
||||
Banana = 50,
|
||||
Beanie = 51,
|
||||
Bear = 52,
|
||||
Cheese = 53,
|
||||
Cherry = 54,
|
||||
Egg = 55,
|
||||
GreenFedora = 56,
|
||||
Flamingo = 57,
|
||||
FlowerPin = 58,
|
||||
Helmet = 59,
|
||||
Plant = 60,
|
||||
BatEyes = 61,
|
||||
BatWings = 62,
|
||||
Horns = 63,
|
||||
Mohawk = 64,
|
||||
Pumpkin = 65,
|
||||
ScaryBag = 66,
|
||||
Witch = 67,
|
||||
Wolf = 68,
|
||||
Pirate = 69,
|
||||
Plague = 70,
|
||||
Machete = 71,
|
||||
Fred = 72,
|
||||
MinerCap = 73,
|
||||
WinterHat = 74,
|
||||
Archae = 75,
|
||||
Antenna = 76,
|
||||
Balloon = 77,
|
||||
BirdNest = 78,
|
||||
BlackBelt = 79,
|
||||
Caution = 80,
|
||||
Chef = 81,
|
||||
CopHat = 82,
|
||||
DoRag = 83,
|
||||
DumSticker = 84,
|
||||
Fez = 85,
|
||||
GeneralHat = 86,
|
||||
GreyThing = 87,
|
||||
HunterCap = 88,
|
||||
JungleHat = 89,
|
||||
MiniCrewmate = 90,
|
||||
NinjaMask = 91,
|
||||
RamHorns = 92,
|
||||
SnowCrewmate = 93,
|
||||
GeoffHat = 94,
|
||||
DaveCap = 95,
|
||||
EllieRose = 96,
|
||||
SvenHat = 97,
|
||||
BurtHat = 98,
|
||||
EllryMohawk = 99,
|
||||
ThomasMonoc = 100,
|
||||
Wizard = 101,
|
||||
Fredrick = 102,
|
||||
MrMacbeth = 103,
|
||||
ToppatHenry = 104,
|
||||
ToppatEllie = 105,
|
||||
GeoffreyPlumb = 106,
|
||||
RightHandMan = 107,
|
||||
AngryEyebrows = 108,
|
||||
ChocolateIce = 109,
|
||||
HeartPin = 110,
|
||||
Ponytail = 111,
|
||||
RubberGlove = 112,
|
||||
UnicornHorn = 113,
|
||||
Zipper = 114,
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public enum PetType : uint
|
||||
{
|
||||
NoPet = 0,
|
||||
Alien = 1,
|
||||
Crewmate = 2,
|
||||
Doggy = 3,
|
||||
Stickmin = 4,
|
||||
Hamster = 5,
|
||||
Robot = 6,
|
||||
Ufo = 7,
|
||||
Ellie = 8,
|
||||
Squig = 9,
|
||||
Bedcrab = 10,
|
||||
Glitch = 11,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Impostor.Api.Net.Messages;
|
||||
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public class PlayerOutfit
|
||||
{
|
||||
public ColorType Color { get; internal set; } = (ColorType)(-1);
|
||||
|
||||
public string HatId { get; internal set; } = "missing";
|
||||
|
||||
public string PetId { get; internal set; } = "missing";
|
||||
|
||||
public string SkinId { get; internal set; } = "missing";
|
||||
|
||||
public string VisorId { get; internal set; } = "missing";
|
||||
|
||||
public string NamePlateId { get; internal set; } = "missing";
|
||||
|
||||
public string PlayerName { get; internal set; } = "missing";
|
||||
|
||||
public bool IsIncomplete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
|
||||
{
|
||||
return NamePlateId == "missing";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.Write(PlayerName);
|
||||
writer.WritePacked((int)Color);
|
||||
writer.Write(HatId);
|
||||
writer.Write(PetId);
|
||||
writer.Write(SkinId);
|
||||
writer.Write(VisorId);
|
||||
writer.Write(NamePlateId);
|
||||
}
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
PlayerName = reader.ReadString();
|
||||
Color = (ColorType)reader.ReadPackedInt32();
|
||||
HatId = reader.ReadString();
|
||||
PetId = reader.ReadString();
|
||||
SkinId = reader.ReadString();
|
||||
VisorId = reader.ReadString();
|
||||
NamePlateId = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public enum PlayerOutfitType
|
||||
{
|
||||
Default,
|
||||
Shapeshifted,
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public enum SkinType : uint
|
||||
{
|
||||
None = 0,
|
||||
Astro = 1,
|
||||
Capt = 2,
|
||||
Mech = 3,
|
||||
Military = 4,
|
||||
Police = 5,
|
||||
Science = 6,
|
||||
SuitB = 7,
|
||||
SuitW = 8,
|
||||
Wall = 9,
|
||||
Hazmat = 10,
|
||||
Security = 11,
|
||||
Tarmac = 12,
|
||||
Miner = 13,
|
||||
Winter = 14,
|
||||
Archae = 15,
|
||||
Prisoner = 16,
|
||||
CCC = 17,
|
||||
RightHandMan = 18,
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Impostor.Api.Innersloth
|
||||
/// <summary>
|
||||
/// The latest major version of the game client.
|
||||
/// </summary>
|
||||
public const int LatestVersion = 4;
|
||||
public const int LatestVersion = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets host's version of the game.
|
||||
@@ -124,6 +124,11 @@ namespace Impostor.Api.Innersloth
|
||||
/// </summary>
|
||||
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets role options.
|
||||
/// </summary>
|
||||
public RoleOptionsData RoleOptions { get; set; } = new RoleOptionsData();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the GameOptions are the default ones.
|
||||
/// </summary>
|
||||
@@ -166,24 +171,34 @@ namespace Impostor.Api.Innersloth
|
||||
writer.Write((uint)VotingTime);
|
||||
writer.Write((bool)IsDefaults);
|
||||
|
||||
if (version > 1)
|
||||
if (version >= 2)
|
||||
{
|
||||
writer.Write((byte)EmergencyCooldown);
|
||||
}
|
||||
|
||||
if (version > 2)
|
||||
if (version >= 3)
|
||||
{
|
||||
writer.Write((bool)ConfirmImpostor);
|
||||
writer.Write((bool)VisualTasks);
|
||||
}
|
||||
|
||||
if (version > 3)
|
||||
if (version >= 4)
|
||||
{
|
||||
writer.Write((bool)AnonymousVotes);
|
||||
writer.Write((byte)TaskBarUpdate);
|
||||
}
|
||||
|
||||
if (version > 4)
|
||||
if (version >= 5)
|
||||
{
|
||||
RoleOptions.Serialize(writer);
|
||||
}
|
||||
|
||||
if (version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (version > 6)
|
||||
{
|
||||
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
|
||||
}
|
||||
@@ -228,24 +243,34 @@ namespace Impostor.Api.Innersloth
|
||||
|
||||
IsDefaults = bytes.ReadBoolean();
|
||||
|
||||
if (Version > 1)
|
||||
if (Version >= 2)
|
||||
{
|
||||
EmergencyCooldown = bytes.ReadByte();
|
||||
}
|
||||
|
||||
if (Version > 2)
|
||||
if (Version >= 3)
|
||||
{
|
||||
ConfirmImpostor = bytes.ReadBoolean();
|
||||
VisualTasks = bytes.ReadBoolean();
|
||||
}
|
||||
|
||||
if (Version > 3)
|
||||
if (Version >= 4)
|
||||
{
|
||||
AnonymousVotes = bytes.ReadBoolean();
|
||||
TaskBarUpdate = (TaskBarUpdate)bytes.ReadByte();
|
||||
}
|
||||
|
||||
if (Version > 4)
|
||||
if (Version >= 5)
|
||||
{
|
||||
RoleOptions = RoleOptionsData.Deserialize(bytes);
|
||||
}
|
||||
|
||||
if (Version >= 6)
|
||||
{
|
||||
// Nothing was changed in V6
|
||||
}
|
||||
|
||||
if (Version > 6)
|
||||
{
|
||||
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Impostor.Api.Net.Messages;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class PlatformSpecificData
|
||||
{
|
||||
public PlatformSpecificData(Platforms platform, string platformName, ulong? xboxPlatformId = null, ulong? psnPlatformId = null)
|
||||
{
|
||||
Platform = platform;
|
||||
PlatformName = platformName;
|
||||
XboxPlatformId = xboxPlatformId;
|
||||
PsnPlatformId = psnPlatformId;
|
||||
}
|
||||
|
||||
public PlatformSpecificData(IMessageReader reader)
|
||||
{
|
||||
Platform = (Platforms)reader.Tag;
|
||||
PlatformName = reader.ReadString();
|
||||
|
||||
XboxPlatformId = Platform == Platforms.Xbox ? reader.ReadUInt64() : null;
|
||||
PsnPlatformId = Platform == Platforms.Playstation ? reader.ReadUInt64() : null;
|
||||
}
|
||||
|
||||
public Platforms Platform { get; }
|
||||
|
||||
public string PlatformName { get; }
|
||||
|
||||
public ulong? XboxPlatformId { get; }
|
||||
|
||||
public ulong? PsnPlatformId { get; }
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.StartMessage((byte)Platform);
|
||||
writer.Write(PlatformName);
|
||||
switch (Platform)
|
||||
{
|
||||
case Platforms.Xbox:
|
||||
writer.Write(XboxPlatformId ?? throw new NullReferenceException($"{nameof(XboxPlatformId)} shouldn't be null when {nameof(Platform)} is Xbox"));
|
||||
break;
|
||||
case Platforms.Playstation:
|
||||
writer.Write(PsnPlatformId ?? throw new NullReferenceException($"{nameof(PsnPlatformId)} shouldn't be null when {nameof(Platform)} is Playstation"));
|
||||
break;
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,7 @@ namespace Impostor.Api.Innersloth
|
||||
IPhone = 6,
|
||||
Android = 7,
|
||||
Switch = 8,
|
||||
Xbox = 9,
|
||||
Playstation = 10,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Impostor.Api.Net.Messages;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class RoleOptionsData
|
||||
{
|
||||
public bool ShapeshifterLeaveSkin { get; set; }
|
||||
|
||||
public byte ShapeshifterCooldown { get; set; } = 10;
|
||||
|
||||
public byte ShapeshifterDuration { get; set; } = 30;
|
||||
|
||||
public byte ScientistCooldown { get; set; } = 15;
|
||||
|
||||
public byte ScientistBatteryCharge { get; set; } = 5;
|
||||
|
||||
public byte GuardianAngelCooldown { get; set; } = 60;
|
||||
|
||||
public bool ImpostorsCanSeeProtect { get; set; }
|
||||
|
||||
public byte ProtectionDurationSeconds { get; set; } = 10;
|
||||
|
||||
public byte EngineerCooldown { get; set; } = 30;
|
||||
|
||||
public byte EngineerInVentMaxTime { get; set; } = 15;
|
||||
|
||||
public Dictionary<RoleTypes, RoleRate> RoleRates { get; } = new Dictionary<RoleTypes, RoleRate>();
|
||||
|
||||
public static RoleOptionsData Deserialize(ReadOnlySpan<byte> span)
|
||||
{
|
||||
var roleOptionsData = new RoleOptionsData();
|
||||
var num = span.ReadInt32();
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var key = (RoleTypes)span.ReadInt16();
|
||||
var roleRate = new RoleRate(span.ReadByte(), span.ReadByte());
|
||||
roleOptionsData.RoleRates[key] = roleRate;
|
||||
}
|
||||
|
||||
roleOptionsData.ShapeshifterLeaveSkin = span.ReadBoolean();
|
||||
roleOptionsData.ShapeshifterCooldown = span.ReadByte();
|
||||
roleOptionsData.ShapeshifterDuration = span.ReadByte();
|
||||
roleOptionsData.ScientistCooldown = span.ReadByte();
|
||||
roleOptionsData.GuardianAngelCooldown = span.ReadByte();
|
||||
roleOptionsData.EngineerCooldown = span.ReadByte();
|
||||
roleOptionsData.EngineerInVentMaxTime = span.ReadByte();
|
||||
roleOptionsData.ScientistBatteryCharge = span.ReadByte();
|
||||
roleOptionsData.ProtectionDurationSeconds = span.ReadByte();
|
||||
roleOptionsData.ImpostorsCanSeeProtect = span.ReadBoolean();
|
||||
return roleOptionsData;
|
||||
}
|
||||
|
||||
public static RoleOptionsData Deserialize(IMessageReader reader)
|
||||
{
|
||||
var roleOptionsData = new RoleOptionsData();
|
||||
var num = reader.ReadPackedInt32();
|
||||
for (var i = 0; i < num; i++)
|
||||
{
|
||||
var key = (RoleTypes)reader.ReadInt16();
|
||||
var roleRate = new RoleRate(reader.ReadByte(), reader.ReadByte());
|
||||
roleOptionsData.RoleRates[key] = roleRate;
|
||||
}
|
||||
|
||||
roleOptionsData.ShapeshifterLeaveSkin = reader.ReadBoolean();
|
||||
roleOptionsData.ShapeshifterCooldown = reader.ReadByte();
|
||||
roleOptionsData.ShapeshifterDuration = reader.ReadByte();
|
||||
roleOptionsData.ScientistCooldown = reader.ReadByte();
|
||||
roleOptionsData.GuardianAngelCooldown = reader.ReadByte();
|
||||
roleOptionsData.EngineerCooldown = reader.ReadByte();
|
||||
roleOptionsData.EngineerInVentMaxTime = reader.ReadByte();
|
||||
roleOptionsData.ScientistBatteryCharge = reader.ReadByte();
|
||||
roleOptionsData.ProtectionDurationSeconds = reader.ReadByte();
|
||||
roleOptionsData.ImpostorsCanSeeProtect = reader.ReadBoolean();
|
||||
return roleOptionsData;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.WritePacked(RoleRates.Count);
|
||||
foreach (var roleRate in RoleRates)
|
||||
{
|
||||
writer.Write((ushort)roleRate.Key);
|
||||
writer.Write((byte)roleRate.Value.MaxCount);
|
||||
writer.Write((byte)roleRate.Value.Chance);
|
||||
}
|
||||
|
||||
writer.Write(ShapeshifterLeaveSkin);
|
||||
writer.Write(ShapeshifterCooldown);
|
||||
writer.Write(ShapeshifterDuration);
|
||||
writer.Write(ScientistCooldown);
|
||||
writer.Write(GuardianAngelCooldown);
|
||||
writer.Write(EngineerCooldown);
|
||||
writer.Write(EngineerInVentMaxTime);
|
||||
writer.Write(ScientistBatteryCharge);
|
||||
writer.Write(ProtectionDurationSeconds);
|
||||
writer.Write(ImpostorsCanSeeProtect);
|
||||
}
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(RoleRates.Count);
|
||||
foreach (var roleRate in RoleRates)
|
||||
{
|
||||
writer.Write((ushort)roleRate.Key);
|
||||
writer.Write((byte)roleRate.Value.MaxCount);
|
||||
writer.Write((byte)roleRate.Value.Chance);
|
||||
}
|
||||
|
||||
writer.Write(ShapeshifterLeaveSkin);
|
||||
writer.Write(ShapeshifterCooldown);
|
||||
writer.Write(ShapeshifterDuration);
|
||||
writer.Write(ScientistCooldown);
|
||||
writer.Write(GuardianAngelCooldown);
|
||||
writer.Write(EngineerCooldown);
|
||||
writer.Write(EngineerInVentMaxTime);
|
||||
writer.Write(ScientistBatteryCharge);
|
||||
writer.Write(ProtectionDurationSeconds);
|
||||
writer.Write(ImpostorsCanSeeProtect);
|
||||
}
|
||||
|
||||
public readonly struct RoleRate
|
||||
{
|
||||
public readonly int MaxCount;
|
||||
public readonly int Chance;
|
||||
|
||||
public RoleRate(int maxCount, int chance)
|
||||
{
|
||||
MaxCount = maxCount;
|
||||
Chance = chance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public enum RoleTypes : ushort
|
||||
{
|
||||
Crewmate,
|
||||
Impostor,
|
||||
Scientist,
|
||||
Engineer,
|
||||
GuardianAngel,
|
||||
Shapeshifter,
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,19 @@
|
||||
{
|
||||
public static bool IsCharAllowed(char i)
|
||||
{
|
||||
return i == ' ' || (i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || (i >= 'À' && i <= 'ÿ') || (i >= 'Ѐ' && i <= 'џ') || (i >= 'ㄱ' && i <= 'ㆎ') || (i >= '가' && i <= '힣');
|
||||
return i == ' ' ||
|
||||
(i >= 'A' && i <= 'Z') ||
|
||||
(i >= 'a' && i <= 'z') ||
|
||||
(i >= '0' && i <= '9') ||
|
||||
|
||||
// U+00C0 to U+00FF (Latin-1 Supplement)
|
||||
(i >= 'À' && i <= 'ÿ') ||
|
||||
|
||||
// U+0400 to U+045F (Cyrillic)
|
||||
(i >= 'Ѐ' && i <= 'џ') ||
|
||||
|
||||
// U+2C61 to U+D7A3 (CJK)
|
||||
(i >= 'ⱡ' && i <= '힣');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net.Messages;
|
||||
@@ -65,6 +65,16 @@ namespace Impostor.Api.Net
|
||||
/// </summary>
|
||||
IClientPlayer? Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the game the client is using.
|
||||
/// </summary>
|
||||
int GameVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets platform specific data of the <see cref="IClient" />.
|
||||
/// </summary>
|
||||
PlatformSpecificData PlatformSpecificData { get; }
|
||||
|
||||
ValueTask<bool> ReportCheatAsync(CheatContext context, string message);
|
||||
|
||||
ValueTask HandleMessageAsync(IMessageReader message, MessageType messageType);
|
||||
|
||||
@@ -49,25 +49,25 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
/// Sets the hat of the current <see cref="IInnerPlayerControl" />.
|
||||
/// Visible to all players.
|
||||
/// </summary>
|
||||
/// <param name="hatType">An hat for the player.</param>
|
||||
/// <param name="hatId">An hat for the player.</param>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask SetHatAsync(HatType hatType);
|
||||
ValueTask SetHatAsync(string hatId);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the pet of the current <see cref="IInnerPlayerControl" />.
|
||||
/// Visible to all players.
|
||||
/// </summary>
|
||||
/// <param name="petType">A pet for the player.</param>
|
||||
/// <param name="petId">A pet for the player.</param>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask SetPetAsync(PetType petType);
|
||||
ValueTask SetPetAsync(string petId);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the skin of the current <see cref="IInnerPlayerControl" />.
|
||||
/// Visible to all players.
|
||||
/// </summary>
|
||||
/// <param name="skinType">A skin for the player.</param>
|
||||
/// <param name="skinId">A skin for the player.</param>
|
||||
/// <returns>Task that must be awaited.</returns>
|
||||
ValueTask SetSkinAsync(SkinType skinType);
|
||||
ValueTask SetSkinAsync(string skinId);
|
||||
|
||||
/// <summary>
|
||||
/// Send a chat message as the current <see cref="IInnerPlayerControl" />.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
@@ -12,26 +12,6 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
/// </summary>
|
||||
string PlayerName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color of the player.
|
||||
/// </summary>
|
||||
ColorType Color { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hat of the player.
|
||||
/// </summary>
|
||||
HatType Hat { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pet of the player.
|
||||
/// </summary>
|
||||
PetType Pet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the skin of the player.
|
||||
/// </summary>
|
||||
SkinType Skin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the player is an impostor.
|
||||
/// </summary>
|
||||
@@ -42,6 +22,12 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
/// </summary>
|
||||
bool IsDead { get; }
|
||||
|
||||
Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; }
|
||||
|
||||
PlayerOutfitType CurrentOutfitType { get; set; }
|
||||
|
||||
PlayerOutfit CurrentOutfit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason why the player is dead in the current game.
|
||||
/// </summary>
|
||||
@@ -50,5 +36,7 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
IEnumerable<ITaskInfo> Tasks { get; }
|
||||
|
||||
DateTimeOffset LastMurder { get; }
|
||||
|
||||
uint PlayerLevel { get; }
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Impostor.Api.Net.Inner.Objects
|
||||
{
|
||||
uint Id { get; }
|
||||
|
||||
ITask Task { get; }
|
||||
ITask? Task { get; }
|
||||
|
||||
bool Complete { get; }
|
||||
|
||||
|
||||
@@ -11,15 +11,12 @@
|
||||
SetName = 6,
|
||||
CheckColor = 7,
|
||||
SetColor = 8,
|
||||
SetHat = 9,
|
||||
SetSkin = 10,
|
||||
ReportDeadBody = 11,
|
||||
MurderPlayer = 12,
|
||||
SendChat = 13,
|
||||
StartMeeting = 14,
|
||||
SetScanner = 15,
|
||||
SendChatNote = 16,
|
||||
SetPet = 17,
|
||||
SetStartCounter = 18,
|
||||
EnterVent = 19,
|
||||
ExitVent = 20,
|
||||
@@ -37,5 +34,16 @@
|
||||
SendQuickChat = 33,
|
||||
BootFromVent = 34,
|
||||
UpdateSystem = 35,
|
||||
SetLevel = 38,
|
||||
SetHat = 39,
|
||||
SetSkin = 40,
|
||||
SetPet = 41,
|
||||
SetVisor = 42,
|
||||
SetNamePlate = 43,
|
||||
SetRole = 44,
|
||||
ProtectPlayer = 45,
|
||||
Shapeshift = 46,
|
||||
CheckMurder = 47,
|
||||
CheckProtect = 48,
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Impostor.Api.Net.Messages.Announcements
|
||||
{
|
||||
public static class Message00UseCache
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.StartMessage(AnnouncementsMessageFlags.UseCache);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Impostor.Api.Net.Messages.Announcements
|
||||
{
|
||||
public static class Message01Update
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, int id, string message)
|
||||
{
|
||||
writer.StartMessage(AnnouncementsMessageFlags.SetUpdate);
|
||||
writer.WritePacked(id);
|
||||
writer.Write(message);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out int id, out string message)
|
||||
{
|
||||
id = reader.ReadPackedInt32();
|
||||
message = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Announcements
|
||||
{
|
||||
public static class Message04AddLanguages
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, Dictionary<string, uint> languages)
|
||||
{
|
||||
writer.StartMessage(AnnouncementsMessageFlags.AddLanguages);
|
||||
|
||||
writer.Write((byte)languages.Count);
|
||||
foreach (var (name, id) in languages)
|
||||
{
|
||||
writer.Write(name);
|
||||
writer.Write(id);
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out Dictionary<string, uint> languages)
|
||||
{
|
||||
var length = reader.ReadByte();
|
||||
languages = new Dictionary<string, uint>(length);
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
languages[reader.ReadString()] = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Announcements
|
||||
{
|
||||
public class MessageHello
|
||||
{
|
||||
public static void Deserialize(IMessageReader reader, out int announcementVersion, out int id, out Language language)
|
||||
{
|
||||
reader.ReadByte(); // SendOption header, probably added by accident
|
||||
announcementVersion = reader.ReadPackedInt32();
|
||||
id = reader.ReadPackedInt32();
|
||||
language = (Language)reader.ReadPackedInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Impostor.Api.Net.Messages
|
||||
{
|
||||
public class AnnouncementsMessageFlags
|
||||
{
|
||||
public const byte UseCache = 0;
|
||||
public const byte SetUpdate = 1;
|
||||
public const byte AddLanguages = 4;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace Impostor.Api.Net.Messages.Auth
|
||||
{
|
||||
public static class Message01Complete
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, uint nonce)
|
||||
{
|
||||
writer.WritePacked(nonce);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out uint nonce)
|
||||
{
|
||||
nonce = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Auth
|
||||
{
|
||||
public static class MessageHandshake
|
||||
{
|
||||
public static void Deserialize(IMessageReader reader, out int clientVersion, out Platforms platform, out string clientId)
|
||||
{
|
||||
clientVersion = reader.ReadInt32();
|
||||
platform = (Platforms)reader.ReadByte();
|
||||
clientId = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,14 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
public static class HandshakeC2S
|
||||
{
|
||||
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out uint? lastNonceReceived, out Language language, out QuickChatModes chatMode)
|
||||
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out Language language, out QuickChatModes chatMode, out PlatformSpecificData? platformSpecificData)
|
||||
{
|
||||
clientVersion = reader.ReadInt32();
|
||||
name = reader.ReadString();
|
||||
|
||||
if (clientVersion >= Version.V1)
|
||||
{
|
||||
lastNonceReceived = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastNonceReceived = null;
|
||||
reader.ReadUInt32(); // lastNonceReceived, always 0 since 2021.11.9
|
||||
}
|
||||
|
||||
if (clientVersion >= Version.V2)
|
||||
@@ -28,6 +24,22 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
language = Language.English;
|
||||
chatMode = QuickChatModes.FreeChatOrQuickChat;
|
||||
}
|
||||
|
||||
if (clientVersion >= Version.V3)
|
||||
{
|
||||
using var platformReader = reader.ReadMessage();
|
||||
platformSpecificData = new PlatformSpecificData(platformReader);
|
||||
reader.ReadInt32(); // crossplayFlags, not used yet
|
||||
}
|
||||
else
|
||||
{
|
||||
platformSpecificData = null;
|
||||
}
|
||||
|
||||
if (clientVersion >= Version.V4)
|
||||
{
|
||||
reader.ReadByte(); // purpose unknown, seems hardcoded to 0
|
||||
}
|
||||
}
|
||||
|
||||
private static class Version
|
||||
@@ -35,6 +47,10 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
public static readonly int V1 = GameVersion.GetVersion(2021, 4, 25);
|
||||
|
||||
public static readonly int V2 = GameVersion.GetVersion(2021, 6, 30);
|
||||
|
||||
public static readonly int V3 = GameVersion.GetVersion(2021, 11, 9);
|
||||
|
||||
public static readonly int V4 = GameVersion.GetVersion(2021, 12, 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
writer.StartMessage(MessageFlags.HostGame);
|
||||
gameOptionsData.Serialize(writer);
|
||||
writer.Write(int.MaxValue); // crossplayFlags
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
@@ -15,12 +16,11 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
/// Deserialize a packet.
|
||||
/// </summary>
|
||||
/// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
|
||||
/// <param name="chatMode">The chat type selected in the client of the player.</param>
|
||||
/// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
|
||||
public static GameOptionsData Deserialize(IMessageReader reader, out QuickChatModes chatMode)
|
||||
public static GameOptionsData Deserialize(IMessageReader reader)
|
||||
{
|
||||
var gameOptionsData = GameOptionsData.DeserializeCreate(reader);
|
||||
chatMode = (QuickChatModes)reader.ReadByte();
|
||||
reader.ReadInt32(); // crossplayFlags, not used yet
|
||||
|
||||
return gameOptionsData;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
|
||||
{
|
||||
gameCode = reader.ReadInt32();
|
||||
reader.ReadBoolean(); // no crossplay
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,15 @@ namespace Impostor.Api.Net.Messages.C2S
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out GameOptionsData options, out QuickChatModes chatMode)
|
||||
{
|
||||
reader.ReadPackedInt32(); // Hardcoded 0.
|
||||
var version = reader.ReadPackedInt32();
|
||||
if (version != 2)
|
||||
{
|
||||
throw new NotSupportedException($"Version {version} of {nameof(Message16GetGameListC2S)} is not supported");
|
||||
}
|
||||
|
||||
options = GameOptionsData.DeserializeCreate(reader);
|
||||
chatMode = (QuickChatModes)reader.ReadByte();
|
||||
reader.ReadInt32(); // crossplayFlags, not used yet
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
public class Message21SetActivePodType
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out string podType)
|
||||
{
|
||||
podType = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Impostor.Api.Games;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.C2S
|
||||
{
|
||||
public class Message22QueryPlatformIdsC2S
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
|
||||
{
|
||||
gameCode = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,10 @@ namespace Impostor.Api.Net.Messages
|
||||
|
||||
int ReadInt32();
|
||||
|
||||
ulong ReadUInt64();
|
||||
|
||||
long ReadInt64();
|
||||
|
||||
float ReadSingle();
|
||||
|
||||
string ReadString(int length);
|
||||
|
||||
@@ -68,6 +68,18 @@ namespace Impostor.Api.Net.Messages
|
||||
/// <param name="value">Value to write.</param>
|
||||
void Write(int value);
|
||||
|
||||
/// <summary>
|
||||
/// Writes an ulong to the message.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write.</param>
|
||||
void Write(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Writes an ulong to the message.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write.</param>
|
||||
void Write(long value);
|
||||
|
||||
/// <summary>
|
||||
/// Writes a float to the message.
|
||||
/// </summary>
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
public const byte WaitForHost = 12;
|
||||
public const byte Redirect = 13;
|
||||
public const byte ReselectServer = 14;
|
||||
public const byte GetGameList = 9;
|
||||
public const byte GetGameListV2 = 16;
|
||||
public const byte ReportPlayer = 17;
|
||||
public const byte QuickMatch = 18;
|
||||
public const byte QuickMatchHost = 19;
|
||||
public const byte SetGameSession = 20;
|
||||
public const byte SetActivePodType = 21;
|
||||
public const byte QueryPlatformIds = 22;
|
||||
public const byte QueryLobbyInfo = 23;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc09SetHat
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, HatType hat)
|
||||
{
|
||||
writer.WritePacked((uint)hat);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out HatType hat)
|
||||
{
|
||||
hat = (HatType)reader.ReadPackedUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc10SetSkin
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, SkinType skin)
|
||||
{
|
||||
writer.WritePacked((uint)skin);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out SkinType skin)
|
||||
{
|
||||
skin = (SkinType)reader.ReadPackedUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc17SetPet
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, PetType pet)
|
||||
{
|
||||
writer.WritePacked((uint)pet);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out PetType pet)
|
||||
{
|
||||
pet = (PetType)reader.ReadPackedUInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc39SetHat
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, string hat)
|
||||
{
|
||||
writer.Write(hat);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out string hat)
|
||||
{
|
||||
hat = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc40SetSkin
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, string skin)
|
||||
{
|
||||
writer.Write(skin);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out string skin)
|
||||
{
|
||||
skin = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc41SetPet
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, string pet)
|
||||
{
|
||||
writer.Write(pet);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out string pet)
|
||||
{
|
||||
pet = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc44SetRole
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, RoleTypes role)
|
||||
{
|
||||
writer.Write((ushort)role);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out RoleTypes role)
|
||||
{
|
||||
role = (RoleTypes)reader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc45ProtectPlayer
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, ColorType color)
|
||||
{
|
||||
writer.Write(playerControl.NetId);
|
||||
writer.Write((byte)color);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out ColorType color)
|
||||
{
|
||||
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
|
||||
color = (ColorType)reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc46Shapeshift
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, bool shouldAnimate)
|
||||
{
|
||||
writer.Write(playerControl.NetId);
|
||||
writer.Write(shouldAnimate);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out bool shouldAnimate)
|
||||
{
|
||||
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
|
||||
shouldAnimate = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc47CheckMurder
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
|
||||
{
|
||||
writer.Write(playerControl.NetId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
|
||||
{
|
||||
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc48CheckProtect
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
|
||||
{
|
||||
writer.Write(playerControl.NetId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
|
||||
{
|
||||
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.S2C
|
||||
{
|
||||
public class Message01JoinGameS2C
|
||||
{
|
||||
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId)
|
||||
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, IClientPlayer player, int hostId)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
@@ -14,11 +14,19 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
|
||||
writer.StartMessage(MessageFlags.JoinGame);
|
||||
writer.Write(gameCode);
|
||||
writer.Write(playerId);
|
||||
writer.Write(player.Client.Id);
|
||||
writer.Write(hostId);
|
||||
writer.Write(player.Client.Name);
|
||||
player.Client.PlatformSpecificData.Serialize(writer);
|
||||
writer.WritePacked(player.Character?.PlayerInfo.PlayerLevel ?? 1);
|
||||
|
||||
// ProductUserId and FriendCode are not yet known, so set them to an empty string
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(string.Empty);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
[Obsolete("JoinGame errors are no longer used by 2021.11.9 and up, disconnect clients instead.")]
|
||||
public static void SerializeError(IMessageWriter writer, bool clear, DisconnectReason reason, string? message = null)
|
||||
{
|
||||
if (clear)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.S2C
|
||||
{
|
||||
public static class Message07JoinedGameS2C
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, int[] otherPlayerIds)
|
||||
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, IClientPlayer[] otherPlayers, bool post20220202 = true)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
@@ -15,11 +15,21 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
writer.Write(gameCode);
|
||||
writer.Write(playerId);
|
||||
writer.Write(hostId);
|
||||
writer.WritePacked(otherPlayerIds.Length);
|
||||
writer.WritePacked(otherPlayers.Length);
|
||||
|
||||
foreach (var id in otherPlayerIds)
|
||||
foreach (var ply in otherPlayers)
|
||||
{
|
||||
writer.WritePacked(id);
|
||||
writer.WritePacked(ply.Client.Id);
|
||||
writer.Write(ply.Client.Name);
|
||||
ply.Client.PlatformSpecificData.Serialize(writer);
|
||||
writer.WritePacked(ply.Character?.PlayerInfo.PlayerLevel ?? 1);
|
||||
|
||||
if (post20220202)
|
||||
{
|
||||
// ProductUserId and FriendCode are not yet known, so set them to an empty string
|
||||
writer.Write(string.Empty);
|
||||
writer.Write(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Games;
|
||||
|
||||
@@ -25,6 +25,9 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
writer.Write((byte)game.Options.Map);
|
||||
writer.Write((byte)game.Options.NumImpostors);
|
||||
writer.Write((byte)game.Options.MaxPlayers);
|
||||
var platform = game.Host?.Client.PlatformSpecificData;
|
||||
writer.Write((byte)(platform?.Platform ?? 0));
|
||||
writer.Write(platform?.PlatformName ?? string.Empty);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.S2C
|
||||
{
|
||||
public class Message22QueryPlatformIdsS2C
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, GameCode gameCode, IEnumerable<PlatformSpecificData> playerSpecificDatas)
|
||||
{
|
||||
writer.StartMessage(MessageFlags.QueryPlatformIds);
|
||||
writer.Write(gameCode);
|
||||
foreach (var playerSpecificData in playerSpecificDatas)
|
||||
{
|
||||
playerSpecificData.Serialize(writer);
|
||||
}
|
||||
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ namespace Impostor.Api.Net.Messages.S2C
|
||||
|
||||
if (hasReason)
|
||||
{
|
||||
var inner = reader.ReadMessage();
|
||||
using var inner = reader.ReadMessage();
|
||||
reason = (DisconnectReason)inner.ReadByte();
|
||||
message = reason == DisconnectReason.Custom ? inner.ReadString() : null;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -10,7 +9,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
|
||||
<ProjectReference Include="..\Impostor.Hazel\Hazel\Hazel.csproj" />
|
||||
|
||||
+1
-1
Submodule src/Impostor.Hazel updated: 8de027461e...74070b8c07.
@@ -1,7 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
|
||||
<Version>1.0.0</Version>
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Impostor.Plugins.Example
|
||||
services.AddSingleton<IEventListener, ClientEventListener>();
|
||||
services.AddSingleton<IEventListener, PlayerEventListener>();
|
||||
services.AddSingleton<IEventListener, MeetingEventListener>();
|
||||
services.AddSingleton<IEventListener, AnnouncementsListener>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Events.Announcements;
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Plugins.Example.Handlers
|
||||
{
|
||||
public class AnnouncementsListener : IEventListener
|
||||
{
|
||||
private const int Id = 50;
|
||||
|
||||
[EventListener]
|
||||
public void OnAnnouncementRequestEvent(IAnnouncementRequestEvent e)
|
||||
{
|
||||
if (e.Id == Id)
|
||||
{
|
||||
// Client already has announcement cached, lets just use that
|
||||
e.Response.UseCached = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client is receiving this announcement for the first time, window will popup
|
||||
e.Response.Announcement = new Announcement(Id, "Hello!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,10 @@ namespace Impostor.Plugins.Example.Handlers
|
||||
{
|
||||
// Modify player properties.
|
||||
await playerControl.SetColorAsync((ColorType)_random.Next(1, 9));
|
||||
await playerControl.SetHatAsync((HatType)_random.Next(1, 9));
|
||||
await playerControl.SetSkinAsync((SkinType)_random.Next(1, 9));
|
||||
await playerControl.SetPetAsync((PetType)_random.Next(1, 9));
|
||||
// TODO Rewrite using cosmetics source generator
|
||||
// await playerControl.SetHatAsync((HatType)_random.Next(1, 9));
|
||||
// await playerControl.SetSkinAsync((SkinType)_random.Next(1, 9));
|
||||
// await playerControl.SetPetAsync((PetType)_random.Next(1, 9));
|
||||
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(5000));
|
||||
}
|
||||
@@ -74,9 +75,9 @@ namespace Impostor.Plugins.Example.Handlers
|
||||
if (e.Message == "look")
|
||||
{
|
||||
await e.PlayerControl.SetColorAsync(ColorType.Pink);
|
||||
await e.PlayerControl.SetHatAsync(HatType.Cheese);
|
||||
await e.PlayerControl.SetSkinAsync(SkinType.Police);
|
||||
await e.PlayerControl.SetPetAsync(PetType.Ufo);
|
||||
await e.PlayerControl.SetHatAsync("hat_pk05_Cheese");
|
||||
await e.PlayerControl.SetSkinAsync("skin_Police");
|
||||
await e.PlayerControl.SetPetAsync("pet_alien1");
|
||||
}
|
||||
|
||||
if (e.Message == "snap")
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using Impostor.Server.Utils;
|
||||
|
||||
namespace Impostor.Server.Config
|
||||
{
|
||||
internal class AnnouncementsServerConfig
|
||||
{
|
||||
public const string Section = "AnnouncementsServer";
|
||||
|
||||
private string? _resolvedListenIp;
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public string ListenIp { get; set; } = "0.0.0.0";
|
||||
|
||||
public ushort ListenPort { get; set; } = 22024;
|
||||
|
||||
public string ResolveListenIp()
|
||||
{
|
||||
return _resolvedListenIp ??= IpUtils.ResolveIp(ListenIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.IO;
|
||||
using Impostor.Server.Utils;
|
||||
|
||||
namespace Impostor.Server.Config
|
||||
{
|
||||
internal class AuthServerConfig
|
||||
{
|
||||
public const string Section = "AuthServer";
|
||||
|
||||
private string? _resolvedListenIp;
|
||||
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
public string ListenIp { get; set; } = "0.0.0.0";
|
||||
|
||||
public ushort ListenPort { get; set; } = 22025;
|
||||
|
||||
public string Certificate { get; set; } = Path.Combine("dtls", "certificate.pem");
|
||||
|
||||
public string PrivateKey { get; set; } = Path.Combine("dtls", "key.pem");
|
||||
|
||||
public string ResolveListenIp()
|
||||
{
|
||||
return _resolvedListenIp ??= IpUtils.ResolveIp(ListenIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Impostor.Api.Events.Announcements;
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Server.Events.Announcements
|
||||
{
|
||||
public class AnnouncementRequestEvent : IAnnouncementRequestEvent
|
||||
{
|
||||
public AnnouncementRequestEvent(int id, Language language)
|
||||
{
|
||||
Id = id;
|
||||
Language = language;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public Language Language { get; }
|
||||
|
||||
public IAnnouncementRequestEvent.IResponse Response { get; set; } = new AnnouncementResponse();
|
||||
|
||||
public class AnnouncementResponse : IAnnouncementRequestEvent.IResponse
|
||||
{
|
||||
public bool UseCached { get; set; } = false;
|
||||
|
||||
public Announcement? Announcement { get; set; } = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Messages.S2C;
|
||||
using Impostor.Hazel;
|
||||
|
||||
namespace Impostor.Server
|
||||
{
|
||||
public static class IHazelConnectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Disconnect a connection using a custom message.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection to disconnect.</param>
|
||||
/// <param name="reason">The reason to disconnect with.</param>
|
||||
/// <param name="message">The custom message to disconnect with if <paramref name="reason"/> is <see cref="DisconnectReason.Custom"/>.</param>
|
||||
/// <returns>Task that should be awaited to ensure disconnection.</returns>
|
||||
public static async ValueTask CustomDisconnectAsync(this IHazelConnection connection, DisconnectReason reason, string? message = null)
|
||||
{
|
||||
if (!connection.IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var writer = MessageWriter.Get();
|
||||
MessageDisconnect.Serialize(writer, true, reason, message);
|
||||
|
||||
await connection.DisconnectAsync(reason.ToString(), writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
@@ -25,18 +24,19 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.4" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.333">
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileSystemGlobbing" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.5" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- HACK: Some plugins depend on System.IO.Pipelines and expect the version to be 5.0.0.0, but .NET 5 only ships version 5.0.0.1. It is pretty hard to convince MSBuild to build against the correct DLL, so we special case it here. Remove once we move past .NET 5 -->
|
||||
<PackageReference Include="System.IO.Pipelines" Version="5.0.0" />
|
||||
<!-- HACK: Some plugins depend on System.IO.Pipelines and expect the version to be 6.0.0.0, but Hazel still depends on version 5.0.0.0. Figure out why this happens. -->
|
||||
<PackageReference Include="System.IO.Pipelines" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Net.Messages.Announcements;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Hazel.Udp;
|
||||
using Impostor.Server.Config;
|
||||
using Impostor.Server.Events.Announcements;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
internal class AnnouncementsService : IHostedService
|
||||
{
|
||||
private readonly ILogger<AnnouncementsService> _logger;
|
||||
private readonly AnnouncementsServerConfig _config;
|
||||
private readonly ObjectPool<MessageReader> _readerPool;
|
||||
private readonly IEventManager _eventManager;
|
||||
private UdpConnectionListener? _connection;
|
||||
|
||||
public AnnouncementsService(ILogger<AnnouncementsService> logger, IOptions<AnnouncementsServerConfig> config, ObjectPool<MessageReader> readerPool, IEventManager eventManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_config = config.Value;
|
||||
_readerPool = readerPool;
|
||||
_eventManager = eventManager;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var endpoint = new IPEndPoint(IPAddress.Parse(_config.ResolveListenIp()), _config.ListenPort);
|
||||
|
||||
var mode = endpoint.AddressFamily switch
|
||||
{
|
||||
AddressFamily.InterNetwork => IPMode.IPv4,
|
||||
AddressFamily.InterNetworkV6 => IPMode.IPv6,
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
|
||||
_connection = new UdpConnectionListener(endpoint, _readerPool, mode)
|
||||
{
|
||||
NewConnection = OnNewConnection,
|
||||
};
|
||||
|
||||
await _connection.StartAsync();
|
||||
|
||||
_logger.LogInformation("Announcements server is listening on {Address}:{Port}", endpoint.Address, endpoint.Port);
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogWarning("Announcements server is shutting down!");
|
||||
|
||||
if (_connection != null)
|
||||
{
|
||||
await _connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask OnNewConnection(NewConnectionEventArgs e)
|
||||
{
|
||||
MessageHello.Deserialize(e.HandshakeData, out var announcementVersion, out var id, out var language);
|
||||
|
||||
_logger.LogDebug("Client requested announcement (version: {Version}, id: {Id}, language: {Language})", announcementVersion, id, language);
|
||||
|
||||
if (announcementVersion != 2)
|
||||
{
|
||||
await e.Connection.Disconnect("Unsupported announcement version");
|
||||
return;
|
||||
}
|
||||
|
||||
var @event = new AnnouncementRequestEvent(id, language);
|
||||
await _eventManager.CallAsync(@event);
|
||||
|
||||
var response = @event.Response;
|
||||
|
||||
if (response.UseCached)
|
||||
{
|
||||
using var writer = MessageWriter.Get(MessageType.Reliable);
|
||||
Message00UseCache.Serialize(writer);
|
||||
await e.Connection.SendAsync(writer);
|
||||
|
||||
_logger.LogDebug("Sent UseCache response");
|
||||
}
|
||||
|
||||
if (response.Announcement != null)
|
||||
{
|
||||
using var writer = MessageWriter.Get(MessageType.Reliable);
|
||||
var announcement = response.Announcement.Value;
|
||||
Message01Update.Serialize(writer, announcement.Id, announcement.Message);
|
||||
await e.Connection.SendAsync(writer);
|
||||
|
||||
_logger.LogDebug("Sent ({Id}) {Message}", announcement.Id, announcement.Message);
|
||||
}
|
||||
|
||||
await e.Connection.Disconnect(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Net.Messages.Auth;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Hazel.Dtls;
|
||||
using Impostor.Server.Config;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
internal class AuthService : IHostedService
|
||||
{
|
||||
private readonly ILogger<AuthService> _logger;
|
||||
private readonly AuthServerConfig _config;
|
||||
private readonly ObjectPool<MessageReader> _readerPool;
|
||||
private readonly IEventManager _eventManager;
|
||||
private DtlsConnectionListener? _connection;
|
||||
|
||||
public AuthService(ILogger<AuthService> logger, IOptions<AuthServerConfig> config, ObjectPool<MessageReader> readerPool, IEventManager eventManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_config = config.Value;
|
||||
_readerPool = readerPool;
|
||||
_eventManager = eventManager;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var endpoint = new IPEndPoint(IPAddress.Parse(_config.ResolveListenIp()), _config.ListenPort);
|
||||
|
||||
var mode = endpoint.AddressFamily switch
|
||||
{
|
||||
AddressFamily.InterNetwork => IPMode.IPv4,
|
||||
AddressFamily.InterNetworkV6 => IPMode.IPv6,
|
||||
_ => throw new InvalidOperationException(),
|
||||
};
|
||||
|
||||
var rsa = RSA.Create();
|
||||
rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(string.Join(string.Empty, (await File.ReadAllLinesAsync(_config.PrivateKey, cancellationToken)).Where(x => !x.StartsWith("-----")))), out _);
|
||||
var cert = new X509Certificate2(_config.Certificate).CopyWithPrivateKey(rsa);
|
||||
|
||||
_connection = new DtlsConnectionListener(endpoint, _readerPool, mode);
|
||||
_connection.SetCertificate(cert);
|
||||
_connection.NewConnection = ConnectionOnNewConnection;
|
||||
|
||||
await _connection.StartAsync();
|
||||
|
||||
_logger.LogInformation("Auth server is listening on {Address}:{Port}", endpoint.Address, endpoint.Port);
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogWarning("Auth server is shutting down!");
|
||||
|
||||
if (_connection != null)
|
||||
{
|
||||
await _connection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private ValueTask ConnectionOnNewConnection(NewConnectionEventArgs e)
|
||||
{
|
||||
MessageHandshake.Deserialize(e.HandshakeData, out var clientVersion, out var platform, out var clientId);
|
||||
|
||||
_logger.LogTrace("New authentication request: {clientVersion}, {platform}, {clientId}", clientVersion, platform, clientId);
|
||||
|
||||
using var writer = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
writer.StartMessage(1);
|
||||
Message01Complete.Serialize(writer, (uint)RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue));
|
||||
writer.EndMessage();
|
||||
|
||||
return e.Connection.SendAsync(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Games;
|
||||
@@ -24,8 +25,8 @@ namespace Impostor.Server.Net
|
||||
private readonly GameManager _gameManager;
|
||||
private readonly ICustomMessageManager<ICustomRootMessage> _customMessageManager;
|
||||
|
||||
public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
|
||||
: base(name, gameVersion, language, chatMode, connection)
|
||||
public Client(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, GameManager gameManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
|
||||
: base(name, gameVersion, language, chatMode, platformSpecificData, connection)
|
||||
{
|
||||
_logger = logger;
|
||||
_antiCheatConfig = antiCheatOptions.Value;
|
||||
@@ -64,7 +65,7 @@ namespace Impostor.Server.Net
|
||||
case MessageFlags.HostGame:
|
||||
{
|
||||
// Read game settings.
|
||||
var gameInfo = Message00HostGameC2S.Deserialize(reader, out _);
|
||||
var gameInfo = Message00HostGameC2S.Deserialize(reader);
|
||||
|
||||
// Create game.
|
||||
var game = await _gameManager.CreateAsync(this, gameInfo);
|
||||
@@ -257,6 +258,19 @@ namespace Impostor.Server.Net
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageFlags.SetActivePodType:
|
||||
{
|
||||
Message21SetActivePodType.Deserialize(reader, out _);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageFlags.QueryPlatformIds:
|
||||
{
|
||||
Message22QueryPlatformIdsC2S.Deserialize(reader, out var gameCode);
|
||||
await OnQueryPlatformIds(gameCode);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (_customMessageManager.TryGet(flag, out var customRootMessage))
|
||||
{
|
||||
@@ -348,5 +362,22 @@ namespace Impostor.Server.Net
|
||||
|
||||
return Connection.SendAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the connected client requests the PlatformSpecificData.
|
||||
/// </summary>
|
||||
/// <param name="code">
|
||||
/// The GameCode of the game whose platform id's are checked.
|
||||
/// </param>
|
||||
private ValueTask OnQueryPlatformIds(GameCode code)
|
||||
{
|
||||
using var message = MessageWriter.Get(MessageType.Reliable);
|
||||
|
||||
var playerSpecificData = _gameManager.Find(code)?.Players.Select(p => p.Client.PlatformSpecificData) ?? Enumerable.Empty<PlatformSpecificData>();
|
||||
|
||||
Message22QueryPlatformIdsS2C.Serialize(message, code, playerSpecificData);
|
||||
|
||||
return Connection.SendAsync(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,19 @@ using Impostor.Api;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Net.Messages.S2C;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Server.Net.State;
|
||||
|
||||
namespace Impostor.Server.Net
|
||||
{
|
||||
internal abstract class ClientBase : IClient
|
||||
{
|
||||
protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
|
||||
protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, IHazelConnection connection)
|
||||
{
|
||||
Name = name;
|
||||
GameVersion = gameVersion;
|
||||
Language = language;
|
||||
ChatMode = chatMode;
|
||||
PlatformSpecificData = platformSpecificData;
|
||||
Connection = connection;
|
||||
Items = new ConcurrentDictionary<object, object>();
|
||||
}
|
||||
@@ -31,6 +30,8 @@ namespace Impostor.Server.Net
|
||||
|
||||
public QuickChatModes ChatMode { get; }
|
||||
|
||||
public PlatformSpecificData PlatformSpecificData { get; }
|
||||
|
||||
public int GameVersion { get; }
|
||||
|
||||
public IHazelConnection Connection { get; }
|
||||
@@ -52,15 +53,7 @@ namespace Impostor.Server.Net
|
||||
|
||||
public async ValueTask DisconnectAsync(DisconnectReason reason, string? message = null)
|
||||
{
|
||||
if (!Connection.IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var writer = MessageWriter.Get();
|
||||
MessageDisconnect.Serialize(writer, true, reason, message);
|
||||
|
||||
await Connection.DisconnectAsync(message ?? reason.ToString(), writer);
|
||||
await Connection.CustomDisconnectAsync(reason, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ namespace Impostor.Server.Net.Factories
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
|
||||
public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData)
|
||||
{
|
||||
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, connection);
|
||||
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, platformSpecificData, connection);
|
||||
connection.Client = client;
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@ namespace Impostor.Server.Net.Factories
|
||||
{
|
||||
internal interface IClientFactory
|
||||
{
|
||||
ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode);
|
||||
ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Server.Net.Inner.Objects;
|
||||
|
||||
@@ -85,6 +86,32 @@ namespace Impostor.Server.Net.Inner
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async ValueTask<bool> ValidateCanVent(CheatContext context, IClientPlayer sender, InnerPlayerInfo playerInfo, bool value = true)
|
||||
{
|
||||
if (playerInfo.CanVent != value)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(context, "Failed can vent check"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async ValueTask<bool> ValidateRole(CheatContext context, IClientPlayer sender, InnerPlayerInfo playerInfo, RoleTypes role)
|
||||
{
|
||||
if (playerInfo.RoleType != role)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(context, $"Failed role = {role} check"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async ValueTask<bool> UnregisteredCall(CheatContext context, IClientPlayer sender)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(context, "Client sent unregistered call"))
|
||||
|
||||
@@ -120,25 +120,36 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (!await ValidateImpostor(call, sender, _playerControl.PlayerInfo))
|
||||
if (!await ValidateCanVent(call, sender, _playerControl.PlayerInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var vents = Game.GameNet.ShipStatus!.Data.Vents.Values;
|
||||
|
||||
var vent = vents.SingleOrDefault(x => Approximately(x.Position, position + ColliderOffset));
|
||||
|
||||
if (vent == null)
|
||||
if (Game.GameNet.ShipStatus == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(call, "Failed vent position check"))
|
||||
// Cannot perform vent position check on unknown ship statuses
|
||||
if (await sender.Client.ReportCheatAsync(call, "Failed vent position check on unknown map"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _eventManager.CallAsync(new PlayerVentEvent(Game, sender, _playerControl, vent));
|
||||
var vents = Game.GameNet.ShipStatus!.Data.Vents.Values;
|
||||
|
||||
var vent = vents.SingleOrDefault(x => Approximately(x.Position, position + ColliderOffset));
|
||||
|
||||
if (vent == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(call, "Failed vent position check"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _eventManager.CallAsync(new PlayerVentEvent(Game, sender, _playerControl, vent));
|
||||
}
|
||||
}
|
||||
|
||||
await SnapToAsync(sender, position, minSid);
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
case RpcCalls.EnterVent:
|
||||
case RpcCalls.ExitVent:
|
||||
{
|
||||
if (!await ValidateImpostor(call, sender, _playerControl.PlayerInfo))
|
||||
if (!await ValidateCanVent(call, sender, _playerControl.PlayerInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -66,7 +66,17 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
throw new ArgumentOutOfRangeException(nameof(call), call, null);
|
||||
}
|
||||
|
||||
if (!Game.GameNet.ShipStatus!.Data.Vents.TryGetValue(ventId, out var vent))
|
||||
if (Game.GameNet.ShipStatus == null)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(call, "Client interacted with vent on unknown map"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Game.GameNet.ShipStatus.Data.Vents.TryGetValue(ventId, out var vent))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(call, "Client interacted with nonexistent vent"))
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
private readonly InnerPlayerInfo _playerInfo;
|
||||
private readonly IEventManager _eventManager;
|
||||
|
||||
public TaskInfo(InnerPlayerInfo playerInfo, IEventManager eventManager, uint id, ITask task)
|
||||
public TaskInfo(InnerPlayerInfo playerInfo, IEventManager eventManager, uint id, ITask? task)
|
||||
{
|
||||
_playerInfo = playerInfo;
|
||||
_eventManager = eventManager;
|
||||
@@ -22,7 +22,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public uint Id { get; internal set; }
|
||||
|
||||
public ITask Task { get; internal set; }
|
||||
public ITask? Task { get; internal set; }
|
||||
|
||||
public bool Complete { get; internal set; }
|
||||
|
||||
|
||||
@@ -59,16 +59,18 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialState)
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
var num = reader.ReadPackedInt32();
|
||||
|
||||
for (var i = 0; i < num; i++)
|
||||
var inner = reader.ReadMessage();
|
||||
var playerInfo = this.GetPlayerById(inner.Tag);
|
||||
if (playerInfo != null)
|
||||
{
|
||||
var playerId = reader.ReadByte();
|
||||
var playerInfo = new InnerPlayerInfo(playerId);
|
||||
|
||||
playerInfo.Deserialize(reader);
|
||||
playerInfo.Deserialize(inner);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerInfo = new InnerPlayerInfo(inner.Tag);
|
||||
playerInfo.Deserialize(inner);
|
||||
|
||||
if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
|
||||
{
|
||||
@@ -76,28 +78,6 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
var inner = reader.ReadMessage();
|
||||
var playerInfo = this.GetPlayerById(inner.Tag);
|
||||
if (playerInfo != null)
|
||||
{
|
||||
playerInfo.Deserialize(inner);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerInfo = new InnerPlayerInfo(inner.Tag);
|
||||
playerInfo.Deserialize(inner);
|
||||
|
||||
if (!_allPlayers.TryAdd(playerInfo.PlayerId, playerInfo))
|
||||
{
|
||||
throw new ImpostorException("Failed to add player to InnerGameData.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
@@ -160,12 +140,9 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
var taskId = 0u;
|
||||
foreach (var taskTypeId in taskTypeIds.Span)
|
||||
{
|
||||
player.Tasks.Add(new TaskInfo(
|
||||
player,
|
||||
_eventManager,
|
||||
taskId++,
|
||||
Game.GameNet!.ShipStatus!.Data.Tasks[taskTypeId]
|
||||
));
|
||||
var mapTasks = Game.GameNet!.ShipStatus?.Data.Tasks;
|
||||
var taskType = (mapTasks != null && mapTasks.ContainsKey(taskTypeId)) ? mapTasks[taskTypeId] : null;
|
||||
player.Tasks.Add(new TaskInfo(player, _eventManager, taskId++, taskType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,10 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
|
||||
Rpc23VotingComplete.Deserialize(reader, out var states, out var playerId, out var tie);
|
||||
foreach (var messageReader in states)
|
||||
{
|
||||
messageReader.Dispose();
|
||||
}
|
||||
|
||||
// This would be a nice place to implement an anti cheat.
|
||||
// But for whatever reason host sends VotingComplete before sending his vote.
|
||||
|
||||
@@ -29,37 +29,37 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public async ValueTask SetColorAsync(ColorType color)
|
||||
{
|
||||
PlayerInfo.Color = color;
|
||||
PlayerInfo.CurrentOutfit.Color = color;
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetColor);
|
||||
Rpc08SetColor.Serialize(writer, color);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
public async ValueTask SetHatAsync(HatType hat)
|
||||
public async ValueTask SetHatAsync(string hatId)
|
||||
{
|
||||
PlayerInfo.Hat = hat;
|
||||
PlayerInfo.CurrentOutfit.HatId = hatId;
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetHat);
|
||||
Rpc09SetHat.Serialize(writer, hat);
|
||||
Rpc39SetHat.Serialize(writer, hatId);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
public async ValueTask SetPetAsync(PetType pet)
|
||||
public async ValueTask SetPetAsync(string petId)
|
||||
{
|
||||
PlayerInfo.Pet = pet;
|
||||
PlayerInfo.CurrentOutfit.PetId = petId;
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetPet);
|
||||
Rpc17SetPet.Serialize(writer, pet);
|
||||
Rpc41SetPet.Serialize(writer, petId);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
public async ValueTask SetSkinAsync(SkinType skin)
|
||||
public async ValueTask SetSkinAsync(string skinId)
|
||||
{
|
||||
PlayerInfo.Skin = skin;
|
||||
PlayerInfo.CurrentOutfit.SkinId = skinId;
|
||||
|
||||
using var writer = Game.StartRpc(NetId, RpcCalls.SetSkin);
|
||||
Rpc10SetSkin.Serialize(writer, skin);
|
||||
Rpc40SetSkin.Serialize(writer, skinId);
|
||||
await Game.FinishRpcAsync(writer);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
internal Queue<ColorType> RequestedColorId { get; } = new Queue<ColorType>();
|
||||
|
||||
/// <summary> Gets or sets target that was set by the last CheckMurder RPC. </summary>
|
||||
internal IInnerPlayerControl? IsMurdering { get; set; } = null;
|
||||
|
||||
public override ValueTask<bool> SerializeAsync(IMessageWriter writer, bool initialState)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
@@ -182,8 +185,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc09SetHat.Deserialize(reader, out var hat);
|
||||
return await HandleSetHat(sender, hat);
|
||||
Rpc39SetHat.Deserialize(reader, out var hat);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RpcCalls.SetSkin:
|
||||
@@ -193,8 +196,41 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc10SetSkin.Deserialize(reader, out var skin);
|
||||
return await HandleSetSkin(sender, skin);
|
||||
Rpc40SetSkin.Deserialize(reader, out var skin);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RpcCalls.SetVisor:
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rpc42SetVistor.Deserialize(reader, out var visor);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RpcCalls.SetNamePlate:
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rpc43SetNamePlate.Deserialize(reader, out var namePlate);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RpcCalls.SetLevel:
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rpc38SetLevel.Deserialize(reader, out var level);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RpcCalls.ReportDeadBody:
|
||||
@@ -210,7 +246,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
case RpcCalls.MurderPlayer:
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender) || !await ValidateImpostor(call, sender, PlayerInfo))
|
||||
if (!await ValidateHost(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -271,7 +307,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc17SetPet.Deserialize(reader, out var pet);
|
||||
Rpc41SetPet.Deserialize(reader, out var pet);
|
||||
return await HandleSetPet(sender, pet);
|
||||
}
|
||||
|
||||
@@ -308,6 +344,70 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.SetRole:
|
||||
{
|
||||
if (!await ValidateHost(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc44SetRole.Deserialize(reader, out var role);
|
||||
PlayerInfo.RoleType = role;
|
||||
|
||||
if (Game.GameState == GameStates.Starting)
|
||||
{
|
||||
await Game.StartedAsync();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.ProtectPlayer:
|
||||
{
|
||||
if (!await ValidateHost(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc45ProtectPlayer.Deserialize(reader, Game, out _, out _);
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.Shapeshift:
|
||||
{
|
||||
if (!await ValidateRole(call, sender, PlayerInfo, RoleTypes.Shapeshifter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc46Shapeshift.Deserialize(reader, Game, out _, out _);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.CheckMurder:
|
||||
{
|
||||
if (!await ValidateImpostor(call, sender, PlayerInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc47CheckMurder.Deserialize(reader, Game, out var murdered);
|
||||
return await HandleCheckMurder(sender, murdered);
|
||||
}
|
||||
|
||||
case RpcCalls.CheckProtect:
|
||||
{
|
||||
if (!await ValidateRole(call, sender, PlayerInfo, RoleTypes.GuardianAngel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Rpc48CheckProtect.Deserialize(reader, Game, out _);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
@@ -343,7 +443,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
var player = Game.GameNet.GameData!.GetPlayerById(infectedIds.Span[i]);
|
||||
if (player != null)
|
||||
{
|
||||
player.IsImpostor = true;
|
||||
// player.IsImpostor = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +579,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
if (sender.IsOwner(this))
|
||||
{
|
||||
if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == color))
|
||||
if (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == color))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.SetColor, "Client sent a color that is already used"))
|
||||
{
|
||||
@@ -497,7 +597,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
var expected = RequestedColorId.Dequeue();
|
||||
|
||||
while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.Color == expected))
|
||||
while (Game.Players.Any(x => x.Character != null && x.Character != this && x.Character.PlayerInfo.CurrentOutfit.Color == expected))
|
||||
{
|
||||
expected = (ColorType)(((byte)expected + 1) % ColorsCount);
|
||||
}
|
||||
@@ -510,45 +610,62 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo.Color = color;
|
||||
PlayerInfo.CurrentOutfit.Color = color;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleSetHat(ClientPlayer sender, HatType hat)
|
||||
private async ValueTask<bool> HandleSetHat(ClientPlayer sender, string hat)
|
||||
{
|
||||
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetHat, "Client tried to change hat while not in lobby"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerInfo.Hat = hat;
|
||||
PlayerInfo.CurrentOutfit.HatId = hat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, SkinType skin)
|
||||
private async ValueTask<bool> HandleSetSkin(ClientPlayer sender, string skin)
|
||||
{
|
||||
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetSkin, "Client tried to change skin while not in lobby"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerInfo.Skin = skin;
|
||||
PlayerInfo.CurrentOutfit.SkinId = skin;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleCheckMurder(ClientPlayer sender, IInnerPlayerControl? target)
|
||||
{
|
||||
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder too fast"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
|
||||
|
||||
if (target == null || target.PlayerInfo.IsImpostor)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.CheckMurder, "Client tried to murder invalid target"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IsMurdering = target;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleMurderPlayer(ClientPlayer sender, IInnerPlayerControl? target)
|
||||
{
|
||||
if (!PlayerInfo.CanMurder(Game, _dateTimeProvider))
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder too fast"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null || target.PlayerInfo.IsImpostor)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Client tried to murder invalid target"))
|
||||
@@ -557,7 +674,14 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfo.LastMurder = _dateTimeProvider.UtcNow - TimeSpan.FromMilliseconds(sender.Client.Connection.AveragePing);
|
||||
// If the host is also the impostor that committed the murder, CheckMurder is actually sent *after* the MurderPlayer RPC
|
||||
if (sender.Character != this && target != IsMurdering)
|
||||
{
|
||||
if (await sender.Client.ReportCheatAsync(RpcCalls.MurderPlayer, "Host tried to murder incorrect target"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null && !target.PlayerInfo.IsDead)
|
||||
{
|
||||
@@ -565,6 +689,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
await _eventManager.CallAsync(new PlayerMurderEvent(Game, sender, this, target));
|
||||
}
|
||||
|
||||
IsMurdering = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -582,14 +708,14 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
await _eventManager.CallAsync(new PlayerStartMeetingEvent(Game, Game.GetClientPlayer(this.OwnerId)!, this, deadPlayer));
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleSetPet(ClientPlayer sender, PetType pet)
|
||||
private async ValueTask<bool> HandleSetPet(ClientPlayer sender, string pet)
|
||||
{
|
||||
if (Game.GameState == GameStates.Started && await sender.Client.ReportCheatAsync(RpcCalls.SetPet, "Client tried to change pet while not in lobby"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerInfo.Pet = pet;
|
||||
PlayerInfo.CurrentOutfit.PetId = pet;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,17 +21,22 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public string PlayerName { get; internal set; } = string.Empty;
|
||||
|
||||
public ColorType Color { get; internal set; } = (ColorType)(-1);
|
||||
public Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; } = new()
|
||||
{
|
||||
[PlayerOutfitType.Default] = new PlayerOutfit(),
|
||||
};
|
||||
|
||||
public HatType Hat { get; internal set; }
|
||||
public PlayerOutfitType CurrentOutfitType { get; set; } = PlayerOutfitType.Default;
|
||||
|
||||
public PetType Pet { get; internal set; }
|
||||
public PlayerOutfit CurrentOutfit => Outfits[CurrentOutfitType];
|
||||
|
||||
public SkinType Skin { get; internal set; }
|
||||
public RoleTypes RoleType { get; internal set; }
|
||||
|
||||
public bool Disconnected { get; internal set; }
|
||||
|
||||
public bool IsImpostor { get; internal set; }
|
||||
public bool IsImpostor => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter;
|
||||
|
||||
public bool CanVent => RoleType is RoleTypes.Impostor or RoleTypes.Shapeshifter or RoleTypes.Engineer;
|
||||
|
||||
public bool IsDead { get; internal set; }
|
||||
|
||||
@@ -41,6 +46,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public DateTimeOffset LastMurder { get; set; }
|
||||
|
||||
public uint PlayerLevel { get; internal set; }
|
||||
|
||||
public bool CanMurder(IGame game, IDateTimeProvider dateTimeProvider)
|
||||
{
|
||||
if (!IsImpostor)
|
||||
@@ -48,7 +55,12 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
return false;
|
||||
}
|
||||
|
||||
return dateTimeProvider.UtcNow.Subtract(LastMurder).TotalSeconds >= game.Options.KillCooldown;
|
||||
// In 2021.11.9 the Guardian Angel was created, which was unfortunately implemented in such a way that
|
||||
// it is hard to determine accurately whether a kill was prevented or not. When a kill was prevented,
|
||||
// the impostor has a cooldown of half the usual duration. As a workaround we always assume the kill was
|
||||
// prevented and the impostor only has half of its cooldown.
|
||||
// FIXME when the base game improved their implementation.
|
||||
return dateTimeProvider.UtcNow.Subtract(LastMurder).TotalSeconds >= game.Options.KillCooldown / 2;
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
@@ -58,22 +70,29 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
public void Deserialize(IMessageReader reader)
|
||||
{
|
||||
PlayerName = reader.ReadString();
|
||||
Color = (ColorType)reader.ReadPackedInt32();
|
||||
Hat = (HatType)reader.ReadPackedUInt32();
|
||||
Pet = (PetType)reader.ReadPackedUInt32();
|
||||
Skin = (SkinType)reader.ReadPackedUInt32();
|
||||
Outfits.Clear();
|
||||
var b = reader.ReadByte();
|
||||
for (var i = 0; i < b; i++)
|
||||
{
|
||||
var key = (PlayerOutfitType)reader.ReadByte();
|
||||
Outfits[key] = new PlayerOutfit();
|
||||
Outfits[key].Deserialize(reader);
|
||||
}
|
||||
|
||||
PlayerLevel = reader.ReadPackedUInt32();
|
||||
|
||||
var flag = reader.ReadByte();
|
||||
Disconnected = (flag & 1) > 0;
|
||||
IsImpostor = (flag & 2) > 0;
|
||||
IsDead = (flag & 4) > 0;
|
||||
Disconnected = (flag & 1) != 0;
|
||||
IsDead = (flag & 4) != 0;
|
||||
|
||||
var roleType = (RoleTypes)reader.ReadUInt16();
|
||||
RoleType = roleType;
|
||||
|
||||
var taskCount = reader.ReadByte();
|
||||
|
||||
if (Tasks.Count != taskCount)
|
||||
if (Tasks.Count < taskCount)
|
||||
{
|
||||
Tasks = new List<InnerGameData.TaskInfo>(taskCount);
|
||||
taskCount = (byte)Tasks.Count;
|
||||
}
|
||||
|
||||
for (var i = 0; i < taskCount; i++)
|
||||
|
||||
@@ -8,7 +8,6 @@ using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Net.Messages.S2C;
|
||||
using Impostor.Hazel;
|
||||
using Impostor.Server.Config;
|
||||
using Impostor.Server.Events.Client;
|
||||
@@ -22,7 +21,10 @@ namespace Impostor.Server.Net.Manager
|
||||
// NOTE: when updating this array, keep the versions ordered from old to new, otherwise the version compare logic doesn't work properly
|
||||
private static readonly int[] SupportedVersions =
|
||||
{
|
||||
GameVersion.GetVersion(2021, 6, 30), // 2021.6.30
|
||||
GameVersion.GetVersion(2021, 11, 9), // 2021.11.9
|
||||
GameVersion.GetVersion(2021, 12, 14), // 2021.12.14
|
||||
GameVersion.GetVersion(2022, 1, 10), // 2021.2.23
|
||||
GameVersion.GetVersion(2022, 2, 2), // 2022.3.29
|
||||
};
|
||||
|
||||
private readonly ILogger<ClientManager> _logger;
|
||||
@@ -65,10 +67,10 @@ namespace Impostor.Server.Net.Manager
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
|
||||
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode, PlatformSpecificData? platformSpecificData)
|
||||
{
|
||||
var versionCompare = CompareVersion(clientVersion);
|
||||
if (versionCompare != VersionCompareResult.Compatible)
|
||||
if (versionCompare != VersionCompareResult.Compatible || platformSpecificData == null)
|
||||
{
|
||||
GameVersion.ParseVersion(clientVersion, out var year, out var month, out var day, out var revision);
|
||||
_logger.LogTrace("Client connected using unsupported version: {clientVersion} ({version})", clientVersion, $"{year}.{month}.{day}{(revision == 0 ? string.Empty : "." + revision)}");
|
||||
@@ -83,29 +85,23 @@ namespace Impostor.Server.Net.Manager
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.Custom, message);
|
||||
|
||||
await connection.SendAsync(packet);
|
||||
await connection.CustomDisconnectAsync(DisconnectReason.Custom, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.Length > 10)
|
||||
{
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.Custom, DisconnectMessages.UsernameLength);
|
||||
await connection.SendAsync(packet);
|
||||
await connection.CustomDisconnectAsync(DisconnectReason.Custom, DisconnectMessages.UsernameLength);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name) || !name.All(TextBox.IsCharAllowed))
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.Custom, DisconnectMessages.UsernameIllegalCharacters);
|
||||
await connection.SendAsync(packet);
|
||||
await connection.CustomDisconnectAsync(DisconnectReason.Custom, DisconnectMessages.UsernameIllegalCharacters);
|
||||
return;
|
||||
}
|
||||
|
||||
var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode);
|
||||
var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode, platformSpecificData);
|
||||
var id = NextId();
|
||||
|
||||
client.Id = id;
|
||||
|
||||
@@ -62,14 +62,14 @@ namespace Impostor.Server.Net
|
||||
private async ValueTask OnNewConnection(NewConnectionEventArgs e)
|
||||
{
|
||||
// Handshake.
|
||||
HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out _, out var language, out var chatMode);
|
||||
HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out var language, out var chatMode, out var platformSpecificData);
|
||||
|
||||
var connection = new HazelConnection(e.Connection, _connectionLogger);
|
||||
|
||||
await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
|
||||
|
||||
// Register client
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode);
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode, platformSpecificData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,12 @@ namespace Impostor.Server.Net.Redirector
|
||||
int gameVersion,
|
||||
Language language,
|
||||
QuickChatModes chatMode,
|
||||
PlatformSpecificData platformSpecificData,
|
||||
HazelConnection connection,
|
||||
ClientManager clientManager,
|
||||
INodeProvider nodeProvider,
|
||||
INodeLocator nodeLocator)
|
||||
: base(name, gameVersion, language, chatMode, connection)
|
||||
: base(name, gameVersion, language, chatMode, platformSpecificData, connection)
|
||||
{
|
||||
_clientManager = clientManager;
|
||||
_nodeProvider = nodeProvider;
|
||||
@@ -55,27 +56,25 @@ namespace Impostor.Server.Net.Redirector
|
||||
{
|
||||
Message01JoinGameC2S.Deserialize(reader, out var gameCode);
|
||||
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
var endpoint = await _nodeLocator.FindAsync(gameCode);
|
||||
if (endpoint == null)
|
||||
{
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.GameMissing);
|
||||
await DisconnectAsync(DisconnectReason.GameMissing);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
Message13RedirectS2C.Serialize(packet, false, endpoint);
|
||||
await Connection.SendAsync(packet);
|
||||
}
|
||||
|
||||
await Connection.SendAsync(packet);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageFlags.GetGameListV2:
|
||||
{
|
||||
// TODO: Implement.
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.Custom, DisconnectMessages.NotImplemented);
|
||||
await Connection.SendAsync(packet);
|
||||
await DisconnectAsync(DisconnectReason.Custom, DisconnectMessages.NotImplemented);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace Impostor.Server.Net.State
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogError("Couldn't find spawnable object {0}.", objectId);
|
||||
_logger.LogWarning("Couldn't find spawnable object {0}.", objectId);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
case InnerPlayerControl control:
|
||||
{
|
||||
if (GameState != GameStates.Started)
|
||||
if (GameState != GameStates.Started && GameState != GameStates.Starting)
|
||||
{
|
||||
GameNet.GameData?.RemovePlayer(control);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
@@ -78,12 +78,13 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
private void WriteJoinedGameMessage(IMessageWriter message, bool clear, IClientPlayer player)
|
||||
{
|
||||
var playerIds = _players
|
||||
var players = _players
|
||||
.Where(x => x.Value != player)
|
||||
.Select(x => x.Key)
|
||||
.Select(x => x.Value)
|
||||
.ToArray();
|
||||
|
||||
Message07JoinedGameS2C.Serialize(message, clear, Code, player.Client.Id, HostId, playerIds);
|
||||
// TODO: clean up post20220202 when versions before it are no longer supported.
|
||||
Message07JoinedGameS2C.Serialize(message, clear, Code, player.Client.Id, HostId, players, player.Client.GameVersion >= GameVersion.GetVersion(2022, 2, 2));
|
||||
}
|
||||
|
||||
private void WriteAlterGameMessage(IMessageWriter message, bool clear, bool isPublic)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
@@ -103,7 +103,11 @@ namespace Impostor.Server.Net.State
|
||||
foreach (var player in _players.Values)
|
||||
{
|
||||
player.Character?.NetworkTransform.OnPlayerSpawn();
|
||||
await player.Character!.NetworkTransform.SetPositionAsync(player, GameNet.ShipStatus!.GetSpawnLocation(player.Character, PlayerCount, true), Vector2.Zero);
|
||||
|
||||
if (GameNet.ShipStatus != null)
|
||||
{
|
||||
await player.Character!.NetworkTransform.SetPositionAsync(player, GameNet.ShipStatus.GetSpawnLocation(player.Character, PlayerCount, true), Vector2.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
GameState = GameStates.Started;
|
||||
@@ -114,7 +118,7 @@ namespace Impostor.Server.Net.State
|
||||
|
||||
private ValueTask BroadcastJoinMessage(IMessageWriter message, bool clear, ClientPlayer player)
|
||||
{
|
||||
Message01JoinGameS2C.SerializeJoin(message, clear, Code, player.Client.Id, HostId);
|
||||
Message01JoinGameS2C.SerializeJoin(message, clear, Code, player, HostId);
|
||||
|
||||
return SendToAllExceptAsync(message, player.Client.Id);
|
||||
}
|
||||
@@ -123,7 +127,8 @@ namespace Impostor.Server.Net.State
|
||||
{
|
||||
return Players
|
||||
.Where(filter)
|
||||
.Select(p => p.Client.Connection)!;
|
||||
.Select(p => p.Client.Connection)
|
||||
.Where(c => c != null && c.IsConnected)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
@@ -26,6 +28,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Settings.Configuration;
|
||||
|
||||
namespace Impostor.Server
|
||||
{
|
||||
@@ -33,31 +36,9 @@ namespace Impostor.Server
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
#if DEBUG
|
||||
var logLevel = LogEventLevel.Debug;
|
||||
#else
|
||||
var logLevel = LogEventLevel.Information;
|
||||
#endif
|
||||
|
||||
if (args.Contains("--verbose"))
|
||||
{
|
||||
logLevel = LogEventLevel.Verbose;
|
||||
}
|
||||
else if (args.Contains("--errors-only"))
|
||||
{
|
||||
logLevel = LogEventLevel.Error;
|
||||
}
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Is(logLevel)
|
||||
#if DEBUG
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
|
||||
#else
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
#endif
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
.CreateBootstrapLogger();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -116,14 +97,6 @@ namespace Impostor.Server
|
||||
.GetSection(ServerRedirectorConfig.Section)
|
||||
.Get<ServerRedirectorConfig>() ?? new ServerRedirectorConfig();
|
||||
|
||||
var announcementsServer = host.Configuration
|
||||
.GetSection(AnnouncementsServerConfig.Section)
|
||||
.Get<AnnouncementsServerConfig>() ?? new AnnouncementsServerConfig();
|
||||
|
||||
var authServer = host.Configuration
|
||||
.GetSection(AuthServerConfig.Section)
|
||||
.Get<AuthServerConfig>() ?? new AuthServerConfig();
|
||||
|
||||
services.AddSingleton<ServerEnvironment>();
|
||||
services.AddSingleton<IServerEnvironment>(p => p.GetRequiredService<ServerEnvironment>());
|
||||
services.AddSingleton<IDateTimeProvider, RealDateTimeProvider>();
|
||||
@@ -131,8 +104,6 @@ namespace Impostor.Server
|
||||
services.Configure<DebugConfig>(host.Configuration.GetSection(DebugConfig.Section));
|
||||
services.Configure<AntiCheatConfig>(host.Configuration.GetSection(AntiCheatConfig.Section));
|
||||
services.Configure<ServerConfig>(host.Configuration.GetSection(ServerConfig.Section));
|
||||
services.Configure<AnnouncementsServerConfig>(host.Configuration.GetSection(AnnouncementsServerConfig.Section));
|
||||
services.Configure<AuthServerConfig>(host.Configuration.GetSection(AuthServerConfig.Section));
|
||||
services.Configure<ServerRedirectorConfig>(host.Configuration.GetSection(ServerRedirectorConfig.Section));
|
||||
|
||||
if (redirector.Enabled)
|
||||
@@ -218,18 +189,56 @@ namespace Impostor.Server
|
||||
services.AddSingleton<IEventManager, EventManager>();
|
||||
services.AddSingleton<Matchmaker>();
|
||||
services.AddHostedService<MatchmakerService>();
|
||||
|
||||
if (announcementsServer.Enabled)
|
||||
{
|
||||
services.AddHostedService<AnnouncementsService>();
|
||||
}
|
||||
|
||||
if (authServer.Enabled)
|
||||
{
|
||||
services.AddHostedService<AuthService>();
|
||||
}
|
||||
})
|
||||
.UseSerilog()
|
||||
.UseSerilog((context, loggerConfiguration) =>
|
||||
{
|
||||
#if DEBUG
|
||||
var logLevel = LogEventLevel.Debug;
|
||||
#else
|
||||
var logLevel = LogEventLevel.Information;
|
||||
#endif
|
||||
|
||||
if (args.Contains("--verbose"))
|
||||
{
|
||||
logLevel = LogEventLevel.Verbose;
|
||||
}
|
||||
else if (args.Contains("--errors-only"))
|
||||
{
|
||||
logLevel = LogEventLevel.Error;
|
||||
}
|
||||
|
||||
static Assembly? LoadSerilogAssembly(AssemblyLoadContext loadContext, AssemblyName name)
|
||||
{
|
||||
var paths = new[] { AppDomain.CurrentDomain.BaseDirectory, Directory.GetCurrentDirectory() };
|
||||
foreach (var path in paths)
|
||||
{
|
||||
try
|
||||
{
|
||||
return loadContext.LoadFromAssemblyPath(Path.Combine(path, name.Name + ".dll"));
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
AssemblyLoadContext.Default.Resolving += LoadSerilogAssembly;
|
||||
|
||||
loggerConfiguration
|
||||
.MinimumLevel.Is(logLevel)
|
||||
#if DEBUG
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
|
||||
#else
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
#endif
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.ReadFrom.Configuration(context.Configuration, ConfigurationAssemblySource.AlwaysScanDllFiles);
|
||||
|
||||
AssemblyLoadContext.Default.Resolving -= LoadSerilogAssembly;
|
||||
})
|
||||
.UseConsoleLifetime()
|
||||
.UsePluginLoader(pluginConfig);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ namespace Impostor.Server.Recorder
|
||||
private bool _createdGame;
|
||||
private bool _recordAfter;
|
||||
|
||||
public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, HazelConnection connection, PacketRecorder recorder)
|
||||
: base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, connection)
|
||||
public ClientRecorder(ILogger<Client> logger, IOptions<AntiCheatConfig> antiCheatOptions, ClientManager clientManager, ICustomMessageManager<ICustomRootMessage> customMessageManager, GameManager gameManager, string name, int gameVersion, Language language, QuickChatModes chatMode, PlatformSpecificData platformSpecificData, HazelConnection connection, PacketRecorder recorder)
|
||||
: base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, language, chatMode, platformSpecificData, connection)
|
||||
{
|
||||
_recorder = recorder;
|
||||
_isFirst = true;
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
"ListenIp": "0.0.0.0",
|
||||
"ListenPort": 22023
|
||||
},
|
||||
"AnnouncementsServer": {
|
||||
"Enabled": true,
|
||||
"ListenIp": "0.0.0.0",
|
||||
"ListenPort": 22024
|
||||
},
|
||||
"AntiCheat": {
|
||||
"Enabled": true,
|
||||
"BanIpFromGame": true
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.reporters" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.0.3">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Pcap.Net.x64" Version="1.0.4.1">
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PackageReference>
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user