Compare commits

...
26 Commits
Author SHA1 Message Date
miniduikbootandjs6pak 66b4d860a9 Improve error when client version is unsupported (#432)
* Improve error when client version is unsupported

In the default message that was used previously the user is asked to
upgrade his game. This is not always correct, as sometimes the Impostor
server is too old and should be updated instead. Therefore custom
disconnect messages are used that blames either the user or the server
operator, which is hopefully more accurate.

I've chosen not to include the game version that is supported by the
server in this message because the network version rarely matches the
advertised version in the main menu.

* Rewrite versionCompare switch case

Co-authored-by: js6pak <kubastaron@hotmail.com>

Co-authored-by: js6pak <kubastaron@hotmail.com>
2021-07-13 22:48:24 +02:00
js6pak dca26cff59 Log unsupported version 2021-07-07 19:01:48 +02:00
js6pak 3437c86bff Merge pull request #430 from Bramux/fix-hat-mismatch
Fix mismatched hats
2021-07-05 23:19:30 +02:00
Bramux 66fb8426a6 Correct hat mismatches 2021-07-05 22:26:28 +02:00
js6pak 4c839e2dd8 Merge pull request #429 from miniduikboot/fix/style 2021-07-05 21:22:28 +02:00
js6pak c6aa596e3b Fix warnings in Impostor.Benchmarks 2021-07-05 21:13:56 +02:00
miniduikboot d8843db3eb Use var instead of explicit type
Also prevent future issues
2021-07-05 20:36:56 +02:00
miniduikboot b918f25443 Remove unnecessary imports
And add rule to prevent future issues
2021-07-05 20:36:56 +02:00
js6pak 824359f93b Merge pull request #428 from miniduikboot/fix/colortype
Correct ColorType array
2021-07-05 20:33:11 +02:00
js6pak 046d09b5fa Merge pull request #427 from miniduikboot/fix/airship-spawn
Stop cancelling initial Airship spawn SnapTo RPC
2021-07-05 20:30:12 +02:00
miniduikboot 5410c71280 Correct ColorType array
When creating this array for the update, I used the wrong array. Oops!
Turns out 5 of these colors aren't actually in the game.
2021-07-05 20:06:21 +02:00
miniduikboot 241e867343 Stop cancelling initial Airship spawn SnapTo RPC
This is used to hide players that are still selecting their spawn
location. If this is cancelled, players are left standing at the
location they were at when the last meeting was called, which is undesirable.
2021-07-05 20:01:20 +02:00
js6pak 11f2ffd24d Remove legacy regionInfo patcher 2021-07-05 18:27:59 +02:00
js6pak 9df01d2cf7 Merge pull request #423 from miniduikboot/feature/2021.6.15 2021-06-26 20:26:07 +00:00
miniduikboot d2720442e9 Turn derived properties into getters
There is no significant performance difference: while accessing the
getters is slightly slower, this can be balanced against the cost of
setting the property. As this is also cleaner, switch to getters.
2021-06-25 18:46:28 +02:00
miniduikboot ddedffd121 Refactor Polus meeting room spawning logic
Also adjust the constants to the new constants used in game
2021-06-25 18:46:28 +02:00
miniduikboot 2f04f6ea98 Stub implementation of SendQuickChat
Proper API will follow later
2021-06-25 18:46:28 +02:00
miniduikboot 5f4c40bf36 Remove the table from the Readme
As Among Us is getting a lot of small network-compatible bugfix releases
nowadays, it doesn't seem necessary to maintain the table anymore.
Release notes state clearly which network version is supported and there
is limited support for old versions anyway.
2021-06-25 18:46:28 +02:00
miniduikboot ef4cc5646b Simplify the setup instructions
The patcher has not been updated to support the new JSON format and is
less necessary now that JSON is used for the region file format.

The website also now has deeplinking support, so we point Android/iOS
people to the website now too as that is more convenient compared to the
previous methods.
2021-06-25 18:46:28 +02:00
js6pak d894e9adc3 Meeting changes 2021-06-25 18:46:28 +02:00
js6pak c086cf80b1 Announcements protocol changes 2021-06-25 18:46:28 +02:00
js6pak 1118a59167 Update GameKeywords 2021-06-25 18:46:28 +02:00
miniduikboot 5154dd9aa5 Adjust Polus Meeting table spawn locations 2021-06-25 18:46:28 +02:00
miniduikboot a621225fcd Add new colors 2021-06-25 18:46:28 +02:00
miniduikboot 73a5463848 Update supported GameVersions 2021-06-25 18:46:28 +02:00
miniduikboot 02d25a290f Bump version because new net version 2021-06-15 22:20:07 +02:00
60 changed files with 334 additions and 3825 deletions

No files matched your search

+6 -36
View File
@@ -3,14 +3,11 @@
[![Discord](https://img.shields.io/badge/Discord-chat-blue?style=flat-square)](https://discord.gg/Mk3w6Tb)
[![AppVeyor](https://img.shields.io/appveyor/build/Impostor/Impostor/master?style=flat-square)](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 | [![Download](https://img.shields.io/badge/Download-v1.1.0-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.1.0) |
| 1.2.2 | 2020.09.22 - 2020.11.17 | No | [![Download](https://img.shields.io/badge/Download-v1.2.2-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.2.2) |
| 1.3.0 | 2021.3.5 | No | [![Download](https://img.shields.io/badge/Download-v1.3.0-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.3.0) |
| 1.4.0 | 2021.3.31 - 2021.4.2 | Yes | [![Download](https://img.shields.io/badge/Download-master-blue?style=flat-square)](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.
![Client](docs/images/client.jpg)
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
-7
View File
@@ -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 -14
View File
@@ -1,13 +1,11 @@
# 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).
## Cloning Impostor
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
@@ -23,14 +21,3 @@ cd src/Impostor.Server/
dotnet build
```
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
```
+5 -5
View File
@@ -6,14 +6,14 @@ There are currently two modes to run the Impostor server in. The first way is th
### 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)
- [Windows x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.7-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.
- [macOS x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.7-macos-x64-installer)
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) and your architecture (x64/arm/arm64).
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
+4 -8
View File
@@ -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
+6
View File
@@ -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
+2 -1
View File
@@ -1,7 +1,8 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.4.0</VersionPrefix>
<VersionPrefix>1.5.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,
}
}
+12 -5
View File
@@ -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,
}
}
+13 -3
View File
@@ -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;
}
}
}
+9 -1
View File
@@ -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,
}
}
-50
View File
@@ -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);
}
}
}
}
-39
View File
@@ -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);
}
}
}
+1
View File
@@ -34,5 +34,6 @@
SetTasks = 29,
ClimbLadder = 31,
UsePlatform = 32,
SendQuickChat = 33,
}
}
@@ -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,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();
}
}
}
@@ -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());
}
}
}
@@ -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>
@@ -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

