mirror of
https://github.com/Impostor/Impostor.git
synced 2026-10-21 05:37:28 +00:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d86efc1345 | ||
|
|
301a851ec3 | ||
|
|
86c27402e2 | ||
|
|
c63eb3b2d1 | ||
|
|
a1dac9f8b7 | ||
|
|
3065e8df48 | ||
|
|
dc52e478dc | ||
|
|
eb86c3c683 | ||
|
|
e7c83769ef | ||
|
|
c6b8066794 | ||
|
|
1ec5a050ad | ||
|
|
1e7d132f31 | ||
|
|
9543134ef3 | ||
|
|
389c72caa6 | ||
|
|
66b4d860a9 | ||
|
|
dca26cff59 | ||
|
|
3437c86bff | ||
|
|
66fb8426a6 | ||
|
|
4c839e2dd8 | ||
|
|
c6aa596e3b | ||
|
|
d8843db3eb | ||
|
|
b918f25443 | ||
|
|
824359f93b | ||
|
|
046d09b5fa | ||
|
|
5410c71280 | ||
|
|
241e867343 | ||
|
|
11f2ffd24d | ||
|
|
9df01d2cf7 | ||
|
|
d2720442e9 | ||
|
|
ddedffd121 | ||
|
|
2f04f6ea98 | ||
|
|
5f4c40bf36 | ||
|
|
ef4cc5646b | ||
|
|
d894e9adc3 | ||
|
|
c086cf80b1 | ||
|
|
1118a59167 | ||
|
|
5154dd9aa5 | ||
|
|
a621225fcd | ||
|
|
73a5463848 | ||
|
|
02d25a290f |
No files matched your search
+3
-3
@@ -9,14 +9,14 @@ We are always looking for people to help improve Impostor.
|
||||
- Don't commit unnecessary changes to the codebase or debugging code.
|
||||
- Write meaningful commits or squash them.
|
||||
- Please try to follow the code style of the rest of the codebase. An `.editorconfig` file has been provided to keep consistency.
|
||||
- Stylecop is also enabled: please make sure your PR compiles without warnings.
|
||||
|
||||
## Pull requests
|
||||
|
||||
- Only make pull requests to the `dev` branch.
|
||||
- Only implement one feature per pull request to keep it easy to understand.
|
||||
- Expect comments or questions on your pull request from the project maintainers. We try to keep the code as consistent and maintainable as possible.
|
||||
- Each pull request should come from a new branch in your fork, it should have a meaningful name.
|
||||
- Each pull request should come from a new branch in your fork, it should have a meaningful name.
|
||||
- We try to respond to pull requests as fast as possible. If you think we might have missed it, let us know on [Discord](https://discord.gg/Mk3w6Tb).
|
||||
|
||||
|
||||
If you have any questions, let us know.
|
||||
If you have any questions, let us know.
|
||||
@@ -3,14 +3,11 @@
|
||||
[](https://discord.gg/Mk3w6Tb)
|
||||
[](https://ci.appveyor.com/project/Impostor/Impostor/branch/master)
|
||||
|
||||
Impostor is one of the first **Among Us** private servers, written in C#.
|
||||
Impostor is the first working **Among Us** private server, written in C#.
|
||||
|
||||
| Impostor version | Among Us version | Experimental | Download |
|
||||
|-|-|-|-|
|
||||
| 1.1.0 | 2020.09.07 - 2020.09.22 | No | [](https://github.com/Impostor/Impostor/releases/tag/v1.1.0) |
|
||||
| 1.2.2 | 2020.09.22 - 2020.11.17 | No | [](https://github.com/Impostor/Impostor/releases/tag/v1.2.2) |
|
||||
| 1.3.0 | 2021.3.5 | No | [](https://github.com/Impostor/Impostor/releases/tag/v1.3.0) |
|
||||
| 1.4.0 | 2021.3.31 - 2021.4.2 | Yes | [](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts) |
|
||||
As Among Us is actively updated, you may want to use the latest build from the master branch if you want to play on the latest version of Among Us that supported by Impostor.
|
||||
|
||||
If you want to play on an older version of Among Us, you can download old releases from [here](https://github.com/Impostor/Impostor/releases/), however these versions are less supported.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -22,34 +19,7 @@ Impostor is one of the first **Among Us** private servers, written in C#.
|
||||
|
||||
### Client
|
||||
|
||||
If you just want to play on a server hosted by someone else, you need to follow these instructions.
|
||||
|
||||
#### Windows
|
||||
|
||||
1. Find the [latest release](https://github.com/AeonLucid/Impostor/releases/latest).
|
||||
2. Download `Impostor-Patcher-win-x64.zip`.
|
||||
3. Extract the zip.
|
||||
4. Run `Impostor.Patcher.exe`.
|
||||
5. Follow the instructions inside the application.
|
||||
|
||||

|
||||
|
||||
If you do not wish to execute any programs. Follow the instructions in [this website](https://impostor.github.io/Impostor)
|
||||
|
||||
#### Android
|
||||
|
||||
##### Android 10 and below.
|
||||
1. Go to [this website](https://impostor.github.io/Impostor) **(ON YOUR MOBILE DEVICE)**
|
||||
2. Follow the instructions listed there.
|
||||
|
||||
##### Android 11.
|
||||
1. Connect your phone to a computer. Go to [this website](https://impostor.github.io/Impostor) on the computer and follow the steps 1 and 2 to generate a `regionInfo.dat` file.
|
||||
2. Instead of following the next steps, open the phone's internal storage on your computer and navigate to `/sdcard/Android/data/com.innersloth.spacemafia/files`.
|
||||
3. Copy the generated `regionInfo.dat` file into the `files` folder you just navigated to.
|
||||
|
||||
#### iOS
|
||||
|
||||
iOS devices need to be jailbroken in order to connect to Impostor servers.
|
||||
If you just want to play on a server hosted by someone else, you need to configure your client to connect to an Impostor server. Please go to [this website](https://impostor.github.io/Impostor) to set up your client by following the instructions for your OS there.
|
||||
|
||||
### Server
|
||||
|
||||
@@ -57,7 +27,7 @@ See the [docs](docs/Running-the-server.md) for instructions on how to set it up.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [TROUBLESHOOTING](docs/TROUBLESHOOTING.md) to solve issues the Impostor client or the server.
|
||||
See [TROUBLESHOOTING](docs/TROUBLESHOOTING.md) to solve issues with the Impostor client or the server.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -114,13 +114,6 @@ Task("Build")
|
||||
DotNetCoreBuild("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetCoreBuildSettings {
|
||||
Configuration = configuration,
|
||||
});
|
||||
|
||||
// Client.
|
||||
ImpostorPublishNF("Impostor-Patcher", "./src/Impostor.Patcher/Impostor.Patcher.WinForms/Impostor.Patcher.WinForms.csproj");
|
||||
|
||||
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "win-x64");
|
||||
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "osx-x64");
|
||||
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "linux-x64");
|
||||
|
||||
// Server.
|
||||
ImpostorPublish("Impostor-Server", "./src/Impostor.Server/Impostor.Server.csproj", "win-x64", true);
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
# Building from source
|
||||
|
||||
The solution contains two main projects, the Impostor server and patcher. The server is built using [.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0) and the winforms patcher is with [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472).
|
||||
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).
|
||||
|
||||
## Cloning Impostor
|
||||
|
||||
You need to clone Impostor using git.
|
||||
You need to clone Impostor using git:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/AeonLucid/Impostor.git
|
||||
git clone https://github.com/Impostor/Impostor.git
|
||||
```
|
||||
|
||||
## Building the server
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
|
||||
- [Rider](https://www.jetbrains.com/rider/) or [Visual Studio](https://visualstudio.microsoft.com/vs/) (Optional, only if you want the full IDE experience)
|
||||
|
||||
@@ -21,16 +22,8 @@ git clone https://github.com/AeonLucid/Impostor.git
|
||||
```bash
|
||||
cd src/Impostor.Server/
|
||||
dotnet build
|
||||
# Or if you want a single file, ready for production: (change linux-x64 to win-x64 if you use Windows)
|
||||
dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true
|
||||
```
|
||||
|
||||
To setup the server, please look at [Running the server](Running-the-server.md).
|
||||
|
||||
## Building the winforms patcher
|
||||
|
||||
### Dependencies
|
||||
* [.NET Framework 4.7.2 Developer Pack](https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer) or [Mono](https://www.mono-project.com/download/)
|
||||
|
||||
### Build using the CLI
|
||||
```bash
|
||||
cd src/Impostor.Patcher/Impostor.Patcher.WinForms
|
||||
dotnet build
|
||||
```
|
||||
+7
-3
@@ -1,11 +1,15 @@
|
||||
# Frequently Answered Questions
|
||||
|
||||
## What is this?
|
||||
The Impostor project is a reverse engineered and open sourced server for the game Among Us. The game itself is developed by [InnerSloth](http://www.innersloth.com/) while this project is maintained by the community. This project was built out of frustration for the lack of server availability in certain regions and the inability for the core developer, [AeonLucid](https://github.com/AeonLucid), to join a public game. As of this time, it has not been officially endorsed by the studio.
|
||||
|
||||
The Impostor project is a reverse engineered and open source server for the game Among Us. The game itself is developed by [Innersloth](http://www.innersloth.com/) while this project is maintained by the community. This project was built out of frustration for the lack of server availability in certain regions and the inability for the core developer, [AeonLucid](https://github.com/AeonLucid), to join a public game. As of this time, it has not been officially endorsed by the studio.
|
||||
|
||||
## Can this be used with the mobile version of the game?
|
||||
Yes, Impostor can be used with both the Android and iOS\* versions of the game.
|
||||
###### \* In order to play on an Impostor server with iOS, you _must_ have a jailbroken device.
|
||||
|
||||
Yes, Impostor can be used with both the Android and iOS versions of the game. You can add a custom server via a link, you can do this using the [site](https://impostor.github.io/Impostor)
|
||||
|
||||
## How can I get started?
|
||||
|
||||
See [Setting up your Server](Running-the-server.md) for more information on running the server and [Client Setup](https://impostor.github.io/Impostor/) for helping your friends join in!
|
||||
|
||||
If you're having issues with the server software, check the [Troubleshooting page](TROUBLESHOOTING.md).
|
||||
+15
-14
@@ -1,30 +1,31 @@
|
||||
# Running the server
|
||||
|
||||
There are currently two modes to run the Impostor server in. The first way is the simplest one and is the one you should probably use. The other way will distribute players across other servers and is a more advanced configuration.
|
||||
There are currently two modes to run the Impostor server. The first way, Single Server, is the simplest one, can handle up to a few hundred simultaneous players, and is the one you should probably use. Multi-Server mode will distribute players across other servers and is intended for advanced users.
|
||||
|
||||
## Single server
|
||||
|
||||
### Without docker
|
||||
1. Install the **.NET 5.0 runtime**.
|
||||
- [Windows x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.0-windows-x64-installer)
|
||||
- [Linux x64](https://docs.microsoft.com/en-us/dotnet/core/install/linux)
|
||||
- [macOS x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.0-macos-x64-installer)
|
||||
2. Find the [latest master release](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts).
|
||||
3. Download either the Windows or the Linux version.
|
||||
### 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.
|
||||
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)*
|
||||
6. Run `Impostor.Server.exe` (Windows) / `Impostor.Server` (Linux)
|
||||
6. Run `Impostor.Server` (Linux/macOS) or `Impostor.Server.exe` (Windows)
|
||||
|
||||
### Using docker
|
||||
### Using Docker
|
||||
|
||||
[](https://hub.docker.com/repository/docker/aeonlucid/impostor)
|
||||
[](https://hub.docker.com/repository/docker/aeonlucid/impostor)
|
||||
[](https://hub.docker.com/r/aeonlucid/impostor)
|
||||
[](https://hub.docker.com/r/aeonlucid/impostor)
|
||||
|
||||
After installing Docker, you can just start a Docker container with `docker run`:
|
||||
|
||||
```
|
||||
docker run -p 22023:22023/udp aeonlucid/impostor:nightly
|
||||
```
|
||||
|
||||
### Using docker-compose
|
||||
### Using Docker Compose
|
||||
|
||||
```
|
||||
version: '3.4'
|
||||
|
||||
@@ -42,7 +43,7 @@ services:
|
||||
|
||||
## Multiple servers
|
||||
|
||||
Follow the steps from the single server on two or more servers.
|
||||
Follow the steps from the single server on two or more servers. You should only need to set this up if you have a very large server and can no longer fit everyone on one server: if this is your first time installing Impostor, use a single server.
|
||||
|
||||
### Master server
|
||||
|
||||
|
||||
@@ -1,35 +1,72 @@
|
||||
# Server configuration
|
||||
|
||||
Some information about all the possible configurations. Click [here](https://github.com/AeonLucid/Impostor/blob/master/src/Impostor.Server/config.full.json) to see all the possible config options.
|
||||
Some information about all the possible configurations. Click [here](https://github.com/Impostor/Impostor/blob/master/src/Impostor.Server/config-full.json) to see all the possible config options.
|
||||
|
||||
## Options
|
||||
|
||||
### Required Server Configuration
|
||||
|
||||
| Key | Default | Description |
|
||||
|-|-|-|
|
||||
| **PublicIp** | `127.0.0.1` | This needs to the public IPv4 address of the server which you give to others to connect. You can find your IPv4 address [on this website](http://whatismyip.host/). Unless you are only planning to use Impostor privately, on your local network, you should change this to your public ip. It is also possible to use hostnames instead of IPv4 addresses, which will be resolved to IPv4 addresses. |
|
||||
| **PublicPort** | `22023` | The public port of the server which you give to others to connect. (**This is the external port you configure on your router when port forwarding.**) Usually `22023`. |
|
||||
| **ListenIp** | `0.0.0.0` | The network interface to listen on. If you do not know what to put here, use `0.0.0.0`. Since 1.2.2 it is also possible to use hostnames instead of IPv4 addresses, these must resolve to a valid IPv4 address. |
|
||||
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. |
|
||||
| Key | Default | Description |
|
||||
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **PublicIp** | `127.0.0.1` | This needs to the public IPv4 address of the server which you give to others to connect. You can find your IPv4 address [on this website](http://whatismyip.host/). Unless you are only planning to use Impostor privately, on your local network, you should change this to your public ip. It is also possible to use hostnames instead of IPv4 addresses, which will be resolved to IPv4 addresses. |
|
||||
| **PublicPort** | `22023` | The public port of the server which you give to others to connect. (**This is the external port you configure on your router when port forwarding.**) Usually `22023`. |
|
||||
| **ListenIp** | `0.0.0.0` | The network interface to listen on. If you do not know what to put here, use `0.0.0.0`. Since 1.2.2 it is also possible to use hostnames instead of IPv4 addresses, these must resolve to a valid IPv4 address. |
|
||||
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. |
|
||||
|
||||
### AntiCheat
|
||||
|
||||
| Key | Default | Value |
|
||||
|-|-|-|
|
||||
| **Enabled** | `true` | Whether the anticheat should be enabled. |
|
||||
| **BanIpFromGame** | `true` | When 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. **(Setting this to false does not disable the anti-cheat!)** |
|
||||
Impostor has an Anticheat that makes it possible to kick cheaters from games automatically. Note that the anticheat is tuned on the vanilla version of the game, so client-side modifications could trigger the Anticheat if you're playing with them.
|
||||
|
||||
| Key | Default | Value |
|
||||
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **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.
|
||||
|
||||
| Key | Default | Value |
|
||||
| ----------------------- | ------- | -------------------------------------------- |
|
||||
| **GameRecorderEnabled** | `false` | Enables the Game Recorder. |
|
||||
| **GameRecorderPath** | *empty* | Path where the recorded games will be saved. |
|
||||
|
||||
### ServerRedirector
|
||||
In a multi-node setup these need to be specified.
|
||||
| Key | Default | Value |
|
||||
|-|-|-|
|
||||
| **Enabled** | `false` | Whether the server runs in multi-node setup. If this is `false`, all other options in this section do not have any effect. |
|
||||
| **Master** | `false` | Whether the current server is a master. A master is responsible for redirecting clients to nodes |
|
||||
| **Locator** | | Fill in either `Redis` or `UdpMasterEndpoint` to choose which method to use for locating other nodes. This must be the same across all servers. |
|
||||
| **>Redis** | | Format `127.0.0.1.6379`, you can also use a password like so: `127.0.0.1.6379,password=value`. |
|
||||
| **>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. |
|
||||
|
||||
In a multi-node setup these values need to be specified. Note that most people do not need to run a Multi-node setup.
|
||||
|
||||
| Key | Default | Value |
|
||||
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Enabled** | `false` | Whether the server runs in multi-node setup. If this is `false`, all other options in this section do not have any effect. |
|
||||
| **Master** | `false` | Whether the current server is a master. A master is responsible for redirecting clients to nodes |
|
||||
| **Locator** | | Fill in either `Redis` or `UdpMasterEndpoint` to choose which method to use for locating other nodes. This must be the same across all servers. |
|
||||
| **>Redis** | | Format `127.0.0.1.6379`, you can also use a password like so: `127.0.0.1.6379,password=value`. |
|
||||
| **>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. |
|
||||
|
||||
## Config providers
|
||||
|
||||
@@ -37,30 +74,9 @@ In a multi-node setup these need to be specified.
|
||||
|
||||
The simplest option to configure is by using the `config.json` file next to the server executable. For all possible options see the [config-full.json](https://github.com/Impostor/Impostor/blob/dev/src/Impostor.Server/config-full.json) file.
|
||||
|
||||
### Command line arguments
|
||||
|
||||
TODO
|
||||
|
||||
```
|
||||
Server:PublicIp=127.0.0.1
|
||||
Server:PublicPort=22023
|
||||
Server:ListenIp=0.0.0.0
|
||||
Server:ListenPort=22023
|
||||
AntiCheat:Enabled=true
|
||||
AntiCheat:BanIpFromGame=true
|
||||
ServerRedirector:Enabled=false
|
||||
ServerRedirector:Master=true
|
||||
ServerRedirector:Locator:Redis=127.0.0.1.6379
|
||||
ServerRedirector:Locator:UdpMasterEndpoint=127.0.0.1:32320
|
||||
ServerRedirector:Nodes:0:Ip=127.0.0.1
|
||||
ServerRedirector:Nodes:0:Port=22024
|
||||
ServerRedirector:Nodes:1:Ip=127.0.0.1
|
||||
ServerRedirector:Nodes:1:Port=22025
|
||||
```
|
||||
|
||||
### Environment variables
|
||||
|
||||
TODO
|
||||
If you're unable to edit the config.json file, you can instead set Environment variables to configure Impostor. The general pattern for each variable is `IMPOSTOR_SectionName__VariableName`. For example, to disable the anticheat, you can set the environment variable `IMPOSTOR_AntiCheat__Enabled=false`. Here are some more examples:
|
||||
|
||||
```
|
||||
IMPOSTOR_Server__PublicIp=127.0.0.1
|
||||
|
||||
+38
-9
@@ -1,28 +1,57 @@
|
||||
# Troubleshooting
|
||||
|
||||
If you're reading this, something went wrong.
|
||||
Don't worry though, as this is the most thorough guide to help you!
|
||||
|
||||
## `./Impostor.Server: line 1: ELF: not found` (plus other errors)
|
||||
|
||||
No idea where you got that system. But we clearly do **NOT** support it.
|
||||
|
||||
## `cannot execute binary file: Exec format error`
|
||||
Please check that you have downloaded the right version of Impostor, as we mantain two CPU architectures (x64 and ARM).
|
||||
Unless you are running Impostor on a SBC (Single-Board Computer), like the Raspberry Pi, you most likely want to use the x64 version.
|
||||
|
||||
Please check that you have downloaded the right version of Impostor, as we mantain two CPU architectures (x64 and ARM).
|
||||
Unless you are running Impostor on a computer like the Raspberry Pi, you most likely want to use the x64 version.
|
||||
|
||||
## `./Impostor.Server: Permission denied`
|
||||
|
||||
This is an error related to Linux file permissions.
|
||||
Some files do not hold their executable bit (the permission that allows them to run) during a download.
|
||||
You can solve this by doing: `chmod +x Impostor.Server`
|
||||
|
||||
## `You are using an older version of the game`
|
||||
You are using an older version of Impostor. The game does not really check who is outdated and blames it on the user.
|
||||
Make sure you got the latest working version of Impostor (probably in AppVeyor, not Github).
|
||||
## Disconnected with `Your client is too new, please update your Impostor to play`
|
||||
|
||||
## `You disconnected from the server. Reliable Packet 1 ...`
|
||||
Please double-check that you have followed the [Server Configuration](Server-configuration.md) correctly.
|
||||
**NOTE: Your public ip does not start with `127` nor `192`**
|
||||
This happens when your game is too new for your copy of Impostor to support.
|
||||
|
||||
If you're using an Impostor release, check if there is a newer release available that supports your version. If this doesn't exist, you can try the [latest build from Appveyor](https://ci.appveyor.com/project/Impostor/Impostor/branch/master).
|
||||
|
||||
If even that build doesn't work, you can try looking through the Pull Requests to see if there is a pending pull request that aims to update it to the next version, but note these may be unstable and may contain malicious code, as they haven't been vetted by the Impostor development team yet.
|
||||
|
||||
## Disconnected with `Please update your game to play on this server`
|
||||
|
||||
This happens when you're using a version of Impostor that is too new for your game version. You can download an older version from [the GitHub release page](https://github.com/Impostor/Impostor/releases). We however don't support older versions of Impostor with new API's and bugfixes so we recommend to update your game instead.
|
||||
|
||||
## Disconnected with `You are using an older version of the game`
|
||||
|
||||
You are using a version of Impostor that is not designed for the version of the game you're playing. The game does not really check who is outdated and blames it on the user. Newer versions (v1.5.0+) do warn correctly and send the correct message.
|
||||
|
||||
Look at which version of the game you're playing, which you can see in the top left corner of the main menu, then download an Impostor version for that game. Every [release on the release page](https://github.com/Impostor/Impostor/releases) shows which version of the game it is compatible with. If your game version is newer than the latest release, check if the [latest build from AppVeyor works](https://ci.appveyor.com/project/Impostor/Impostor/branch/master).
|
||||
|
||||
## Disconnected with `You disconnected from the server. Reliable Packet 1 ...`
|
||||
|
||||
Please double-check that you have followed the [Server Configuration](Server-configuration.md) correctly.
|
||||
**NOTE: Your public ip does not start with `10`, `127` or `192`**
|
||||
Also check if the port Impostor (ListenPort) is listening on is correctly port-forwarded for UDP (or TCP/UDP).
|
||||
|
||||
## `Could not load file or assembly...`
|
||||
Please check that you only have **working** plugins in the `plugins` folder.
|
||||
|
||||
Please check that you only have **working** plugins in the `plugins` folder.
|
||||
This error can be caused by non-plugin files or plugins that are not working correctly.
|
||||
|
||||
## My question is not yet answered and I'm still having problems!
|
||||
|
||||
That's unfortunate. Join the [Impostor Discord](https://discord.gg/Mk3w6Tb), ask your question there and we'll try to help you out. Note that we're not always available, so it may take some time to get an answer. To make answering your question easier, please add the following details:
|
||||
|
||||
- Which version of Impostor are you using?
|
||||
- Which version or Among Us are you using?
|
||||
- Which operating system are you running Impostor on?
|
||||
- If you have console logs, attaching these will also help.
|
||||
@@ -31,12 +31,11 @@ The first step is creating a new C# project, it must be a **Class Library (.NET
|
||||
|
||||
- .NET Standard 2.0
|
||||
- .NET Standard 2.1
|
||||
- .NET Core 3.1
|
||||
- .NET 5
|
||||
|
||||
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** until .NET 5 is released officially. This should give you enough functionality. If not, upgrade to .NET Core 3.1.
|
||||
> 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.
|
||||
|
||||
When the project has been created, you should have `Class.cs` and `Project.csproj` files. Your `Project.csproj` should look something like this.
|
||||
|
||||
@@ -62,16 +61,13 @@ Make sure to get a prerelease if you are writing a plugin for a dev release of t
|
||||
1. Right click your project.
|
||||
2. Click `Manage NuGet Packages`.
|
||||
3. Click `Browse`.
|
||||
4. Next to the search bar, enable `Include prerelease`.
|
||||
5. Search for `Impostor.Api`.
|
||||
6. Click the `Impostor.Api` result and press install on the right side.
|
||||
4. Search for `Impostor.Api`.
|
||||
5. Click the `Impostor.Api` result and press install on the right side.
|
||||
|
||||
### Dotnet CLI
|
||||
|
||||
> Make sure to grab the latest (pre-)release version from NuGet [here](https://www.nuget.org/packages/Impostor.Api).
|
||||
|
||||
1. Open your project folder in command prompt / bash.
|
||||
2. Run `dotnet add package Impostor.Api -v "1.2.0-ci.58"`.
|
||||
2. Run `dotnet add package Impostor.Api`.
|
||||
|
||||
## 4. The plugin class
|
||||
|
||||
|
||||
@@ -144,6 +144,12 @@ dotnet_naming_style.end_in_async.word_separator =
|
||||
# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}'
|
||||
dotnet_diagnostic.RS2008.severity = none
|
||||
|
||||
# IDE0005: Remove unnecessary import
|
||||
dotnet_diagnostic.IDE0005.severity = warning
|
||||
|
||||
# IDE0007: Use `var` instead of explicit type
|
||||
dotnet_diagnostic.IDE0007.severity = warning
|
||||
|
||||
# IDE0035: Remove unreachable code
|
||||
dotnet_diagnostic.IDE0035.severity = warning
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.4.0</VersionPrefix>
|
||||
<VersionPrefix>1.6.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -9,11 +9,6 @@ namespace Impostor.Api.Events.Announcements
|
||||
{
|
||||
public interface IResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets FreeWeekendState, currently unused by the client.
|
||||
/// </summary>
|
||||
public FreeWeekendState FreeWeekendState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether announcement should be loaded from client's cache, can save some bytes.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,10 +2,12 @@ using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Events.Player
|
||||
{
|
||||
public enum VoteType : sbyte
|
||||
public enum VoteType : byte
|
||||
{
|
||||
ForceSkip = -2,
|
||||
Skip = -1,
|
||||
HasNotVoted = 255,
|
||||
Missed = 254,
|
||||
Skipped = 253,
|
||||
Dead = 252,
|
||||
Player = 0,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,5 +14,11 @@
|
||||
Brown = 9,
|
||||
Cyan = 10,
|
||||
Lime = 11,
|
||||
Maroon = 12,
|
||||
Rose = 13,
|
||||
Banana = 14,
|
||||
Gray = 15,
|
||||
Tan = 16,
|
||||
Coral = 17,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
namespace Impostor.Api.Innersloth.Customization
|
||||
{
|
||||
public enum HatType : uint
|
||||
{
|
||||
@@ -109,13 +109,13 @@
|
||||
ToppatHenry = 104,
|
||||
ToppatEllie = 105,
|
||||
GeoffreyPlumb = 106,
|
||||
AngryEyebrows = 107,
|
||||
ChocolateIce = 108,
|
||||
HeartPin = 109,
|
||||
Ponytail = 110,
|
||||
RubberGlove = 111,
|
||||
UnicornHorn = 112,
|
||||
Zipper = 113,
|
||||
RightHandMan = 114,
|
||||
RightHandMan = 107,
|
||||
AngryEyebrows = 108,
|
||||
ChocolateIce = 109,
|
||||
HeartPin = 110,
|
||||
Ponytail = 111,
|
||||
RubberGlove = 112,
|
||||
UnicornHorn = 113,
|
||||
Zipper = 114,
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
[Flags]
|
||||
public enum FreeWeekendState : byte
|
||||
{
|
||||
NotFree,
|
||||
FreeMIRA,
|
||||
FreePolus,
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,21 @@ namespace Impostor.Api.Innersloth
|
||||
public enum GameKeywords : uint
|
||||
{
|
||||
All = 0,
|
||||
Other = 1,
|
||||
Spanish = 2,
|
||||
English = 256,
|
||||
SpanishLA = 2,
|
||||
Brazilian = 2048,
|
||||
Portuguese = 16,
|
||||
Korean = 4,
|
||||
Russian = 8,
|
||||
Portuguese = 16,
|
||||
Dutch = 4096,
|
||||
Filipino = 64,
|
||||
French = 8192,
|
||||
German = 16384,
|
||||
Italian = 32768,
|
||||
Japanese = 512,
|
||||
SpanishEU = 1024,
|
||||
Arabic = 32,
|
||||
Filipone = 64,
|
||||
Polish = 128,
|
||||
English = 256,
|
||||
Other = 1,
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,20 @@
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class GameVersion
|
||||
public static class GameVersion
|
||||
{
|
||||
public static int GetVersion(int year, int month, int day, int rev = 0)
|
||||
public static int GetVersion(int year, int month, int day, int revision = 0)
|
||||
{
|
||||
return (year * 25000) + (month * 1800) + (day * 50) + rev;
|
||||
return (year * 25000) + (month * 1800) + (day * 50) + revision;
|
||||
}
|
||||
|
||||
public static void ParseVersion(int version, out int year, out int month, out int day, out int revision)
|
||||
{
|
||||
year = version / 25000;
|
||||
version %= 25000;
|
||||
month = version / 1800;
|
||||
version %= 1800;
|
||||
day = version / 50;
|
||||
revision = version % 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,17 @@ namespace Impostor.Api.Innersloth
|
||||
public enum Language
|
||||
{
|
||||
English,
|
||||
Spanish,
|
||||
SpanishLA,
|
||||
Brazilian,
|
||||
Portuguese,
|
||||
Korean,
|
||||
Russian,
|
||||
Dutch,
|
||||
Filipino,
|
||||
French,
|
||||
German,
|
||||
Italian,
|
||||
Japanese,
|
||||
SpanishEU,
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ namespace Impostor.Api.Innersloth.Maps
|
||||
{
|
||||
new AirshipTask(AirshipTask.Ids.ElectricalFixWiring, TaskTypes.FixWiring, TaskCategories.CommonTask),
|
||||
new AirshipTask(AirshipTask.Ids.MeetingRoomEnterIDCode, TaskTypes.EnterIdCode, TaskCategories.CommonTask),
|
||||
new AirshipTask(AirshipTask.Ids.ElectricalCalibrateDistributor, TaskTypes.CalibrateDistributor, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.ElectricalResetBreakers, TaskTypes.ResetBreakers, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.VaultRoomDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.BrigDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
|
||||
@@ -50,8 +51,9 @@ namespace Impostor.Api.Innersloth.Maps
|
||||
new AirshipTask(AirshipTask.Ids.MainHallDevelopPhotos, TaskTypes.DevelopPhotos, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.CargoBayFuelEngines, TaskTypes.FuelEngines, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.SecurityRewindTapes, TaskTypes.RewindTapes, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.LoungeEmptyGarbage, TaskTypes.EmptyGarbage, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.ShowersEmptyGarbage, TaskTypes.EmptyGarbage, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.VaultRoomPolishRuby, TaskTypes.PolishRuby, TaskCategories.ShortTask),
|
||||
new AirshipTask(AirshipTask.Ids.ElectricalCalibrateDistributor, TaskTypes.CalibrateDistributor, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.CockpitStabilizeSteering, TaskTypes.StabilizeSteering, TaskCategories.ShortTask),
|
||||
new AirshipTask(AirshipTask.Ids.ArmoryDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
|
||||
new AirshipTask(AirshipTask.Ids.CockpitDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
|
||||
@@ -74,6 +76,7 @@ namespace Impostor.Api.Innersloth.Maps
|
||||
new AirshipTask(AirshipTask.Ids.MainHallDecontaminate, TaskTypes.Decontaminate, TaskCategories.ShortTask),
|
||||
new AirshipTask(AirshipTask.Ids.KitchenMakeBurger, TaskTypes.MakeBurger, TaskCategories.ShortTask),
|
||||
new AirshipTask(AirshipTask.Ids.ShowersFixShower, TaskTypes.FixShower, TaskCategories.ShortTask),
|
||||
new AirshipTask(AirshipTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
|
||||
};
|
||||
|
||||
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace Impostor.Api.Innersloth.Maps
|
||||
new MiraTask(MiraTask.Ids.OfficeProcessData, TaskTypes.ProcessData, TaskCategories.ShortTask),
|
||||
new MiraTask(MiraTask.Ids.LaunchpadRunDiagnostics, TaskTypes.RunDiagnostics, TaskCategories.LongTask),
|
||||
new MiraTask(MiraTask.Ids.ReactorUnlockManifolds, TaskTypes.UnlockManifolds, TaskCategories.ShortTask),
|
||||
new MiraTask(MiraTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
|
||||
};
|
||||
|
||||
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace Impostor.Api.Innersloth.Maps
|
||||
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToNavigation, TaskTypes.DivertPower, TaskCategories.ShortTask),
|
||||
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToLowerEngine, TaskTypes.DivertPower, TaskCategories.ShortTask),
|
||||
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToSecurity, TaskTypes.DivertPower, TaskCategories.ShortTask),
|
||||
new SkeldTask(SkeldTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
|
||||
};
|
||||
|
||||
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
|
||||
|
||||
@@ -15,44 +15,47 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
|
||||
{
|
||||
ElectricalFixWiring = 0,
|
||||
MeetingRoomEnterIDCode = 1,
|
||||
ElectricalResetBreakers = 2,
|
||||
VaultRoomDownloadData = 3,
|
||||
BrigDownloadData = 4,
|
||||
CargoBayDownloadData = 5,
|
||||
GapRoomDownloadData = 6,
|
||||
RecordsDownloadData = 7,
|
||||
CargoBayUnlockSafe = 8,
|
||||
VentilationStartFans = 9,
|
||||
MainHallEmptyGarbage = 10,
|
||||
MedicalEmptyGarbage = 11,
|
||||
KitchenEmptyGarbage = 12,
|
||||
MainHallDevelopPhotos = 13,
|
||||
CargoBayFuelEngines = 14,
|
||||
SecurityRewindTapes = 15,
|
||||
VaultRoomPolishRuby = 16,
|
||||
ElectricalCalibrateDistributor = 17,
|
||||
CockpitStabilizeSteering = 18,
|
||||
ArmoryDownloadData = 19,
|
||||
CockpitDownloadData = 20,
|
||||
CommsDownloadData = 21,
|
||||
MedicalDownloadData = 22,
|
||||
ViewingDeckDownloadData = 23,
|
||||
ElectricalDivertPowerToArmory = 24,
|
||||
ElectricalDivertPowerToCockpit = 25,
|
||||
ElectricalDivertPowerToGapRoom = 26,
|
||||
ElectricalDivertPowerToMainHall = 27,
|
||||
ElectricalDivertPowerToMeetingRoom = 28,
|
||||
ElectricalDivertPowerToShowers = 29,
|
||||
ElectricalDivertPowerToEngine = 30,
|
||||
ShowersPickUpTowels = 31,
|
||||
LoungeCleanToilet = 32,
|
||||
VaultRoomDressMannequin = 33,
|
||||
RecordsSortRecords = 34,
|
||||
ArmoryPutAwayPistols = 35,
|
||||
ArmoryPutAwayRifles = 36,
|
||||
MainHallDecontaminate = 37,
|
||||
KitchenMakeBurger = 38,
|
||||
ShowersFixShower = 39,
|
||||
ElectricalCalibrateDistributor = 2,
|
||||
ElectricalResetBreakers = 3,
|
||||
VaultRoomDownloadData = 4,
|
||||
BrigDownloadData = 5,
|
||||
CargoBayDownloadData = 6,
|
||||
GapRoomDownloadData = 7,
|
||||
RecordsDownloadData = 8,
|
||||
CargoBayUnlockSafe = 9,
|
||||
VentilationStartFans = 10,
|
||||
MainHallEmptyGarbage = 11,
|
||||
MedicalEmptyGarbage = 12,
|
||||
KitchenEmptyGarbage = 13,
|
||||
MainHallDevelopPhotos = 14,
|
||||
CargoBayFuelEngines = 15,
|
||||
SecurityRewindTapes = 16,
|
||||
LoungeEmptyGarbage = 17,
|
||||
ShowersEmptyGarbage = 18,
|
||||
VaultRoomPolishRuby = 19,
|
||||
CockpitStabilizeSteering = 20,
|
||||
ArmoryDownloadData = 21,
|
||||
CockpitDownloadData = 22,
|
||||
CommsDownloadData = 23,
|
||||
MedicalDownloadData = 24,
|
||||
ViewingDeckDownloadData = 25,
|
||||
ElectricalDivertPowerToArmory = 26,
|
||||
ElectricalDivertPowerToCockpit = 27,
|
||||
ElectricalDivertPowerToGapRoom = 28,
|
||||
ElectricalDivertPowerToMainHall = 29,
|
||||
ElectricalDivertPowerToMeetingRoom = 30,
|
||||
ElectricalDivertPowerToShowers = 31,
|
||||
ElectricalDivertPowerToEngine = 32,
|
||||
ShowersPickUpTowels = 33,
|
||||
LoungeCleanToilet = 34,
|
||||
VaultRoomDressMannequin = 35,
|
||||
RecordsSortRecords = 36,
|
||||
ArmoryPutAwayPistols = 37,
|
||||
ArmoryPutAwayRifles = 38,
|
||||
MainHallDecontaminate = 39,
|
||||
KitchenMakeBurger = 40,
|
||||
ShowersFixShower = 41,
|
||||
CleanVent = 42,
|
||||
}
|
||||
|
||||
public Ids Id { get; }
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
|
||||
OfficeProcessData = 23,
|
||||
LaunchpadRunDiagnostics = 24,
|
||||
ReactorUnlockManifolds = 25,
|
||||
CleanVent = 26,
|
||||
}
|
||||
|
||||
public Ids Id { get; }
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
|
||||
ElectricalDivertPowerToNavigation = 26,
|
||||
ElectricalDivertPowerToLowerEngine = 27,
|
||||
ElectricalDivertPowerToSecurity = 28,
|
||||
CleanVent = 29,
|
||||
}
|
||||
|
||||
public Ids Id { get; }
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class RegionInfo
|
||||
{
|
||||
public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers)
|
||||
{
|
||||
Name = name;
|
||||
Ping = ping;
|
||||
Servers = servers;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string Ping { get; }
|
||||
|
||||
public IReadOnlyList<ServerInfo> Servers { get; }
|
||||
|
||||
public static RegionInfo Deserialize(BinaryReader reader)
|
||||
{
|
||||
var unknown = reader.ReadInt32();
|
||||
var name = reader.ReadString();
|
||||
var ping = reader.ReadString();
|
||||
var servers = new List<ServerInfo>();
|
||||
var serverCount = reader.ReadInt32();
|
||||
|
||||
for (var i = 0; i < serverCount; i++)
|
||||
{
|
||||
servers.Add(ServerInfo.Deserialize(reader));
|
||||
}
|
||||
|
||||
return new RegionInfo(name, ping, servers);
|
||||
}
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(0);
|
||||
writer.Write(Name);
|
||||
writer.Write(Ping);
|
||||
writer.Write(Servers.Count);
|
||||
|
||||
foreach (var server in Servers)
|
||||
{
|
||||
server.Serialize(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace Impostor.Api.Innersloth
|
||||
{
|
||||
public class ServerInfo
|
||||
{
|
||||
public ServerInfo(string name, string ip, ushort port)
|
||||
{
|
||||
Name = name;
|
||||
Ip = ip;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string Ip { get; }
|
||||
|
||||
public ushort Port { get; }
|
||||
|
||||
public static ServerInfo Deserialize(BinaryReader reader)
|
||||
{
|
||||
var name = reader.ReadString();
|
||||
var ip = new IPAddress(reader.ReadBytes(4)).ToString();
|
||||
var port = reader.ReadUInt16();
|
||||
var unknown = reader.ReadInt32();
|
||||
|
||||
return new ServerInfo(name, ip, port);
|
||||
}
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Name);
|
||||
writer.Write(IPAddress.Parse(Ip).GetAddressBytes());
|
||||
writer.Write(Port);
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,5 +62,6 @@ namespace Impostor.Api.Innersloth
|
||||
GetBiggolSword = 57,
|
||||
PutAwayRifles = 58,
|
||||
StopCharles = 59,
|
||||
VentCleaning = 60,
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,16 @@ namespace Impostor.Api.Net
|
||||
/// </remarks>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the language.
|
||||
/// </summary>
|
||||
Language Language { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quick chat mode.
|
||||
/// </summary>
|
||||
QuickChatModes ChatMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection of the client.
|
||||
/// </summary>
|
||||
|
||||
@@ -34,5 +34,8 @@
|
||||
SetTasks = 29,
|
||||
ClimbLadder = 31,
|
||||
UsePlatform = 32,
|
||||
SendQuickChat = 33,
|
||||
BootFromVent = 34,
|
||||
UpdateSystem = 35,
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Announcements
|
||||
{
|
||||
public static class Message02SetFreeWeekend
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, FreeWeekendState state)
|
||||
{
|
||||
writer.StartMessage(AnnouncementsMessageFlags.SetFreeWeekend);
|
||||
writer.Write((byte)state);
|
||||
writer.EndMessage();
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out FreeWeekendState state)
|
||||
{
|
||||
state = (FreeWeekendState)reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ namespace Impostor.Api.Net.Messages
|
||||
{
|
||||
public const byte UseCache = 0;
|
||||
public const byte SetUpdate = 1;
|
||||
public const byte SetFreeWeekend = 2;
|
||||
public const byte AddLanguages = 4;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,40 @@
|
||||
using Impostor.Api.Innersloth;
|
||||
|
||||
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)
|
||||
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out uint? lastNonceReceived, out Language language, out QuickChatModes chatMode)
|
||||
{
|
||||
clientVersion = reader.ReadInt32();
|
||||
name = reader.ReadString();
|
||||
lastNonceReceived = reader.ReadUInt32();
|
||||
|
||||
if (clientVersion >= Version.V1)
|
||||
{
|
||||
lastNonceReceived = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastNonceReceived = null;
|
||||
}
|
||||
|
||||
if (clientVersion >= Version.V2)
|
||||
{
|
||||
language = (Language)reader.ReadUInt32();
|
||||
chatMode = (QuickChatModes)reader.ReadByte();
|
||||
}
|
||||
else
|
||||
{
|
||||
language = Language.English;
|
||||
chatMode = QuickChatModes.FreeChatOrQuickChat;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Version
|
||||
{
|
||||
public static readonly int V1 = GameVersion.GetVersion(2021, 4, 25);
|
||||
|
||||
public static readonly int V2 = GameVersion.GetVersion(2021, 6, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc23VotingComplete
|
||||
@@ -11,9 +9,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
writer.Write(tie);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out ReadOnlyMemory<byte> states, out byte playerId, out bool tie)
|
||||
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie)
|
||||
{
|
||||
states = reader.ReadBytesAndSize();
|
||||
var length = reader.ReadPackedInt32();
|
||||
states = new IMessageReader[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
states[i] = reader.ReadMessage();
|
||||
}
|
||||
|
||||
playerId = reader.ReadByte();
|
||||
tie = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ namespace Impostor.Api.Net.Messages.Rpcs
|
||||
writer.Write(suspectPlayerId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out byte playerId, out sbyte suspectPlayerId)
|
||||
public static void Deserialize(IMessageReader reader, out byte playerId, out byte suspectPlayerId)
|
||||
{
|
||||
playerId = reader.ReadByte();
|
||||
suspectPlayerId = reader.ReadSByte();
|
||||
suspectPlayerId = reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc34BootFromVent
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, int ventId)
|
||||
{
|
||||
writer.WritePacked(ventId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, out int ventId)
|
||||
{
|
||||
ventId = reader.ReadPackedInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net.Inner.Objects;
|
||||
|
||||
namespace Impostor.Api.Net.Messages.Rpcs
|
||||
{
|
||||
public static class Rpc35UpdateSystem
|
||||
{
|
||||
public static void Serialize(IMessageWriter writer, SystemTypes systemType, IInnerPlayerControl playerControl, ushort sequenceId, byte state, byte ventId)
|
||||
{
|
||||
writer.Write((byte)systemType);
|
||||
writer.Write(playerControl.NetId);
|
||||
writer.Write(sequenceId);
|
||||
writer.Write(state);
|
||||
writer.Write(ventId);
|
||||
}
|
||||
|
||||
public static void Deserialize(IMessageReader reader, IGame game, out SystemTypes systemType, out IInnerPlayerControl? playerControl, out ushort sequenceId, out byte state, out byte ventId)
|
||||
{
|
||||
systemType = (SystemTypes)reader.ReadByte();
|
||||
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
|
||||
sequenceId = reader.ReadUInt16();
|
||||
state = reader.ReadByte();
|
||||
ventId = reader.ReadByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Api.Plugins
|
||||
{
|
||||
public interface IPluginInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique id of this plugin.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the human readable name of this plugin.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the author of this plugin.
|
||||
/// </summary>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of this plugin.
|
||||
/// </summary>
|
||||
string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional startup object of this plugin.
|
||||
/// </summary>
|
||||
IPluginStartup? Startup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of this plugin.
|
||||
/// </summary>
|
||||
IPlugin? Instance { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance type of this plugin.
|
||||
/// </summary>
|
||||
Type PluginType { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Api.Plugins
|
||||
{
|
||||
public interface IPluginManager
|
||||
{
|
||||
IReadOnlyList<IPluginInformation> Plugins { get; }
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Impostor.Benchmarks.Data
|
||||
{
|
||||
public class MessageReader_Bytes_Pooled
|
||||
{
|
||||
private static ConcurrentQueue<MessageReader_Bytes_Pooled> _readers;
|
||||
private static readonly ConcurrentQueue<MessageReader_Bytes_Pooled> _readers;
|
||||
|
||||
static MessageReader_Bytes_Pooled()
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Impostor.Benchmarks.Data
|
||||
{
|
||||
public MessageType SendOption { get; private set; }
|
||||
|
||||
private Stack<int> messageStarts = new Stack<int>();
|
||||
private readonly Stack<int> _messageStarts = new Stack<int>();
|
||||
|
||||
public MessageWriter(byte[] buffer)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace Impostor.Benchmarks.Data
|
||||
///
|
||||
public void StartMessage(byte typeFlag)
|
||||
{
|
||||
messageStarts.Push(this.Position);
|
||||
_messageStarts.Push(this.Position);
|
||||
this.Position += 2; // Skip for size
|
||||
this.Write(typeFlag);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ namespace Impostor.Benchmarks.Data
|
||||
///
|
||||
public void EndMessage()
|
||||
{
|
||||
var lastMessageStart = messageStarts.Pop();
|
||||
var lastMessageStart = _messageStarts.Pop();
|
||||
var length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte
|
||||
this.Buffer[lastMessageStart] = (byte)length;
|
||||
this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
|
||||
@@ -95,13 +95,13 @@ namespace Impostor.Benchmarks.Data
|
||||
///
|
||||
public void CancelMessage()
|
||||
{
|
||||
this.Position = this.messageStarts.Pop();
|
||||
this.Position = this._messageStarts.Pop();
|
||||
this.Length = this.Position;
|
||||
}
|
||||
|
||||
public void Clear(MessageType sendOption)
|
||||
{
|
||||
this.messageStarts.Clear();
|
||||
this._messageStarts.Clear();
|
||||
this.SendOption = sendOption;
|
||||
this.Buffer[0] = (byte)sendOption;
|
||||
switch (sendOption)
|
||||
@@ -293,7 +293,7 @@ namespace Impostor.Benchmarks.Data
|
||||
this.Write(msg.Buffer, offset, msg.Length - offset);
|
||||
}
|
||||
|
||||
public unsafe static bool IsLittleEndian()
|
||||
public static unsafe bool IsLittleEndian()
|
||||
{
|
||||
byte b;
|
||||
unsafe
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>Impostor</AssemblyTitle>
|
||||
<Product>Impostor</Product>
|
||||
<Copyright>Copyright © AeonLucid 2020</Copyright>
|
||||
<Version>1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Impostor.Cli</AssemblyName>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Impostor.Patcher.Shared\Impostor.Patcher.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20478.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Patcher.Shared;
|
||||
using Impostor.Patcher.Shared.Events;
|
||||
|
||||
namespace Impostor.Patcher.Cli
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static readonly AmongUsModifier Modifier = new AmongUsModifier();
|
||||
|
||||
internal static Task<int> Main(string[] args)
|
||||
{
|
||||
var rootCommand = new RootCommand
|
||||
{
|
||||
new Option<string>(
|
||||
"--address",
|
||||
"IP Address of the server, will prompt if not specified"
|
||||
),
|
||||
new Option<string>(
|
||||
"--name",
|
||||
() => AmongUsModifier.DefaultRegionName,
|
||||
"Name for server region"
|
||||
),
|
||||
};
|
||||
|
||||
rootCommand.Handler = CommandHandler.Create<string, string>((address, name) =>
|
||||
{
|
||||
Modifier.RegionName = name;
|
||||
Modifier.Error += ModifierOnError;
|
||||
Modifier.Saved += ModifierOnSaved;
|
||||
|
||||
Console.WriteLine("Welcome to Impostor");
|
||||
|
||||
if (Modifier.TryLoadRegionInfo(out var regionInfo))
|
||||
{
|
||||
Console.WriteLine($"Currently selected region: {regionInfo.Name} ({regionInfo.Ping}, {regionInfo.Servers.Count} server(s))");
|
||||
}
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
return Modifier.SaveIpAsync(address);
|
||||
}
|
||||
|
||||
return PromptAsync();
|
||||
});
|
||||
|
||||
return rootCommand.InvokeAsync(args);
|
||||
}
|
||||
|
||||
private static void ModifierOnSaved(object sender, SavedEventArgs e)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine("The IP Address was saved, please (re)start Among Us.");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
private static void WriteError(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
private static void ModifierOnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
WriteError(e.Message);
|
||||
}
|
||||
|
||||
private static async Task PromptAsync()
|
||||
{
|
||||
Console.WriteLine("Please enter in the IP Address of the server you would like to use for Among Us");
|
||||
Console.WriteLine("If you want to stop playing on the server, simply select another region");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.Write("> ");
|
||||
|
||||
if (await Modifier.SaveIpAsync(Console.ReadLine()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Gameloop.Vdf;
|
||||
using Gameloop.Vdf.Linq;
|
||||
using Impostor.Patcher.Shared.Events;
|
||||
using Impostor.Patcher.Shared.Innersloth;
|
||||
using ErrorEventArgs = Impostor.Patcher.Shared.Events.ErrorEventArgs;
|
||||
|
||||
namespace Impostor.Patcher.Shared
|
||||
{
|
||||
public class AmongUsModifier
|
||||
{
|
||||
private const uint AppId = 945360;
|
||||
public const string DefaultRegionName = "Impostor";
|
||||
public const ushort DefaultPort = 22023;
|
||||
|
||||
private readonly string _amongUsDir;
|
||||
private readonly string _regionFile;
|
||||
|
||||
public string RegionName { get; set; } = DefaultRegionName;
|
||||
|
||||
public AmongUsModifier()
|
||||
{
|
||||
var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "LocalLow");
|
||||
|
||||
if (!Directory.Exists(appData))
|
||||
{
|
||||
appData = FindProtonAppData();
|
||||
}
|
||||
|
||||
if (appData == null)
|
||||
return;
|
||||
|
||||
_amongUsDir = Path.Combine(appData, "Innersloth", "Among Us");
|
||||
_regionFile = Path.Combine(_amongUsDir, "regionInfo.dat");
|
||||
}
|
||||
|
||||
private string FindProtonAppData()
|
||||
{
|
||||
string steamApps;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".steam", "steam", "steamapps");
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Steam", "steamapps");
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(steamApps))
|
||||
return null;
|
||||
|
||||
var libraries = new List<string>
|
||||
{
|
||||
steamApps,
|
||||
};
|
||||
|
||||
var vdf = Path.Combine(steamApps, "libraryfolders.vdf");
|
||||
if (File.Exists(vdf))
|
||||
{
|
||||
var libraryFolders = VdfConvert.Deserialize(File.ReadAllText(vdf));
|
||||
|
||||
foreach (var libraryFolder in libraryFolders.Value.Children<VProperty>())
|
||||
{
|
||||
if (!int.TryParse(libraryFolder.Key, out _))
|
||||
continue;
|
||||
|
||||
libraries.Add(Path.Combine(libraryFolder.Value.Value<string>(), "steamapps"));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var library in libraries)
|
||||
{
|
||||
var path = Path.Combine(library, "compatdata", AppId.ToString(), "pfx", "drive_c", "users", "steamuser", "AppData", "LocalLow");
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<bool> SaveIpAsync(string input)
|
||||
{
|
||||
// Filter out whitespace.
|
||||
input = input.Trim();
|
||||
|
||||
// Split port from ip.
|
||||
// Only IPv4 is supported so just do it simple.
|
||||
var ip = string.Empty;
|
||||
var port = DefaultPort;
|
||||
|
||||
var parts = input.Split(':');
|
||||
if (parts.Length >= 1)
|
||||
{
|
||||
ip = parts[0];
|
||||
}
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
ushort.TryParse(parts[1], out port);
|
||||
}
|
||||
|
||||
// Check if a valid IP address was entered.
|
||||
if (!IPAddress.TryParse(ip, out var ipAddress))
|
||||
{
|
||||
// Attempt to resolve DNS.
|
||||
try
|
||||
{
|
||||
var hostAddresses = await Dns.GetHostAddressesAsync(ip);
|
||||
if (hostAddresses.Length == 0)
|
||||
{
|
||||
OnError("Invalid IP Address entered");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use first IPv4 result.
|
||||
ipAddress = hostAddresses.First(x => x.AddressFamily == AddressFamily.InterNetwork);
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
OnError("Failed to resolve hostname.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only IPv4.
|
||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
OnError("Invalid IP Address entered, only IPv4 is allowed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = WriteIp(ipAddress, port);
|
||||
OnSaved(ip, port);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an IP Address to the Among Us region file.
|
||||
/// </summary>
|
||||
/// <param name="ipAddress">The IPv4 address to write.</param>
|
||||
/// <param name="port"></param>
|
||||
private bool WriteIp(IPAddress ipAddress, ushort port)
|
||||
{
|
||||
if (ipAddress == null ||
|
||||
ipAddress.AddressFamily != AddressFamily.InterNetwork)
|
||||
{
|
||||
throw new ArgumentException(nameof(ipAddress));
|
||||
}
|
||||
|
||||
if (!Directory.Exists(_amongUsDir))
|
||||
{
|
||||
OnError("Among Us directory was not found, is it installed? Try running it once.");
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var file = File.Open(_regionFile, FileMode.Create, FileAccess.Write))
|
||||
using (var writer = new BinaryWriter(file))
|
||||
{
|
||||
var ip = ipAddress.ToString();
|
||||
var region = new RegionInfo(RegionName, ip, new[]
|
||||
{
|
||||
new ServerInfo($"{RegionName}-Master-1", ip, port),
|
||||
});
|
||||
|
||||
region.Serialize(writer);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the existing region info from the Among Us.
|
||||
/// </summary>
|
||||
public bool TryLoadRegionInfo(out RegionInfo regionInfo)
|
||||
{
|
||||
regionInfo = null;
|
||||
|
||||
if (!File.Exists(_regionFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var file = File.Open(_regionFile, FileMode.Open, FileAccess.Read))
|
||||
using (var reader = new BinaryReader(file))
|
||||
{
|
||||
try
|
||||
{
|
||||
regionInfo = RegionInfo.Deserialize(reader);
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnError("Couldn't parse region info\n" + exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the existing IP Address from the Among Us region file
|
||||
/// if it was set by Impostor before.
|
||||
/// </summary>
|
||||
public bool TryLoadIp(out string ipAddress)
|
||||
{
|
||||
ipAddress = null;
|
||||
|
||||
if (!TryLoadRegionInfo(out var regionInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((regionInfo.Name == RegionName || regionInfo.Name == DefaultRegionName) && regionInfo.Servers.Count >= 1)
|
||||
{
|
||||
ipAddress = regionInfo.Servers.ElementAt(0).Ip;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnError(string message)
|
||||
{
|
||||
Error?.Invoke(this, new ErrorEventArgs(message));
|
||||
}
|
||||
|
||||
private void OnSaved(string ipAddress, ushort port)
|
||||
{
|
||||
Saved?.Invoke(this, new SavedEventArgs(ipAddress, port));
|
||||
}
|
||||
|
||||
public event EventHandler<ErrorEventArgs> Error;
|
||||
public event EventHandler<SavedEventArgs> Saved;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Impostor.Patcher.Shared
|
||||
{
|
||||
public class Configuration
|
||||
{
|
||||
private const string FileRecentIps = @"recent_ips.txt";
|
||||
private const int MaxRecentIps = 5;
|
||||
|
||||
private readonly string _baseDir;
|
||||
private readonly string _recentIpsPath;
|
||||
private readonly List<string> _recentIps;
|
||||
|
||||
public Configuration()
|
||||
{
|
||||
var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
|
||||
|
||||
_baseDir = Path.Combine(appData, "Impostor");
|
||||
_recentIpsPath = Path.Combine(_baseDir, FileRecentIps);
|
||||
_recentIps = new List<string>();
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> RecentIps => _recentIps;
|
||||
|
||||
public void Load()
|
||||
{
|
||||
if (File.Exists(_recentIpsPath))
|
||||
{
|
||||
_recentIps.AddRange(File.ReadAllLines(_recentIpsPath));
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
Directory.CreateDirectory(_baseDir);
|
||||
|
||||
if (!Directory.Exists(_baseDir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_recentIps.Count > 0)
|
||||
{
|
||||
File.WriteAllLines(_recentIpsPath, _recentIps);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddIp(string ip)
|
||||
{
|
||||
if (_recentIps.Contains(ip))
|
||||
{
|
||||
_recentIps.Remove(ip);
|
||||
}
|
||||
|
||||
_recentIps.Insert(0, ip);
|
||||
|
||||
if (_recentIps.Count > MaxRecentIps)
|
||||
{
|
||||
_recentIps.RemoveAt(MaxRecentIps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Patcher.Shared.Events
|
||||
{
|
||||
public class ErrorEventArgs : EventArgs
|
||||
{
|
||||
public ErrorEventArgs(string message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public string Message { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Impostor.Patcher.Shared.Events
|
||||
{
|
||||
public class SavedEventArgs : EventArgs
|
||||
{
|
||||
public SavedEventArgs(string ipAddress, ushort port)
|
||||
{
|
||||
IpAddress = ipAddress;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public string IpAddress { get; }
|
||||
public ushort Port { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gameloop.Vdf" Version="0.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,48 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Impostor.Patcher.Shared.Innersloth
|
||||
{
|
||||
public class RegionInfo
|
||||
{
|
||||
public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers)
|
||||
{
|
||||
Name = name;
|
||||
Ping = ping;
|
||||
Servers = servers;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string Ping { get; }
|
||||
public IReadOnlyList<ServerInfo> Servers { get; }
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(0);
|
||||
writer.Write(Name);
|
||||
writer.Write(Ping);
|
||||
writer.Write(Servers.Count);
|
||||
|
||||
foreach (var server in Servers)
|
||||
{
|
||||
server.Serialize(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public static RegionInfo Deserialize(BinaryReader reader)
|
||||
{
|
||||
var unknown = reader.ReadInt32();
|
||||
var name = reader.ReadString();
|
||||
var ping = reader.ReadString();
|
||||
var servers = new List<ServerInfo>();
|
||||
var serverCount = reader.ReadInt32();
|
||||
|
||||
for (var i = 0; i < serverCount; i++)
|
||||
{
|
||||
servers.Add(ServerInfo.Deserialize(reader));
|
||||
}
|
||||
|
||||
return new RegionInfo(name, ping, servers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace Impostor.Patcher.Shared.Innersloth
|
||||
{
|
||||
public class ServerInfo
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Ip { get; }
|
||||
public ushort Port { get; }
|
||||
|
||||
public ServerInfo(string name, string ip, ushort port)
|
||||
{
|
||||
Name = name;
|
||||
Ip = ip;
|
||||
Port = port;
|
||||
}
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Name);
|
||||
writer.Write(IPAddress.Parse(Ip).GetAddressBytes());
|
||||
writer.Write(Port);
|
||||
writer.Write(0);
|
||||
}
|
||||
|
||||
public static ServerInfo Deserialize(BinaryReader reader)
|
||||
{
|
||||
var name = reader.ReadString();
|
||||
var ip = new IPAddress(reader.ReadBytes(4)).ToString();
|
||||
var port = reader.ReadUInt16();
|
||||
var unknown = reader.ReadInt32();
|
||||
|
||||
return new ServerInfo(name, ip, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -1,141 +0,0 @@
|
||||
namespace Impostor.Patcher.WinForms.Forms
|
||||
{
|
||||
partial class FrmMain
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMain));
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.buttonLaunch = new System.Windows.Forms.Button();
|
||||
this.lblUrl = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.comboIp = new System.Windows.Forms.ComboBox();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label1.Location = new System.Drawing.Point(28, 139);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(60, 13);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "IP Address";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label2.Location = new System.Drawing.Point(28, 23);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(225, 91);
|
||||
this.label2.TabIndex = 0;
|
||||
this.label2.Text = "Welcome to Impostor\r\n\r\nPlease enter in the IP Address of the \r\nserver you would l" +
|
||||
"ike to use for Among Us\r\n\r\nIf you want to stop playing on the server, \r\nsimply s" +
|
||||
"elect another region";
|
||||
//
|
||||
// buttonLaunch
|
||||
//
|
||||
this.buttonLaunch.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.buttonLaunch.Location = new System.Drawing.Point(179, 155);
|
||||
this.buttonLaunch.Name = "buttonLaunch";
|
||||
this.buttonLaunch.Size = new System.Drawing.Size(74, 22);
|
||||
this.buttonLaunch.TabIndex = 3;
|
||||
this.buttonLaunch.Text = "Save";
|
||||
this.buttonLaunch.UseVisualStyleBackColor = true;
|
||||
this.buttonLaunch.Click += new System.EventHandler(this.buttonLaunch_Click);
|
||||
//
|
||||
// lblUrl
|
||||
//
|
||||
this.lblUrl.AutoSize = true;
|
||||
this.lblUrl.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||
this.lblUrl.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.lblUrl.ForeColor = System.Drawing.SystemColors.Highlight;
|
||||
this.lblUrl.Location = new System.Drawing.Point(39, 232);
|
||||
this.lblUrl.Name = "lblUrl";
|
||||
this.lblUrl.Size = new System.Drawing.Size(212, 13);
|
||||
this.lblUrl.TabIndex = 5;
|
||||
this.lblUrl.Text = "https://github.com/Impostor/Impostor";
|
||||
this.lblUrl.Click += new System.EventHandler(this.lblUrl_Click);
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label3.Location = new System.Drawing.Point(54, 216);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(182, 13);
|
||||
this.label3.TabIndex = 6;
|
||||
this.label3.Text = "Source code and latest versions at\r\n";
|
||||
//
|
||||
// comboIp
|
||||
//
|
||||
this.comboIp.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.comboIp.FormattingEnabled = true;
|
||||
this.comboIp.Location = new System.Drawing.Point(31, 155);
|
||||
this.comboIp.Name = "comboIp";
|
||||
this.comboIp.Size = new System.Drawing.Size(141, 21);
|
||||
this.comboIp.TabIndex = 2;
|
||||
this.comboIp.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textIp_KeyDown);
|
||||
//
|
||||
// FrmMain
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(283, 262);
|
||||
this.Controls.Add(this.comboIp);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.lblUrl);
|
||||
this.Controls.Add(this.buttonLaunch);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.ForeColor = System.Drawing.SystemColors.ControlText;
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "FrmMain";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Impostor";
|
||||
this.Load += new System.EventHandler(this.FrmMain_Load);
|
||||
this.Shown += new System.EventHandler(this.FrmMain_Shown);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Button buttonLaunch;
|
||||
private System.Windows.Forms.Label lblUrl;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.ComboBox comboIp;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using Impostor.Patcher.Shared;
|
||||
using Impostor.Patcher.Shared.Events;
|
||||
|
||||
namespace Impostor.Patcher.WinForms.Forms
|
||||
{
|
||||
public partial class FrmMain : Form
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AmongUsModifier _modifier;
|
||||
|
||||
public FrmMain()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
AcceptButton = buttonLaunch;
|
||||
|
||||
_config = new Configuration();
|
||||
_modifier = new AmongUsModifier();
|
||||
_modifier.Error += ModifierOnError;
|
||||
_modifier.Saved += ModifierOnSaved;
|
||||
}
|
||||
|
||||
private void ModifierOnError(object sender, ErrorEventArgs e)
|
||||
{
|
||||
MessageBox.Show(e.Message, "Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error);
|
||||
|
||||
comboIp.Text = string.Empty;
|
||||
comboIp.Focus();
|
||||
|
||||
comboIp.Enabled = true;
|
||||
buttonLaunch.Enabled = true;
|
||||
}
|
||||
|
||||
private void ModifierOnSaved(object sender, SavedEventArgs e)
|
||||
{
|
||||
MessageBox.Show("The IP Address was saved, please (re)start Among Us.", "Success",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
|
||||
var ipText = e.Port == AmongUsModifier.DefaultPort
|
||||
? e.IpAddress
|
||||
: $"{e.IpAddress}:{e.Port}";
|
||||
|
||||
comboIp.Text = ipText;
|
||||
comboIp.Enabled = true;
|
||||
buttonLaunch.Enabled = true;
|
||||
|
||||
_config.AddIp(ipText);
|
||||
_config.Save();
|
||||
|
||||
RefreshComboIps();
|
||||
}
|
||||
|
||||
private void FrmMain_Load(object sender, EventArgs e)
|
||||
{
|
||||
_config.Load();
|
||||
|
||||
RefreshComboIps();
|
||||
|
||||
if (_modifier.TryLoadIp(out var ipAddress))
|
||||
{
|
||||
comboIp.Text = ipAddress;
|
||||
}
|
||||
}
|
||||
|
||||
private void FrmMain_Shown(object sender, EventArgs e)
|
||||
{
|
||||
comboIp.Focus();
|
||||
}
|
||||
|
||||
private void textIp_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode != Keys.Enter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
buttonLaunch_Click(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private async void buttonLaunch_Click(object sender, EventArgs e)
|
||||
{
|
||||
comboIp.Enabled = false;
|
||||
buttonLaunch.Enabled = false;
|
||||
|
||||
await _modifier.SaveIpAsync(comboIp.Text);
|
||||
}
|
||||
|
||||
private void lblUrl_Click(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("https://github.com/Impostor/Impostor");
|
||||
}
|
||||
|
||||
private void RefreshComboIps()
|
||||
{
|
||||
comboIp.Items.Clear();
|
||||
|
||||
if (_config.RecentIps.Count > 0)
|
||||
{
|
||||
foreach (var ip in _config.RecentIps)
|
||||
{
|
||||
comboIp.Items.Add(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,22 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Impostor</AssemblyName>
|
||||
<ProjectGuid>{804CF172-0C87-4423-9688-BD97D549891E}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Copyright>Copyright © AeonLucid 2020</Copyright>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Impostor.Patcher.Shared\Impostor.Patcher.Shared.csproj" />
|
||||
<PackageReference Include="System.Resources.Extensions" Version="5.0.0" />
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using Impostor.Patcher.WinForms.Forms;
|
||||
|
||||
namespace Impostor.Patcher.WinForms
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
[STAThread]
|
||||
private static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new FrmMain());
|
||||
}
|
||||
}
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Impostor.Patcher.WinForms.Properties
|
||||
{
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder",
|
||||
"4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance",
|
||||
"CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState
|
||||
.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp =
|
||||
new global::System.Resources.ResourceManager("Impostor.Client.WinForms.Properties.Resources",
|
||||
typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState
|
||||
.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get { return resourceCulture; }
|
||||
set { resourceCulture = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Impostor.Patcher.Properties
|
||||
{
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute(
|
||||
"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
private static Settings defaultInstance =
|
||||
((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get { return defaultInstance; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 130 KiB |
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
@@ -12,21 +11,17 @@ namespace Impostor.Plugins.Example
|
||||
{
|
||||
private readonly ILogger<ExamplePlugin> _logger;
|
||||
private readonly IGameManager _gameManager;
|
||||
private readonly IPluginManager _pluginManager;
|
||||
|
||||
public ExamplePlugin(ILogger<ExamplePlugin> logger, IGameManager gameManager, IPluginManager pluginManager)
|
||||
public ExamplePlugin(ILogger<ExamplePlugin> logger, IGameManager gameManager)
|
||||
{
|
||||
_logger = logger;
|
||||
_gameManager = gameManager;
|
||||
_pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public override async ValueTask EnableAsync()
|
||||
{
|
||||
_logger.LogInformation("Example is being enabled.");
|
||||
|
||||
_logger.LogInformation("Following plugins are installed: {@plugins}", _pluginManager.Plugins.Select(x => $"{x.Id} ({x.Version})"));
|
||||
|
||||
var game = await _gameManager.CreateAsync(new GameOptionsData());
|
||||
if (game == null)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Impostor.Plugins.Example
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IEventListener, GameEventListener>();
|
||||
services.AddSingleton<IEventListener, ClientEventListener>();
|
||||
services.AddSingleton<IEventListener, PlayerEventListener>();
|
||||
services.AddSingleton<IEventListener, MeetingEventListener>();
|
||||
services.AddSingleton<IEventListener, AnnouncementsListener>();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Events.Client;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Impostor.Plugins.Example.Handlers
|
||||
{
|
||||
public class ClientEventListener : IEventListener
|
||||
{
|
||||
private readonly ILogger<ClientEventListener> _logger;
|
||||
|
||||
public ClientEventListener(ILogger<ClientEventListener> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[EventListener]
|
||||
public void OnClientConnected(IClientConnectedEvent e)
|
||||
{
|
||||
_logger.LogInformation("Client {name} > connected (language: {language}, chat mode: {chatMode})", e.Client.Name, e.Client.Language, e.Client.ChatMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
{
|
||||
public const string Error = "There was an internal server error. " +
|
||||
"Check the server console for more information. " +
|
||||
"Please report the issue on the AmongUsServer GitHub if it keeps happening.";
|
||||
"Please report the issue on the Impostor GitHub if it keeps happening.";
|
||||
|
||||
public const string Destroyed = "The game you tried to join is being destroyed. " +
|
||||
"Please create a new game.";
|
||||
@@ -15,5 +15,11 @@
|
||||
public const string UsernameLength = "Your username is too long, please make it shorter.";
|
||||
|
||||
public const string UsernameIllegalCharacters = "Your username contains illegal characters, please remove them.";
|
||||
|
||||
public const string VersionClientTooOld = "Please update your game to play on this server.";
|
||||
|
||||
public const string VersionServerTooOld = "Your client is too new, please update your Impostor server to play.";
|
||||
|
||||
public const string VersionUnsupported = "Your client version is unsupported, please update your Game and/or Impostor server.";
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ namespace Impostor.Server.Events.Announcements
|
||||
|
||||
public class AnnouncementResponse : IAnnouncementRequestEvent.IResponse
|
||||
{
|
||||
public FreeWeekendState FreeWeekendState { get; set; } = FreeWeekendState.NotFree;
|
||||
|
||||
public bool UseCached { get; set; } = false;
|
||||
|
||||
public Announcement? Announcement { get; set; } = null;
|
||||
|
||||
@@ -26,12 +26,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
|
||||
<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.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="5.0.4" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.1.2" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.333">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Net.Messages.Announcements;
|
||||
using Impostor.Hazel;
|
||||
@@ -101,15 +100,6 @@ namespace Impostor.Server.Net
|
||||
_logger.LogDebug("Sent ({Id}) {Message}", announcement.Id, announcement.Message);
|
||||
}
|
||||
|
||||
if (response.FreeWeekendState != FreeWeekendState.NotFree)
|
||||
{
|
||||
using var writer = MessageWriter.Get(MessageType.Reliable);
|
||||
Message02SetFreeWeekend.Serialize(writer, response.FreeWeekendState);
|
||||
await e.Connection.SendAsync(writer);
|
||||
|
||||
_logger.LogDebug("Sent {FreeWeekendState} weekend state", response.FreeWeekendState);
|
||||
}
|
||||
|
||||
await e.Connection.Disconnect(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,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, IHazelConnection connection)
|
||||
: base(name, gameVersion, connection)
|
||||
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)
|
||||
{
|
||||
_logger = logger;
|
||||
_antiCheatConfig = antiCheatOptions.Value;
|
||||
|
||||
@@ -13,10 +13,12 @@ namespace Impostor.Server.Net
|
||||
{
|
||||
internal abstract class ClientBase : IClient
|
||||
{
|
||||
protected ClientBase(string name, int gameVersion, IHazelConnection connection)
|
||||
protected ClientBase(string name, int gameVersion, Language language, QuickChatModes chatMode, IHazelConnection connection)
|
||||
{
|
||||
Name = name;
|
||||
GameVersion = gameVersion;
|
||||
Language = language;
|
||||
ChatMode = chatMode;
|
||||
Connection = connection;
|
||||
Items = new ConcurrentDictionary<object, object>();
|
||||
}
|
||||
@@ -25,6 +27,10 @@ namespace Impostor.Server.Net
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Language Language { get; }
|
||||
|
||||
public QuickChatModes ChatMode { get; }
|
||||
|
||||
public int GameVersion { get; }
|
||||
|
||||
public IHazelConnection Connection { get; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -14,9 +15,9 @@ namespace Impostor.Server.Net.Factories
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public ClientBase Create(IHazelConnection connection, string name, int clientVersion)
|
||||
public ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
|
||||
{
|
||||
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, connection);
|
||||
var client = ActivatorUtilities.CreateInstance<TClient>(_serviceProvider, name, clientVersion, language, chatMode, connection);
|
||||
connection.Client = client;
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
|
||||
namespace Impostor.Server.Net.Factories
|
||||
{
|
||||
internal interface IClientFactory
|
||||
{
|
||||
ClientBase Create(IHazelConnection connection, string name, int clientVersion);
|
||||
ClientBase Create(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
private readonly ObjectPool<PlayerMovementEvent> _pool;
|
||||
|
||||
private ushort _lastSequenceId;
|
||||
private bool _spawnSnapAllowed;
|
||||
private AirshipSpawnState _spawnState;
|
||||
|
||||
public InnerCustomNetworkTransform(ICustomMessageManager<ICustomRpc> customMessageManager, Game game, ILogger<InnerCustomNetworkTransform> logger, InnerPlayerControl playerControl, IEventManager eventManager, ObjectPool<PlayerMovementEvent> pool) : base(customMessageManager, game)
|
||||
{
|
||||
@@ -36,6 +36,13 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
_pool = pool;
|
||||
}
|
||||
|
||||
private enum AirshipSpawnState : byte
|
||||
{
|
||||
PreSpawn,
|
||||
SelectingSpawn,
|
||||
Spawned,
|
||||
}
|
||||
|
||||
public Vector2 Position { get; private set; }
|
||||
|
||||
public Vector2 Velocity { get; private set; }
|
||||
@@ -98,15 +105,17 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
|
||||
if (Game.GameNet.ShipStatus is InnerAirshipStatus airshipStatus)
|
||||
{
|
||||
// As part of airship spawning, clients are sending snap to -25 40 for no reason(?), cancelling it works just fine
|
||||
if (Approximately(position, airshipStatus.PreSpawnLocation))
|
||||
// As part of airship spawning, clients are sending snap to -25 40 to move themself out of view
|
||||
if (_spawnState == AirshipSpawnState.PreSpawn && Approximately(position, airshipStatus.PreSpawnLocation))
|
||||
{
|
||||
return false;
|
||||
_spawnState = AirshipSpawnState.SelectingSpawn;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_spawnSnapAllowed && airshipStatus.SpawnLocations.Any(location => Approximately(position, location)))
|
||||
// Once the spawn has been selected, the client sends a second snap to the select spawn location
|
||||
if (_spawnState == AirshipSpawnState.SelectingSpawn && airshipStatus.SpawnLocations.Any(location => Approximately(position, location)))
|
||||
{
|
||||
_spawnSnapAllowed = false;
|
||||
_spawnState = AirshipSpawnState.Spawned;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -152,7 +161,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
|
||||
internal void OnPlayerSpawn()
|
||||
{
|
||||
_spawnSnapAllowed = true;
|
||||
_spawnState = AirshipSpawnState.PreSpawn;
|
||||
}
|
||||
|
||||
private static bool SidGreaterThan(ushort newSid, ushort prevSid)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
@@ -38,7 +37,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
|
||||
public override async ValueTask<bool> HandleRpcAsync(ClientPlayer sender, ClientPlayer? target, RpcCalls call, IMessageReader reader)
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender))
|
||||
if (call != RpcCalls.BootFromVent && !await ValidateOwnership(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -47,6 +46,7 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
{
|
||||
case RpcCalls.EnterVent:
|
||||
case RpcCalls.ExitVent:
|
||||
{
|
||||
if (!await ValidateImpostor(call, sender, _playerControl.PlayerInfo))
|
||||
{
|
||||
return false;
|
||||
@@ -87,6 +87,13 @@ namespace Impostor.Server.Net.Inner.Objects.Components
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.BootFromVent:
|
||||
{
|
||||
Rpc34BootFromVent.Deserialize(reader, out var ventId);
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.ClimbLadder:
|
||||
Rpc31ClimbLadder.Deserialize(reader, out var ladderId, out var lastClimbLadderSid);
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Innersloth.Maps;
|
||||
using Impostor.Api.Net;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Inner;
|
||||
|
||||
@@ -9,32 +9,27 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
public class PlayerVoteArea : IInnerMeetingHud.IPlayerVoteArea
|
||||
{
|
||||
private const byte VoteMask = 15;
|
||||
private const byte ReportedBit = 32;
|
||||
private const byte VotedBit = 64;
|
||||
private const byte DeadBit = 128;
|
||||
|
||||
private sbyte _votedForId;
|
||||
private byte _votedForId;
|
||||
|
||||
public PlayerVoteArea(InnerMeetingHud parent, InnerPlayerInfo targetPlayer, bool isDead)
|
||||
{
|
||||
Parent = parent;
|
||||
TargetPlayer = targetPlayer;
|
||||
|
||||
IsDead = isDead;
|
||||
VotedForId = (byte)(isDead ? TVoteType.Dead : TVoteType.HasNotVoted);
|
||||
}
|
||||
|
||||
public InnerMeetingHud Parent { get; }
|
||||
|
||||
public InnerPlayerInfo TargetPlayer { get; }
|
||||
|
||||
public bool IsDead { get; private set; }
|
||||
public bool IsDead => VoteType == TVoteType.Dead;
|
||||
|
||||
public bool DidVote { get; private set; }
|
||||
public bool DidVote => VoteType != TVoteType.HasNotVoted;
|
||||
|
||||
public bool DidReport { get; private set; }
|
||||
|
||||
public sbyte VotedForId
|
||||
public byte VotedForId
|
||||
{
|
||||
get => _votedForId;
|
||||
|
||||
@@ -42,25 +37,22 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
_votedForId = value;
|
||||
|
||||
if (DidVote)
|
||||
switch ((VoteType)value)
|
||||
{
|
||||
switch ((VoteType)value)
|
||||
{
|
||||
case TVoteType.ForceSkip:
|
||||
case TVoteType.Skip:
|
||||
VoteType = (VoteType)value;
|
||||
break;
|
||||
case TVoteType.Dead:
|
||||
VoteType = TVoteType.Dead;
|
||||
break;
|
||||
|
||||
default:
|
||||
VoteType = TVoteType.Player;
|
||||
VotedFor = Parent.Game.GameNet.GameData!.GetPlayerById((byte)value)?.Controller;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VoteType = null;
|
||||
VotedFor = null;
|
||||
case TVoteType.HasNotVoted:
|
||||
case TVoteType.Missed:
|
||||
case TVoteType.Skipped:
|
||||
VoteType = (VoteType)value;
|
||||
break;
|
||||
|
||||
default:
|
||||
VoteType = TVoteType.Player;
|
||||
VotedFor = Parent.Game.GameNet.GameData!.GetPlayerById(value)?.Controller;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,39 +65,20 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
|
||||
internal void Deserialize(IMessageReader reader, bool updateVote)
|
||||
{
|
||||
var state = reader.ReadByte();
|
||||
DeserializeState(state, out var votedForId, out var isDead, out var didVote, out var didReport);
|
||||
|
||||
IsDead = isDead;
|
||||
DidReport = didReport;
|
||||
var votedForId = reader.ReadByte();
|
||||
|
||||
if (updateVote)
|
||||
{
|
||||
DidVote = didVote;
|
||||
VotedForId = votedForId;
|
||||
}
|
||||
|
||||
DidReport = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
internal void SetVotedFor(sbyte votedFor)
|
||||
internal void SetVotedFor(byte votedFor)
|
||||
{
|
||||
DidVote = true;
|
||||
VotedForId = votedFor;
|
||||
}
|
||||
|
||||
private static void DeserializeState(byte state, out sbyte votedForId, out bool isDead, out bool didVote, out bool didReport)
|
||||
{
|
||||
votedForId = (sbyte)((state & VoteMask) - 1);
|
||||
|
||||
// Among Us's meeting system is really scuffed, we need this to ensure that ForceSkip state is correct (this will break any 14+ players game)
|
||||
if (votedForId == ((sbyte)TVoteType.ForceSkip + 1 & 14))
|
||||
{
|
||||
votedForId = (sbyte)TVoteType.ForceSkip;
|
||||
}
|
||||
|
||||
isDead = (state & DeadBit) > 0;
|
||||
didVote = (state & VotedBit) > 0;
|
||||
didReport = (state & ReportedBit) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Impostor.Api.Net.Messages;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
internal partial class InnerMeetingHud
|
||||
{
|
||||
public class VoterState
|
||||
{
|
||||
public VoterState(byte voterId, byte votedForId)
|
||||
{
|
||||
VoterId = voterId;
|
||||
VotedForId = votedForId;
|
||||
}
|
||||
|
||||
public byte VoterId { get; }
|
||||
|
||||
public byte VotedForId { get; }
|
||||
|
||||
public static VoterState Deserialize(IMessageReader reader)
|
||||
{
|
||||
return new VoterState(reader.Tag, reader.ReadByte());
|
||||
}
|
||||
|
||||
public void Serialize(IMessageWriter writer)
|
||||
{
|
||||
writer.StartMessage(this.VoterId);
|
||||
writer.Write(this.VotedForId);
|
||||
writer.EndMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -42,7 +43,8 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(Game.Options.DiscussionTime + Game.Options.VotingTime), _timerToken.Token);
|
||||
const float AnimationTime = 0.25f + 0.5f + 0.4f + 3f + 0.75f + 5f;
|
||||
await Task.Delay(TimeSpan.FromSeconds(AnimationTime + Game.Options.DiscussionTime + Game.Options.VotingTime), _timerToken.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
@@ -70,37 +72,31 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
if (initialState)
|
||||
{
|
||||
PopulateButtons();
|
||||
|
||||
foreach (var playerState in _playerStates)
|
||||
{
|
||||
playerState.Deserialize(reader, false);
|
||||
|
||||
if (playerState.DidReport)
|
||||
{
|
||||
Reporter = playerState.TargetPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
var length = reader.ReadPackedUInt32();
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var num = reader.ReadPackedUInt32();
|
||||
var inner = reader.ReadMessage();
|
||||
var playerVoteArea = _playerStates.SingleOrDefault(x => x.TargetPlayer.PlayerId == inner.Tag);
|
||||
|
||||
for (var i = 0; i < _playerStates.Length; i++)
|
||||
if (playerVoteArea != null)
|
||||
{
|
||||
if ((num & 1 << i) != 0)
|
||||
var clientPlayer = Game.Players.SingleOrDefault(x => x.Character?.PlayerId == playerVoteArea.TargetPlayer.PlayerId);
|
||||
var updateVote = !playerVoteArea.DidVote && (clientPlayer?.IsHost ?? false) && playerVoteArea.VoteType != VoteType.Missed;
|
||||
|
||||
playerVoteArea.Deserialize(inner, updateVote);
|
||||
|
||||
if (updateVote)
|
||||
{
|
||||
var playerVoteArea = _playerStates[i];
|
||||
await HandleVoteAsync(playerVoteArea);
|
||||
await CheckForEndVotingAsync();
|
||||
}
|
||||
|
||||
var clientPlayer = Game.Players.SingleOrDefault(x => x.Character?.PlayerId == playerVoteArea.TargetPlayer.PlayerId);
|
||||
var isHost = (clientPlayer?.IsHost ?? false) && playerVoteArea.VoteType != VoteType.ForceSkip;
|
||||
|
||||
playerVoteArea.Deserialize(reader, isHost);
|
||||
|
||||
if (isHost)
|
||||
{
|
||||
await HandleVoteAsync(playerVoteArea);
|
||||
await CheckForEndVotingAsync();
|
||||
}
|
||||
if (initialState && playerVoteArea.DidReport)
|
||||
{
|
||||
Reporter = playerVoteArea.TargetPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +173,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<bool> HandleCastVoteAsync(ClientPlayer sender, ClientPlayer? target, byte playerId, sbyte suspectPlayerId)
|
||||
private async ValueTask<bool> HandleCastVoteAsync(ClientPlayer sender, ClientPlayer? target, byte playerId, byte suspectPlayerId)
|
||||
{
|
||||
if (sender.IsHost)
|
||||
{
|
||||
@@ -221,48 +217,47 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] CalculateVotes()
|
||||
private KeyValuePair<byte, int> MaxPair(Dictionary<byte, int> self, out bool tie)
|
||||
{
|
||||
byte[] array = new byte[_playerStates.Max(x => x.TargetPlayer.PlayerId) + 2];
|
||||
foreach (var playerVoteArea in _playerStates)
|
||||
tie = true;
|
||||
var result = new KeyValuePair<byte, int>(byte.MaxValue, int.MinValue);
|
||||
foreach (var keyValuePair in self)
|
||||
{
|
||||
if (playerVoteArea.DidVote)
|
||||
if (keyValuePair.Value > result.Value)
|
||||
{
|
||||
var index = playerVoteArea.VotedForId + 1;
|
||||
if (index >= 0 && index < array.Length)
|
||||
{
|
||||
array[index] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
private int IndexOfMax<T>(T[] self, Func<T, int> comparer, out bool tie)
|
||||
{
|
||||
tie = false;
|
||||
var num = int.MinValue;
|
||||
var result = -1;
|
||||
for (var i = 0; i < self.Length; i++)
|
||||
{
|
||||
var num2 = comparer.Invoke(self[i]);
|
||||
if (num2 > num)
|
||||
{
|
||||
result = i;
|
||||
num = num2;
|
||||
result = keyValuePair;
|
||||
tie = false;
|
||||
}
|
||||
else if (num2 == num)
|
||||
else if (keyValuePair.Value == result.Value)
|
||||
{
|
||||
tie = true;
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Dictionary<byte, int> CalculateVotes()
|
||||
{
|
||||
var players = new Dictionary<byte, int>();
|
||||
foreach (var playerVoteArea in _playerStates)
|
||||
{
|
||||
if (!playerVoteArea.IsDead && playerVoteArea.DidVote && playerVoteArea.VoteType != VoteType.Missed)
|
||||
{
|
||||
if (players.TryGetValue(playerVoteArea.VotedForId, out var current))
|
||||
{
|
||||
players[playerVoteArea.VotedForId] = current + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
players[playerVoteArea.VotedForId] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
private async ValueTask HandleVotingCompleteAsync()
|
||||
{
|
||||
_timerToken.Cancel();
|
||||
@@ -271,14 +266,14 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
if (!playerVoteArea.DidVote)
|
||||
{
|
||||
playerVoteArea.SetVotedFor((sbyte)VoteType.ForceSkip);
|
||||
playerVoteArea.SetVotedFor((byte)VoteType.Missed);
|
||||
await HandleVoteAsync(playerVoteArea);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] self = this.CalculateVotes();
|
||||
var maxIdx = IndexOfMax(self, p => p, out var tie) - 1;
|
||||
var exiled = Game.GameNet.GameData!.GetPlayerById((byte)maxIdx)?.Controller;
|
||||
var self = this.CalculateVotes();
|
||||
var max = MaxPair(self, out var tie);
|
||||
var exiled = tie ? null : Game.GameNet.GameData!.GetPlayerById(max.Key)?.Controller;
|
||||
|
||||
if (exiled != null)
|
||||
{
|
||||
|
||||
@@ -297,6 +297,17 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.SendQuickChat:
|
||||
{
|
||||
if (!await ValidateOwnership(call, sender))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: deserialize and expose the result in an API
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
@@ -416,7 +427,7 @@ namespace Impostor.Server.Net.Inner.Objects
|
||||
var i = 1;
|
||||
while (true)
|
||||
{
|
||||
string text = expected + " " + i;
|
||||
var text = expected + " " + i;
|
||||
|
||||
if (Game.Players.All(x => x.Character == null || x.Character == this || x.Character.PlayerInfo.PlayerName != text))
|
||||
{
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Innersloth.Customization;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Server.Net.State;
|
||||
|
||||
namespace Impostor.Server.Net.Inner.Objects
|
||||
{
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace Impostor.Server.Net.Inner.Objects.ShipStatus
|
||||
|
||||
public override Vector2 InitialSpawnCenter { get; } = new Vector2(16.64f, -2.46f);
|
||||
|
||||
public override Vector2 MeetingSpawnCenter { get; } = new Vector2(17.726f, -16.286f);
|
||||
public override Vector2 MeetingSpawnCenter { get; } = new Vector2(17.4f, -16.286f);
|
||||
|
||||
public Vector2 MeetingSpawnCenter2 { get; } = new Vector2(-17.7f, -17.5f);
|
||||
public Vector2 MeetingSpawnCenter2 { get; } = new Vector2(17.4f, -17.515f);
|
||||
|
||||
public override Vector2 GetSpawnLocation(InnerPlayerControl player, int numPlayers, bool initialSpawn)
|
||||
{
|
||||
@@ -35,17 +35,16 @@ namespace Impostor.Server.Net.Inner.Objects.ShipStatus
|
||||
return base.GetSpawnLocation(player, numPlayers, initialSpawn);
|
||||
}
|
||||
|
||||
Vector2 position;
|
||||
if (player.PlayerId < 5)
|
||||
var halfPlayers = numPlayers / 2; // floored intentionally
|
||||
var spawnId = player.PlayerId % 15;
|
||||
if (player.PlayerId < halfPlayers)
|
||||
{
|
||||
position = this.MeetingSpawnCenter + (new Vector2(1, 0) * player.PlayerId);
|
||||
return this.MeetingSpawnCenter + (new Vector2(0.6f, 0) * spawnId);
|
||||
}
|
||||
else
|
||||
{
|
||||
position = this.MeetingSpawnCenter2 + (new Vector2(1, 0) * (player.PlayerId - 5));
|
||||
return this.MeetingSpawnCenter2 + (new Vector2(0.6f, 0) * (spawnId - halfPlayers));
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
protected override void AddSystems(Dictionary<SystemTypes, ISystemType> systems)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
@@ -105,6 +105,12 @@ namespace Impostor.Server.Net.Inner.Objects.ShipStatus
|
||||
break;
|
||||
}
|
||||
|
||||
case RpcCalls.UpdateSystem:
|
||||
{
|
||||
Rpc35UpdateSystem.Deserialize(reader, Game, out var systemType, out var playerControl, out var sequenceId, out var state, out var ventId);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return await base.HandleRpcAsync(sender, target, call, reader);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -18,10 +19,10 @@ namespace Impostor.Server.Net.Manager
|
||||
{
|
||||
internal partial class ClientManager
|
||||
{
|
||||
private static readonly HashSet<int> SupportedVersions = new HashSet<int>
|
||||
// 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, 3, 25), // 2021.3.31s
|
||||
GameVersion.GetVersion(2021, 4, 2), // 2021.4.2a
|
||||
GameVersion.GetVersion(2021, 6, 30), // 2021.6.30
|
||||
};
|
||||
|
||||
private readonly ILogger<ClientManager> _logger;
|
||||
@@ -38,6 +39,14 @@ namespace Impostor.Server.Net.Manager
|
||||
_clients = new ConcurrentDictionary<int, ClientBase>();
|
||||
}
|
||||
|
||||
private enum VersionCompareResult
|
||||
{
|
||||
Compatible,
|
||||
ClientTooOld,
|
||||
ServerTooOld,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
public IEnumerable<ClientBase> Clients => _clients.Values;
|
||||
|
||||
public int NextId()
|
||||
@@ -56,12 +65,26 @@ namespace Impostor.Server.Net.Manager
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion)
|
||||
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion, Language language, QuickChatModes chatMode)
|
||||
{
|
||||
if (!SupportedVersions.Contains(clientVersion))
|
||||
var versionCompare = CompareVersion(clientVersion);
|
||||
if (versionCompare != VersionCompareResult.Compatible)
|
||||
{
|
||||
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)}");
|
||||
|
||||
using var packet = MessageWriter.Get(MessageType.Reliable);
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.IncorrectVersion);
|
||||
|
||||
var message = versionCompare switch
|
||||
{
|
||||
VersionCompareResult.ClientTooOld => DisconnectMessages.VersionClientTooOld,
|
||||
VersionCompareResult.ServerTooOld => DisconnectMessages.VersionServerTooOld,
|
||||
VersionCompareResult.Unknown => DisconnectMessages.VersionUnsupported,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
Message01JoinGameS2C.SerializeError(packet, false, DisconnectReason.Custom, message);
|
||||
|
||||
await connection.SendAsync(packet);
|
||||
return;
|
||||
}
|
||||
@@ -82,7 +105,7 @@ namespace Impostor.Server.Net.Manager
|
||||
return;
|
||||
}
|
||||
|
||||
var client = _clientFactory.Create(connection, name, clientVersion);
|
||||
var client = _clientFactory.Create(connection, name, clientVersion, language, chatMode);
|
||||
var id = NextId();
|
||||
|
||||
client.Id = id;
|
||||
@@ -104,5 +127,29 @@ namespace Impostor.Server.Net.Manager
|
||||
&& _clients.TryGetValue(client.Id, out var registeredClient)
|
||||
&& ReferenceEquals(client, registeredClient);
|
||||
}
|
||||
|
||||
private VersionCompareResult CompareVersion(int clientVersion)
|
||||
{
|
||||
foreach (var serverVersion in SupportedVersions)
|
||||
{
|
||||
if (clientVersion == serverVersion)
|
||||
{
|
||||
return VersionCompareResult.Compatible;
|
||||
}
|
||||
}
|
||||
|
||||
if (clientVersion < SupportedVersions[0])
|
||||
{
|
||||
return VersionCompareResult.ClientTooOld;
|
||||
}
|
||||
|
||||
if (clientVersion > SupportedVersions.Last())
|
||||
{
|
||||
return VersionCompareResult.ServerTooOld;
|
||||
}
|
||||
|
||||
// This may happen in the very rare case that version X is supported, X+2 is as well, but X+1 is not.
|
||||
return VersionCompareResult.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 _);
|
||||
HandshakeC2S.Deserialize(e.HandshakeData, out var clientVersion, out var name, out _, out var language, out var chatMode);
|
||||
|
||||
var connection = new HazelConnection(e.Connection, _connectionLogger);
|
||||
|
||||
await _eventManager.CallAsync(new ClientConnectionEvent(connection, e.HandshakeData));
|
||||
|
||||
// Register client
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion);
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, clientVersion, language, chatMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,13 @@ namespace Impostor.Server.Net.Redirector
|
||||
public ClientRedirector(
|
||||
string name,
|
||||
int gameVersion,
|
||||
Language language,
|
||||
QuickChatModes chatMode,
|
||||
HazelConnection connection,
|
||||
ClientManager clientManager,
|
||||
INodeProvider nodeProvider,
|
||||
INodeLocator nodeLocator)
|
||||
: base(name, gameVersion, connection)
|
||||
: base(name, gameVersion, language, chatMode, connection)
|
||||
{
|
||||
_clientManager = clientManager;
|
||||
_nodeProvider = nodeProvider;
|
||||
|
||||
@@ -8,20 +8,17 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private Assembly? _assembly;
|
||||
|
||||
public AssemblyInformation(AssemblyName assemblyName, string path, bool isPlugin, bool isDefaultAssembly)
|
||||
public AssemblyInformation(AssemblyName assemblyName, string path, bool isPlugin)
|
||||
{
|
||||
AssemblyName = assemblyName;
|
||||
Path = path;
|
||||
IsPlugin = isPlugin;
|
||||
IsDefaultAssembly = isDefaultAssembly;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public bool IsPlugin { get; }
|
||||
|
||||
public bool IsDefaultAssembly { get; }
|
||||
|
||||
public AssemblyName AssemblyName { get; }
|
||||
|
||||
public Assembly Load(AssemblyLoadContext context)
|
||||
|
||||
@@ -9,8 +9,6 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
bool IsPlugin { get; }
|
||||
|
||||
bool IsDefaultAssembly { get; }
|
||||
|
||||
Assembly Load(AssemblyLoadContext context);
|
||||
}
|
||||
}
|
||||
@@ -7,19 +7,16 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private readonly Assembly _assembly;
|
||||
|
||||
public LoadedAssemblyInformation(Assembly assembly, bool isDefaultAssembly)
|
||||
public LoadedAssemblyInformation(Assembly assembly)
|
||||
{
|
||||
AssemblyName = assembly.GetName();
|
||||
_assembly = assembly;
|
||||
IsDefaultAssembly = isDefaultAssembly;
|
||||
}
|
||||
|
||||
public AssemblyName AssemblyName { get; }
|
||||
|
||||
public bool IsPlugin => false;
|
||||
|
||||
public bool IsDefaultAssembly { get; }
|
||||
|
||||
public Assembly Load(AssemblyLoadContext context)
|
||||
{
|
||||
return _assembly;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginAssemblyLoadContext : AssemblyLoadContext, IDisposable
|
||||
{
|
||||
private readonly IDictionary<string, IAssemblyInformation> _assembliesByName;
|
||||
|
||||
public PluginAssemblyLoadContext(IDictionary<string, IAssemblyInformation> assembliesByName)
|
||||
: base(true)
|
||||
{
|
||||
_assembliesByName = assembliesByName;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_assembliesByName.Clear();
|
||||
this.Unload();
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName name)
|
||||
{
|
||||
return name.Name != null && _assembliesByName.TryGetValue(name.Name, out var assemblyInfo)
|
||||
? assemblyInfo.Load(this)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Server.Config
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginLoaderConfig
|
||||
public class PluginConfig
|
||||
{
|
||||
public const string Section = "PluginLoader";
|
||||
|
||||
public List<string> Paths { get; set; } = new List<string>();
|
||||
|
||||
public List<string> LibraryPaths { get; set; } = new List<string>();
|
||||
@@ -6,7 +6,7 @@ using Impostor.Api.Plugins;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginInformation : IPluginInformation
|
||||
public class PluginInformation
|
||||
{
|
||||
private readonly ImpostorPluginAttribute _attribute;
|
||||
|
||||
|
||||
+56
-150
@@ -4,72 +4,24 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Plugins;
|
||||
using Impostor.Server.Config;
|
||||
using Impostor.Server.Plugins.Proxies;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileSystemGlobbing;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
|
||||
namespace Impostor.Server.Plugins
|
||||
{
|
||||
public class PluginManager : IPluginManager, IHostedService
|
||||
public static class PluginLoader
|
||||
{
|
||||
private readonly ILogger<PluginManager> _logger;
|
||||
private readonly IOptions<PluginLoaderConfig> _pluginConfig;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ServiceProxyCollection _serviceProxyCollection;
|
||||
private static readonly ILogger Logger = Log.ForContext(typeof(PluginLoader));
|
||||
|
||||
private PluginAssemblyLoadContext? _currentContext;
|
||||
|
||||
public PluginManager(ILogger<PluginManager> logger, IOptions<PluginLoaderConfig> pluginConfig, IConfiguration configuration, IServiceProvider serviceProvider, ServiceProxyCollection serviceProxyCollection)
|
||||
public static IHostBuilder UsePluginLoader(this IHostBuilder builder, PluginConfig config)
|
||||
{
|
||||
_logger = logger;
|
||||
_pluginConfig = pluginConfig;
|
||||
_configuration = configuration;
|
||||
_serviceProvider = serviceProvider;
|
||||
_serviceProxyCollection = serviceProxyCollection;
|
||||
}
|
||||
var assemblyInfos = new List<IAssemblyInformation>();
|
||||
var context = AssemblyLoadContext.Default;
|
||||
|
||||
public IHost? CurrentHost { get; private set; }
|
||||
|
||||
public List<PluginInformation> Plugins { get; } = new List<PluginInformation>();
|
||||
|
||||
IReadOnlyList<IPluginInformation> IPluginManager.Plugins => Plugins.AsReadOnly();
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
CurrentHost = CreatePluginHost();
|
||||
return CurrentHost.StartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (CurrentHost != null)
|
||||
{
|
||||
await CurrentHost.StopAsync(cancellationToken);
|
||||
|
||||
if (CurrentHost is IAsyncDisposable asyncDisposable)
|
||||
{
|
||||
await asyncDisposable.DisposeAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentHost.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string, IAssemblyInformation> GetAssemblyInformation()
|
||||
{
|
||||
// Add the plugins and libraries.
|
||||
var config = _pluginConfig.Value;
|
||||
var pluginPaths = new List<string>(config.Paths);
|
||||
var libraryPaths = new List<string>(config.LibraryPaths);
|
||||
CheckPaths(pluginPaths);
|
||||
@@ -83,70 +35,38 @@ namespace Impostor.Server.Plugins
|
||||
var matcher = new Matcher(StringComparison.OrdinalIgnoreCase);
|
||||
matcher.AddInclude("*.dll");
|
||||
matcher.AddExclude("Impostor.Api.dll");
|
||||
matcher.AddExclude("Impostor.Server.dll");
|
||||
|
||||
var assembliesByName = AssemblyLoadContext.Default.Assemblies
|
||||
.Where(a => a.GetName().Name != null)
|
||||
.ToDictionary(
|
||||
a => a.GetName().Name!,
|
||||
a => (IAssemblyInformation)new LoadedAssemblyInformation(a, true));
|
||||
RegisterAssemblies(pluginPaths, matcher, assemblyInfos, true);
|
||||
RegisterAssemblies(libraryPaths, matcher, assemblyInfos, false);
|
||||
|
||||
RegisterAssemblies(assembliesByName, pluginPaths, matcher, true);
|
||||
RegisterAssemblies(assembliesByName, libraryPaths, matcher, false);
|
||||
|
||||
return assembliesByName;
|
||||
}
|
||||
|
||||
private IHost CreatePluginHost()
|
||||
{
|
||||
var assembliesByName = GetAssemblyInformation();
|
||||
_currentContext ??= new PluginAssemblyLoadContext(assembliesByName);
|
||||
|
||||
var pluginHostBuilder = new HostBuilder()
|
||||
.UseConsoleLifetime(options => options.SuppressStatusMessages = true)
|
||||
.UseContentRoot(_configuration.GetValue<string>(HostDefaults.ContentRootKey))
|
||||
.ConfigureHostConfiguration(builder => { builder.AddConfiguration(_configuration); })
|
||||
.UseServiceProviderFactory(_ => new DefaultServiceProviderFactory());
|
||||
|
||||
Plugins.Clear();
|
||||
Plugins.AddRange(GetPluginInformation(assembliesByName, _currentContext));
|
||||
|
||||
foreach (var plugin in Plugins)
|
||||
// Register the resolver to the current context.
|
||||
// TODO: Move this to a new context so we can unload/reload plugins.
|
||||
context.Resolving += (loadContext, name) =>
|
||||
{
|
||||
plugin.Startup?.ConfigureHost(pluginHostBuilder);
|
||||
}
|
||||
Logger.Verbose("Loading assembly {0} v{1}", name.Name, name.Version);
|
||||
|
||||
pluginHostBuilder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService<PluginLoaderService>();
|
||||
|
||||
foreach (var (type, lifetime) in _serviceProxyCollection.ServiceTypes)
|
||||
// Some plugins may be referencing another Impostor.Api version and try to load it.
|
||||
// We want to only use the one shipped with the server.
|
||||
if (name.Name == "Impostor.Api")
|
||||
{
|
||||
services.Add(new ServiceDescriptor(
|
||||
type,
|
||||
_ => _serviceProvider.GetService(type)!,
|
||||
lifetime));
|
||||
return typeof(IPlugin).Assembly;
|
||||
}
|
||||
|
||||
foreach (var plugin in Plugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureServices(services);
|
||||
}
|
||||
});
|
||||
var info = assemblyInfos.FirstOrDefault(a => a.AssemblyName.Name == name.Name);
|
||||
|
||||
return pluginHostBuilder.Build();
|
||||
}
|
||||
return info?.Load(loadContext);
|
||||
};
|
||||
|
||||
private List<PluginInformation> GetPluginInformation(IDictionary<string, IAssemblyInformation> assembliesByName, PluginAssemblyLoadContext context)
|
||||
{
|
||||
var plugins = new List<PluginInformation>();
|
||||
var pluginAssemblies = assembliesByName
|
||||
.Values
|
||||
// TODO: Catch uncaught exceptions.
|
||||
var assemblies = assemblyInfos
|
||||
.Where(a => a.IsPlugin)
|
||||
.Select(a => context.LoadFromAssemblyName(a.AssemblyName))
|
||||
.ToList();
|
||||
|
||||
foreach (var assembly in pluginAssemblies)
|
||||
// Find all plugins.
|
||||
var plugins = new List<PluginInformation>();
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// Find plugin startup.
|
||||
var pluginStartup = assembly
|
||||
@@ -156,7 +76,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
if (pluginStartup.Count > 1)
|
||||
{
|
||||
_logger.LogWarning("A plugin may only define zero or one IPluginStartup implementation ({0}).", assembly);
|
||||
Logger.Warning("A plugin may only define zero or one IPluginStartup implementation ({0}).", assembly);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -171,7 +91,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
if (plugin.Count != 1)
|
||||
{
|
||||
_logger.LogWarning("A plugin must define exactly one IPlugin or PluginBase implementation ({0}).", assembly);
|
||||
Logger.Warning("A plugin must define exactly one IPlugin or PluginBase implementation ({0}).", assembly);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -184,75 +104,61 @@ namespace Impostor.Server.Plugins
|
||||
plugin.Single()));
|
||||
}
|
||||
|
||||
return OrderPlugins(plugins);
|
||||
var orderedPlugins = LoadOrderPlugins(plugins);
|
||||
|
||||
foreach (var plugin in orderedPlugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureHost(builder);
|
||||
}
|
||||
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostedService(provider => ActivatorUtilities.CreateInstance<PluginLoaderService>(provider, orderedPlugins));
|
||||
|
||||
foreach (var plugin in orderedPlugins)
|
||||
{
|
||||
plugin.Startup?.ConfigureServices(services);
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void CheckPaths(IEnumerable<string> paths)
|
||||
private static void CheckPaths(IEnumerable<string> paths)
|
||||
{
|
||||
foreach (var path in paths)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
_logger.LogWarning("Path {path} was specified in the PluginLoader configuration, but this folder doesn't exist!", path);
|
||||
Logger.Warning("Path {path} was specified in the PluginLoader configuration, but this folder doesn't exist!", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterAssemblies(
|
||||
IDictionary<string, IAssemblyInformation> assemblies,
|
||||
private static void RegisterAssemblies(
|
||||
IEnumerable<string> paths,
|
||||
Matcher matcher,
|
||||
ICollection<IAssemblyInformation> assemblyInfos,
|
||||
bool isPlugin)
|
||||
{
|
||||
foreach (var path in paths.SelectMany(matcher.GetResultsInFullPath))
|
||||
{
|
||||
AssemblyName name;
|
||||
AssemblyName assemblyName;
|
||||
|
||||
try
|
||||
{
|
||||
name = AssemblyName.GetAssemblyName(path);
|
||||
assemblyName = AssemblyName.GetAssemblyName(path);
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.Name == null)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Skipped {0} because the assembly name could not be resolved",
|
||||
path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (assemblies.TryGetValue(name.Name, out var loaded))
|
||||
{
|
||||
var version = name.Version;
|
||||
var loadedVersion = loaded.AssemblyName.Version;
|
||||
|
||||
if (loadedVersion != null && version != null && version.Major != loadedVersion.Major)
|
||||
{
|
||||
_logger.LogError(
|
||||
"Assembly {0} is already loaded with version {1} while loading version {2}",
|
||||
name.Name,
|
||||
loadedVersion,
|
||||
version);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Skipped assembly {0} because it's already loaded", name.Name);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
_logger.LogTrace("Registered assembly {name} v{version} ({path})", name.Name, name.Version, path);
|
||||
|
||||
assemblies.Add(name.Name, new AssemblyInformation(name, path, isPlugin, false));
|
||||
assemblyInfos.Add(new AssemblyInformation(assemblyName, path, isPlugin));
|
||||
}
|
||||
}
|
||||
|
||||
private List<PluginInformation> OrderPlugins(IEnumerable<PluginInformation> plugins)
|
||||
private static List<PluginInformation> LoadOrderPlugins(IEnumerable<PluginInformation> plugins)
|
||||
{
|
||||
var pluginDictionary = new Dictionary<string, PluginInformation>();
|
||||
var hardDependencies = new Dictionary<string, List<string>>();
|
||||
@@ -302,7 +208,7 @@ namespace Impostor.Server.Plugins
|
||||
return ordered;
|
||||
}
|
||||
|
||||
private List<string> CheckHardDependencies(
|
||||
private static List<string> CheckHardDependencies(
|
||||
List<string> plugins,
|
||||
IReadOnlyDictionary<string, List<string>> hardDependencies)
|
||||
{
|
||||
@@ -315,7 +221,7 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
foreach (var dependency in hardDependencies[plugin].Where(dependency => !plugins.Contains(dependency)))
|
||||
{
|
||||
_logger.LogError(
|
||||
Logger.Error(
|
||||
"The plugin {plugin} has defined the plugin {dependency} as a hard dependency but its not present! {plugin} will not loaded.",
|
||||
plugin,
|
||||
dependency,
|
||||
@@ -333,7 +239,7 @@ namespace Impostor.Server.Plugins
|
||||
return plugins;
|
||||
}
|
||||
|
||||
private void RecursiveOrder(
|
||||
private static void RecursiveOrder(
|
||||
string plugin,
|
||||
IReadOnlyDictionary<string, List<string>> dependencyGraph,
|
||||
ICollection<string> processed,
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Plugins;
|
||||
@@ -12,20 +13,20 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
private readonly ILogger<PluginLoaderService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly PluginManager _pluginManager;
|
||||
private readonly List<PluginInformation> _plugins;
|
||||
|
||||
public PluginLoaderService(ILogger<PluginLoaderService> logger, IServiceProvider serviceProvider, PluginManager pluginManager)
|
||||
public PluginLoaderService(ILogger<PluginLoaderService> logger, IServiceProvider serviceProvider, List<PluginInformation> plugins)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_pluginManager = pluginManager;
|
||||
_plugins = plugins;
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Enabling plugins.");
|
||||
_logger.LogInformation("Loading plugins.");
|
||||
|
||||
foreach (var plugin in _pluginManager.Plugins)
|
||||
foreach (var plugin in _plugins)
|
||||
{
|
||||
_logger.LogInformation("Enabling plugin {0}.", plugin);
|
||||
|
||||
@@ -37,10 +38,10 @@ namespace Impostor.Server.Plugins
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
_pluginManager.Plugins.Count == 1
|
||||
? "Enabled {0} plugin."
|
||||
: "Enabled {0} plugins.",
|
||||
_pluginManager.Plugins.Count
|
||||
_plugins.Count == 1
|
||||
? "Loaded {0} plugin."
|
||||
: "Loaded {0} plugins.",
|
||||
_plugins.Count
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ namespace Impostor.Server.Plugins
|
||||
{
|
||||
// Disable all plugins with a valid instance set.
|
||||
// In the case of a failed startup, some can be null.
|
||||
foreach (var plugin in _pluginManager.Plugins)
|
||||
foreach (var plugin in _plugins)
|
||||
{
|
||||
if (plugin.Instance != null)
|
||||
{
|
||||
@@ -56,7 +57,6 @@ namespace Impostor.Server.Plugins
|
||||
|
||||
// Disable plugin.
|
||||
await plugin.Instance.DisableAsync();
|
||||
plugin.Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Impostor.Server.Plugins.Proxies
|
||||
{
|
||||
public class ServiceProxyCollection
|
||||
{
|
||||
public ServiceProxyCollection(IReadOnlyList<ServiceRegistration> serviceTypes)
|
||||
{
|
||||
ServiceTypes = serviceTypes;
|
||||
}
|
||||
|
||||
public IReadOnlyList<ServiceRegistration> ServiceTypes { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Impostor.Server.Plugins.Proxies
|
||||
{
|
||||
public record ServiceRegistration(Type Type, ServiceLifetime Lifetime);
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Events;
|
||||
using Impostor.Api.Events.Managers;
|
||||
using Impostor.Api.Games;
|
||||
using Impostor.Api.Games.Managers;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Manager;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Api.Plugins;
|
||||
using Impostor.Api.Utils;
|
||||
using Impostor.Hazel.Extensions;
|
||||
using Impostor.Server.Config;
|
||||
@@ -21,13 +18,11 @@ using Impostor.Server.Net.Manager;
|
||||
using Impostor.Server.Net.Messages;
|
||||
using Impostor.Server.Net.Redirector;
|
||||
using Impostor.Server.Plugins;
|
||||
using Impostor.Server.Plugins.Proxies;
|
||||
using Impostor.Server.Recorder;
|
||||
using Impostor.Server.Utils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
@@ -36,16 +31,38 @@ namespace Impostor.Server
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
private static async Task<int> Main(string[] args)
|
||||
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()
|
||||
.CreateBootstrapLogger();
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting Impostor v{0}", DotnetUtils.Version);
|
||||
await CreateHostBuilder(args).RunConsoleAsync();
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -59,8 +76,25 @@ namespace Impostor.Server
|
||||
}
|
||||
}
|
||||
|
||||
private static IConfiguration CreateConfiguration(string[] args)
|
||||
{
|
||||
var configurationBuilder = new ConfigurationBuilder();
|
||||
|
||||
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
|
||||
configurationBuilder.AddJsonFile("config.json", true);
|
||||
configurationBuilder.AddJsonFile("config.Development.json", true);
|
||||
configurationBuilder.AddEnvironmentVariables(prefix: "IMPOSTOR_");
|
||||
configurationBuilder.AddCommandLine(args);
|
||||
|
||||
return configurationBuilder.Build();
|
||||
}
|
||||
|
||||
private static IHostBuilder CreateHostBuilder(string[] args)
|
||||
{
|
||||
var configuration = CreateConfiguration(args);
|
||||
var pluginConfig = configuration.GetSection("PluginLoader")
|
||||
.Get<PluginConfig>() ?? new PluginConfig();
|
||||
|
||||
return Host.CreateDefaultBuilder(args)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
#if DEBUG
|
||||
@@ -70,10 +104,7 @@ namespace Impostor.Server
|
||||
#endif
|
||||
.ConfigureAppConfiguration(builder =>
|
||||
{
|
||||
builder.AddJsonFile("config.json", true);
|
||||
builder.AddJsonFile("config.Development.json", true);
|
||||
builder.AddEnvironmentVariables(prefix: "IMPOSTOR_");
|
||||
builder.AddCommandLine(args);
|
||||
builder.AddConfiguration(configuration);
|
||||
})
|
||||
.ConfigureServices((host, services) =>
|
||||
{
|
||||
@@ -103,7 +134,6 @@ namespace Impostor.Server
|
||||
services.Configure<AnnouncementsServerConfig>(host.Configuration.GetSection(AnnouncementsServerConfig.Section));
|
||||
services.Configure<AuthServerConfig>(host.Configuration.GetSection(AuthServerConfig.Section));
|
||||
services.Configure<ServerRedirectorConfig>(host.Configuration.GetSection(ServerRedirectorConfig.Section));
|
||||
services.Configure<PluginLoaderConfig>(host.Configuration.GetSection(PluginLoaderConfig.Section));
|
||||
|
||||
if (redirector.Enabled)
|
||||
{
|
||||
@@ -198,53 +228,10 @@ namespace Impostor.Server
|
||||
{
|
||||
services.AddHostedService<AuthService>();
|
||||
}
|
||||
|
||||
services.AddSingleton<PluginManager>();
|
||||
services.AddSingleton<IPluginManager>(p => p.GetRequiredService<PluginManager>());
|
||||
services.AddHostedService(p => p.GetRequiredService<PluginManager>());
|
||||
})
|
||||
.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;
|
||||
}
|
||||
|
||||
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);
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddTransient(serviceProvider => serviceProvider.GetRequiredService<PluginManager>().CurrentHost?.Services.GetServices<IEventListener>() ?? Enumerable.Empty<IEventListener>());
|
||||
|
||||
var apiAssembly = typeof(IPlugin).Assembly;
|
||||
var serilogAssembly = typeof(Serilog.ILogger).Assembly;
|
||||
|
||||
services.AddSingleton(new ServiceProxyCollection(
|
||||
services
|
||||
.Where(t => t.ServiceType.Assembly == apiAssembly || t.ServiceType.Assembly == serilogAssembly || t.ServiceType == typeof(PluginManager) || t.ServiceType == typeof(ILoggerFactory))
|
||||
.Select(t => new ServiceRegistration(t.ServiceType, t.Lifetime))
|
||||
.Distinct()
|
||||
.ToArray()));
|
||||
});
|
||||
.UseSerilog()
|
||||
.UseConsoleLifetime()
|
||||
.UsePluginLoader(pluginConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Impostor.Api.Innersloth;
|
||||
using Impostor.Api.Net.Custom;
|
||||
using Impostor.Api.Net.Messages;
|
||||
using Impostor.Server.Config;
|
||||
@@ -17,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, HazelConnection connection, PacketRecorder recorder)
|
||||
: base(logger, antiCheatOptions, clientManager, gameManager, customMessageManager, name, gameVersion, connection)
|
||||
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)
|
||||
{
|
||||
_recorder = recorder;
|
||||
_isFirst = true;
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Impostor.Tools.ServerReplay
|
||||
// Create and register connection.
|
||||
var connection = new MockHazelConnection(address);
|
||||
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, gameVersion);
|
||||
await _clientManager.RegisterConnectionAsync(connection, name, gameVersion, Language.English, QuickChatModes.FreeChatOrQuickChat);
|
||||
|
||||
// Store reference for ourselfs.
|
||||
Connections.Add(clientId, connection);
|
||||
|
||||
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user