@@ -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;
@@ -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);
}
}
@@ -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;
@@ -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,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, 4, 25), // 2021.6.15
};
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()
@@ -58,10 +67,24 @@ namespace Impostor.Server.Net.Manager
public async ValueTask RegisterConnectionAsync(IHazelConnection connection, string name, int clientVersion)
{
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;
}
@@ -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;
}
}
}
-32
View File
@@ -13,14 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server", "Impostor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Api", "Impostor.Api\Impostor.Api.csproj", "{E096A7D7-D693-4A13-A526-38CC574D84F8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "patcher", "patcher", "{94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.Shared", "Impostor.Patcher\Impostor.Patcher.Shared\Impostor.Patcher.Shared.csproj", "{7C3EB599-2292-4532-B280-D5BED1094DD4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.WinForms", "Impostor.Patcher\Impostor.Patcher.WinForms\Impostor.Patcher.WinForms.csproj", "{804CF172-0C87-4423-9688-BD97D549891E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.Cli", "Impostor.Patcher\Impostor.Patcher.Cli\Impostor.Patcher.Cli.csproj", "{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{36AA9913-E6EA-4A6C-90E6-2FD3CC2E3124}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Plugins.Debugger", "Impostor.Plugins.Debugger\Impostor.Plugins.Debugger.csproj", "{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}"
@@ -71,11 +63,6 @@ Global
{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Release|Any CPU.Build.0 = Release|Any CPU
{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Release|x86.ActiveCfg = Release|Any CPU
{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Release|x86.Build.0 = Release|Any CPU
{804CF172-0C87-4423-9688-BD97D549891E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{804CF172-0C87-4423-9688-BD97D549891E}.Debug|x86.ActiveCfg = Debug|Any CPU
{804CF172-0C87-4423-9688-BD97D549891E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{804CF172-0C87-4423-9688-BD97D549891E}.Release|x86.ActiveCfg = Release|Any CPU
{804CF172-0C87-4423-9688-BD97D549891E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -84,22 +71,6 @@ Global
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|Any CPU.Build.0 = Release|Any CPU
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|x86.ActiveCfg = Release|Any CPU
{E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|x86.Build.0 = Release|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|x86.ActiveCfg = Debug|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|x86.Build.0 = Debug|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Release|Any CPU.Build.0 = Release|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Release|x86.ActiveCfg = Release|Any CPU
{7C3EB599-2292-4532-B280-D5BED1094DD4}.Release|x86.Build.0 = Release|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Debug|x86.ActiveCfg = Debug|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Debug|x86.Build.0 = Debug|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|Any CPU.Build.0 = Release|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|x86.ActiveCfg = Release|Any CPU
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|x86.Build.0 = Release|Any CPU
{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -165,9 +136,6 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D} = {56DD9707-D811-4056-9E2C-8A9CC2479B07}
{804CF172-0C87-4423-9688-BD97D549891E} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{7C3EB599-2292-4532-B280-D5BED1094DD4} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}
{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7} = {36AA9913-E6EA-4A6C-90E6-2FD3CC2E3124}
{BE44C4A8-A202-4B63-A3BA-AC0AB5E7A0EB} = {9F1919B0-915B-4749-9944-697DF7E7F67F}
{3DF86F12-7099-44F6-B98B-A148213D60B1} = {9F1919B0-915B-4749-9944-697DF7E7F67F}