+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup Label="ProjectConfigurations">
- <ProjectConfiguration Include="Debug|Win32">
- <Configuration>Debug</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- <ProjectConfiguration Include="Release|Win32">
- <Configuration>Release</Configuration>
- <Platform>Win32</Platform>
- </ProjectConfiguration>
- </ItemGroup>
- <PropertyGroup Label="Globals">
- <VCProjectVersion>16.0</VCProjectVersion>
- <Keyword>Win32Proj</Keyword>
- <ProjectGuid>{097be795-88e9-4e7b-92f3-f6dddbe3f012}</ProjectGuid>
- <RootNamespace>AmongUsAgent</RootNamespace>
- <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
- <ConfigurationType>DynamicLibrary</ConfigurationType>
- <UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
- <WholeProgramOptimization>true</WholeProgramOptimization>
- <CharacterSet>Unicode</CharacterSet>
- </PropertyGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <ImportGroup Label="ExtensionSettings">
- </ImportGroup>
- <ImportGroup Label="Shared">
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- </ImportGroup>
- <PropertyGroup Label="UserMacros" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <LinkIncremental>true</LinkIncremental>
- <OutDir>$(ProjectDir)build\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(ProjectDir)build\$(Platform)\$(Configuration)\Intermediate\</IntDir>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <LinkIncremental>false</LinkIncremental>
- <OutDir>$(ProjectDir)build\$(Platform)\$(Configuration)\</OutDir>
- <IntDir>$(ProjectDir)build\$(Platform)\$(Configuration)\Intermediate\</IntDir>
- </PropertyGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ConformanceMode>true</ConformanceMode>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <ClCompile>
- <WarningLevel>Level3</WarningLevel>
- <FunctionLevelLinking>true</FunctionLevelLinking>
- <IntrinsicFunctions>true</IntrinsicFunctions>
- <SDLCheck>true</SDLCheck>
- <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
- <ConformanceMode>true</ConformanceMode>
- </ClCompile>
- <Link>
- <SubSystem>Console</SubSystem>
- <EnableCOMDATFolding>true</EnableCOMDATFolding>
- <OptimizeReferences>true</OptimizeReferences>
- <GenerateDebugInformation>true</GenerateDebugInformation>
- </Link>
- </ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="logger.cpp" />
- <ClCompile Include="main.cpp" />
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="logger.h" />
- </ItemGroup>
- <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
- <ImportGroup Label="ExtensionTargets">
- </ImportGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <ItemGroup>
- <Filter Include="Source Files">
- <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
- <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
- </Filter>
- <Filter Include="Header Files">
- <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
- <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
- </Filter>
- <Filter Include="Resource Files">
- <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
- <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
- </Filter>
- </ItemGroup>
- <ItemGroup>
- <ClCompile Include="main.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="logger.cpp">
- <Filter>Source Files</Filter>
- </ClCompile>
- </ItemGroup>
- <ItemGroup>
- <ClInclude Include="logger.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- </ItemGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-#include "logger.h"
-#include <cstdio>
-#include <Windows.h>
-
-HANDLE h_out = nullptr, h_old_out = nullptr;
-HANDLE h_err = nullptr, h_old_err = nullptr;
-HANDLE h_in = nullptr, h_old_in = nullptr;
-
-namespace Logger
-{
- void Attach()
- {
- h_old_out = GetStdHandle(STD_OUTPUT_HANDLE);
- h_old_err = GetStdHandle(STD_ERROR_HANDLE);
- h_old_in = GetStdHandle(STD_INPUT_HANDLE);
-
- AllocConsole() && AttachConsole(GetCurrentProcessId());
-
- h_out = GetStdHandle(STD_OUTPUT_HANDLE);
- h_err = GetStdHandle(STD_ERROR_HANDLE);
- h_in = GetStdHandle(STD_INPUT_HANDLE);
-
- SetConsoleMode(h_out,
- ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
-
- SetConsoleMode(h_in,
- ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS |
- ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE);
- }
-
- void Detach()
- {
- if (h_out && h_err && h_in) {
- FreeConsole();
-
- if (h_old_out)
- {
- SetStdHandle(STD_OUTPUT_HANDLE, h_old_out);
- }
-
- if (h_old_err)
- {
- SetStdHandle(STD_ERROR_HANDLE, h_old_err);
- }
-
- if (h_old_in)
- {
- SetStdHandle(STD_INPUT_HANDLE, h_old_in);
- }
- }
- }
-
- bool Print(const char* fmt, ...)
- {
- if (!h_out)
- {
- return false;
- }
-
- char buf[1024];
- va_list va;
-
- va_start(va, fmt);
- _vsnprintf_s(buf, 1024, fmt, va);
- va_end(va);
-
- return !!WriteConsoleA(h_out, buf, static_cast<DWORD>(strlen(buf)), nullptr, nullptr);
- }
-
- char ReadKey()
- {
- if (!h_in)
- {
- return -1;
- }
-
- auto key = char{ 0 };
- auto keysread = DWORD{ 0 };
-
- ReadConsoleA(h_in, &key, 1, &keysread, nullptr);
-
- return key;
- }
-};
\ No newline at end of file
+++ /dev/null
-#pragma once
-
-namespace Logger
-{
- void Attach();
- void Detach();
- bool Print(const char* fmt, ...);
- char ReadKey();
-};
\ No newline at end of file
+++ /dev/null
-#include <Windows.h>
-#include "logger.h"
-
-DWORD WINAPI OnDllAttach(LPVOID base)
-{
- Logger::Attach();
- Logger::Print("[AmongUs.Agent] DLL Attached\n");
-
- FreeLibraryAndExitThread(static_cast<HMODULE>(base), 1);
-}
-
-BOOL WINAPI OnDllDetach()
-{
- Logger::Print("[AmongUs.Agent] DLL Detached\n");
- Logger::Detach();
-
- return true;
-}
-
-BOOL WINAPI DllMain(
- const _In_ HINSTANCE hinstDll,
- const _In_ DWORD fdwReason,
- const _In_opt_ LPVOID lpvReserved
-)
-{
- switch (fdwReason)
- {
- case DLL_PROCESS_ATTACH:
- DisableThreadLibraryCalls(hinstDll);
- CreateThread(nullptr, 0, OnDllAttach, hinstDll, 0, nullptr);
- return true;
-
- case DLL_PROCESS_DETACH:
- if (lpvReserved == nullptr)
- return OnDllDetach();
- return true;
- default:
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\AmongUs.Shared\AmongUs.Shared.csproj" />
- </ItemGroup>
-
-</Project>
+++ /dev/null
-using System;
-using System.IO;
-using AmongUs.Shared.Innersloth;
-
-namespace AmongUs.Client
-{
- internal static class Program
- {
- private static void Main(string[] args)
- {
- var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..\\LocalLow");
- var regionFile = Path.Combine(appData, "Innersloth", "Among Us", "regionInfo.dat");
- var region = new RegionInfo("Private", "192.168.1.211", new []
- {
- new ServerInfo("Private-Master-1", "192.168.1.211", 22023)
- });
-
- using (var file = File.Open(regionFile, FileMode.Create, FileAccess.Write))
- using (var writer = new BinaryWriter(file))
- {
- region.Serialize(writer);
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- </PropertyGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\submodules\Hazel-Networking\Hazel\Hazel.csproj" />
- <ProjectReference Include="..\AmongUs.Shared\AmongUs.Shared.csproj" />
- </ItemGroup>
-
- <ItemGroup>
- <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
- </ItemGroup>
-
-</Project>
+++ /dev/null
-namespace AmongUs.Server.Data
-{
- public static class DisconnectMessages
- {
- 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.";
-
- public const string Destroyed = "The game you tried to join is being destroyed. " +
- "Please create a new game.";
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Runtime.Serialization;
-
-namespace AmongUs.Server.Exceptions
-{
- public class AmongUsException : Exception
- {
- public AmongUsException()
- {
- }
-
- protected AmongUsException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- }
-
- public AmongUsException(string? message) : base(message)
- {
- }
-
- public AmongUsException(string? message, Exception? innerException) : base(message, innerException)
- {
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using AmongUs.Server.Net.Response;
-using Hazel;
-
-namespace AmongUs.Server.Extensions
-{
- internal static class ConnectionExtensions
- {
- public static void Send(this Connection connection, MessageBase message)
- {
- using (message)
- {
- connection.Send(message.Write());
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using AmongUs.Server.Data;
-using AmongUs.Server.Extensions;
-using AmongUs.Server.Net.Response;
-using AmongUs.Shared.Innersloth;
-using AmongUs.Shared.Innersloth.Data;
-using Hazel;
-using Serilog;
-using ILogger = Serilog.ILogger;
-
-namespace AmongUs.Server.Net
-{
- public class Client
- {
- private static readonly ILogger Logger = Log.ForContext<Client>();
-
- private readonly ClientManager _clientManager;
- private readonly GameManager _gameManager;
-
- public Client(ClientManager clientManager, GameManager gameManager, int id, string name, Connection connection)
- {
- _clientManager = clientManager;
- _gameManager = gameManager;
- Id = id;
- Name = name;
- Connection = connection;
- Connection.DataReceived += OnDataReceived;
- Connection.Disconnected += OnDisconnected;
- Player = new ClientPlayer(this);
- }
-
- public int Id { get; }
- public string Name { get; }
- public Connection Connection { get; }
- public ClientPlayer Player { get; }
-
- public void Send(MessageWriter writer)
- {
- Connection.Send(writer);
- }
-
- private bool IsPacketAllowed(MessageReader message, bool hostOnly)
- {
- var game = Player.Game;
- if (game == null)
- {
- return false;
- }
-
- // GameCode must match code of the current game assigned to the player.
- if (message.ReadInt32() != game.Code)
- {
- return false;
- }
-
- // Some packets should only be sent by the host of the game.
- if (hostOnly)
- {
- if (game.HostId == Id)
- {
- return true;
- }
-
- Logger.Warning("[{0}] Client sent packet only allowed by the host ({1}).", Id, game.HostId);
- return false;
- }
-
- return true;
- }
-
- private void OnDataReceived(DataReceivedEventArgs e)
- {
- try
- {
- while (true)
- {
- if (e.Message.Position >= e.Message.Length)
- {
- break;
- }
-
- OnMessageReceived(e.Message.ReadMessage(), e.SendOption);
- }
- }
- catch (Exception ex)
- {
- Logger.Error(ex, "Exception caught in client data handler.");
- Connection.Send(new Message1DisconnectReason(DisconnectReason.Custom, DisconnectMessages.Error));
- }
- }
-
- private void OnMessageReceived(MessageReader message, SendOption sendOption)
- {
- var flag = (RequestFlag) message.Tag;
-
- Logger.Verbose("[{0}] Server got {1}.", Id, flag);
-
- switch (flag)
- {
- case RequestFlag.HostGame:
- {
- // Read game settings.
- var gameInfoBytes = message.ReadBytesAndSize();
- var gameInfo = GameOptionsData.Deserialize(gameInfoBytes);
-
- // Create game.
- var game = _gameManager.Create(this, gameInfo);
- if (game == null)
- {
- Connection.Send(new Message1DisconnectReason(DisconnectReason.ServerFull));
- return;
- }
-
- // Code in the packet below will be used in JoinGame.
- using (var writer = MessageWriter.Get(SendOption.Reliable))
- {
- writer.StartMessage(0);
- writer.Write(game.Code);
- writer.EndMessage();
-
- Connection.Send(writer);
- }
- break;
- }
-
- case RequestFlag.JoinGame:
- {
- var gameCode = message.ReadInt32();
- var unknown = message.ReadByte();
- var game = _gameManager.Find(gameCode);
- if (game == null)
- {
- Connection.Send(new Message1DisconnectReason(DisconnectReason.GameMissing));
- return;
- }
-
- game.HandleJoinGame(Player);
- break;
- }
-
- case RequestFlag.StartGame:
- {
- if (!IsPacketAllowed(message, true))
- {
- return;
- }
-
- Player.Game.HandleStartGame(message);
- break;
- }
-
- // No idea how this flag is triggered.
- case RequestFlag.RemoveGame:
- break;
-
- case RequestFlag.RemovePlayer:
- {
- if (!IsPacketAllowed(message, true))
- {
- return;
- }
-
- var playerId = message.ReadPackedInt32();
- var reason = message.ReadByte();
-
- Player.Game.HandleRemovePlayer(playerId, (DisconnectReason) reason);
- break;
- }
-
- case RequestFlag.GameData:
- case RequestFlag.GameDataTo:
- {
- if (!IsPacketAllowed(message, false))
- {
- return;
- }
-
- // Broadcast packet to all other players.
- using (var writer = MessageWriter.Get(sendOption))
- {
- if (flag == RequestFlag.GameDataTo)
- {
- var target = message.ReadPackedInt32();
- writer.CopyFrom(message);
- Player.Game.SendTo(writer, target);
- }
- else
- {
- writer.CopyFrom(message);
- Player.Game.SendToAllExcept(writer, Player);
- }
- }
- break;
- }
-
- case RequestFlag.EndGame:
- {
- if (!IsPacketAllowed(message, true))
- {
- return;
- }
-
- Player.Game.HandleEndGame(message);
- break;
- }
-
- case RequestFlag.AlterGame:
- {
- if (!IsPacketAllowed(message, true))
- {
- return;
- }
-
- if (message.ReadByte() != (byte) AlterGameTags.ChangePrivacy)
- {
- return;
- }
-
- var isPublic = message.ReadByte() == 1;
-
- Player.Game.HandleAlterGame(message, Player, isPublic);
- break;
- }
-
- case RequestFlag.KickPlayer:
- {
- if (!IsPacketAllowed(message, true))
- {
- return;
- }
-
- var playerId = message.ReadPackedInt32();
- var isBan = message.ReadBoolean();
-
- Player.Game.HandleKickPlayer(playerId, isBan);
- break;
- }
-
- default:
- Logger.Warning("Server received unknown flag {0}.", flag);
- break;
- }
-
- if (flag != RequestFlag.GameData &&
- flag != RequestFlag.GameDataTo &&
- flag != RequestFlag.EndGame &&
- message.Position < message.Length)
- {
- Logger.Warning("Server did not consume all bytes from {0} ({1} < {2}).",
- flag,
- message.Position,
- message.Length);
- }
- }
-
- private void OnDisconnected(object sender, DisconnectedEventArgs e)
- {
- try
- {
- if (Player.Game != null)
- {
- Player.Game.HandleRemovePlayer(Id, DisconnectReason.ExitGame);
- }
- }
- catch (Exception ex)
- {
- Logger.Error(ex, "Exception caught in client disconnection.");
- }
-
- _clientManager.Remove(this);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Concurrent;
-using AmongUs.Server.Exceptions;
-using Serilog;
-
-namespace AmongUs.Server.Net
-{
- public class ClientManager
- {
- private static readonly ILogger Logger = Log.ForContext<ClientManager>();
-
- private readonly ConcurrentDictionary<int, Client> _clients;
- private readonly object _idLock;
- private int _idLast;
-
- public ClientManager()
- {
- _clients = new ConcurrentDictionary<int, Client>();
- _idLock = new object();
- _idLast = 0;
- }
-
- // No idea what a good way for this is.
- public int NextId()
- {
- lock (_idLock)
- {
- // 3 Attempts.
- for (var i = 0; i < 3; i++)
- {
- // It is important that ids start from 1, a 0 id causes issues.
- var result = ++_idLast;
-
- if (_idLast == int.MaxValue)
- {
- _idLast = 0;
- }
-
- if (_clients.ContainsKey(_idLast))
- {
- continue;
- }
-
- return result;
- }
-
- throw new AmongUsException("Unable to generate a client id.");
- }
- }
-
- public void Add(Client client)
- {
- Logger.Information("Client connected.");
-
- _clients.TryAdd(client.Id, client);
- }
-
- public void Remove(Client client)
- {
- Logger.Information("Client disconnected.");
-
- _clients.TryRemove(client.Id, out _);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using AmongUs.Shared.Innersloth.Data;
-
-namespace AmongUs.Server.Net
-{
- public class ClientPlayer
- {
- public ClientPlayer(Client client)
- {
- Client = client;
- }
-
- public Client Client { get; }
- public Game Game { get; set; }
- public LimboStates LimboState { get; set; }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using AmongUs.Server.Data;
-using AmongUs.Server.Exceptions;
-using AmongUs.Server.Extensions;
-using AmongUs.Server.Net.Response;
-using AmongUs.Shared.Innersloth;
-using AmongUs.Shared.Innersloth.Data;
-using Hazel;
-using Serilog;
-using ILogger = Serilog.ILogger;
-
-namespace AmongUs.Server.Net
-{
- public class Game
- {
- private static readonly ILogger Logger = Log.ForContext<Game>();
-
- private readonly GameManager _gameManager;
- private readonly ConcurrentDictionary<int, ClientPlayer> _players;
- private readonly HashSet<IPAddress> _bannedIps;
-
- public Game(GameManager gameManager, int code, GameOptionsData options)
- {
- _gameManager = gameManager;
- _players = new ConcurrentDictionary<int, ClientPlayer>();
- _bannedIps = new HashSet<IPAddress>();
-
- Code = code;
- CodeStr = GameCode.IntToGameName(code);
- HostId = -1;
- GameState = GameStates.NotStarted;
- Options = options;
- }
-
- public int Code { get; }
- public string CodeStr { get; }
- public bool IsPublic { get; private set; }
- public int HostId { get; private set; }
- public GameStates GameState { get; private set; }
- public GameOptionsData Options { get; }
-
- public void SendToAllExcept(MessageWriter message, ClientPlayer sender)
- {
- foreach (var (_, player) in _players.Where(x => x.Value != sender))
- {
- if (player.Client.Connection.State != ConnectionState.Connected)
- {
- Logger.Warning("[{0}] Tried to sent data to a disconnected player ({1}).", sender?.Client.Id, player.Client.Id);
- continue;
- }
-
- player.Client.Send(message);
- }
- }
-
- public void SendTo(MessageWriter message, int playerId)
- {
- if (_players.TryGetValue(playerId, out var player))
- {
- if (player.Client.Connection.State != ConnectionState.Connected)
- {
- Logger.Warning("[{0}] Sending data to {1} failed, player is not connected.", CodeStr, player.Client.Id);
- return;
- }
-
- player.Client.Send(message);
- }
- else
- {
- Logger.Warning("[{0}] Sending data to {1} failed, player does not exist.", CodeStr, playerId);
- }
- }
-
- public void HandleStartGame(MessageReader message)
- {
- GameState = GameStates.Started;
-
- using (var packet = MessageWriter.Get(SendOption.Reliable))
- {
- packet.CopyFrom(message);
- SendToAllExcept(packet, null);
- }
- }
-
- public void HandleJoinGame(ClientPlayer sender)
- {
- if (_bannedIps.Contains(sender.Client.Connection.EndPoint.Address))
- {
- sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.Banned));
- return;
- }
-
- switch (GameState)
- {
- case GameStates.NotStarted:
- HandleJoinGameNew(sender);
- break;
- case GameStates.Ended:
- HandleJoinGameNext(sender);
- break;
- case GameStates.Started:
- sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameStarted));
- return;
- case GameStates.Destroyed:
- sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.Custom, DisconnectMessages.Destroyed));
- return;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
- public void HandleEndGame(MessageReader message)
- {
- GameState = GameStates.Ended;
-
- // Broadcast end of the game.
- using (var packet = MessageWriter.Get(SendOption.Reliable))
- {
- packet.CopyFrom(message);
- SendToAllExcept(packet, null);
- }
-
- // Remove all players from this game.
- foreach (var player in _players)
- {
- player.Value.Game = null;
- }
-
- _players.Clear();
- }
-
- public void HandleAlterGame(MessageReader message, ClientPlayer sender, bool isPublic)
- {
- IsPublic = isPublic;
-
- using (var packet = MessageWriter.Get(SendOption.Reliable))
- {
- packet.CopyFrom(message);
- SendToAllExcept(packet, sender);
- }
- }
-
- public void HandleRemovePlayer(int playerId, DisconnectReason reason)
- {
- if (_players.TryRemove(playerId, out var player))
- {
- player.Game = null;
- }
-
- Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, player?.Client.Name, playerId);
-
- // Game is empty, remove it.
- if (_players.Count == 0)
- {
- GameState = GameStates.Destroyed;
-
- // Remove instance reference.
- _gameManager.Remove(Code);
- return;
- }
-
- // Host migration.
- if (HostId == playerId)
- {
- var newHost = _players.First().Value;
- HostId = newHost.Client.Id;
- Logger.Information("{0} - Assigned {1} ({2}) as new host.", CodeStr, newHost.Client.Name, newHost.Client.Id);
- }
-
- using (var packet = MessageWriter.Get(SendOption.Reliable))
- {
- WriteRemovePlayerMessage(packet, false, playerId, reason);
- SendToAllExcept(packet, player);
- }
- }
-
- public void HandleKickPlayer(int playerId, bool isBan)
- {
- _players.TryGetValue(playerId, out var p);
- Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, p?.Client.Name, playerId);
-
- using (var message = MessageWriter.Get(SendOption.Reliable))
- {
- WriteKickPlayerMessage(message, false, playerId, isBan);
- SendToAllExcept(message, null);
-
- if (_players.TryRemove(playerId, out var player))
- {
- player.Game = null;
-
- if (isBan)
- {
- _bannedIps.Add(player.Client.Connection.EndPoint.Address);
- }
- }
-
- WriteRemovePlayerMessage(message, true, playerId, isBan
- ? DisconnectReason.Banned
- : DisconnectReason.Kicked);
- SendToAllExcept(message, player);
- }
- }
-
- private void HandleJoinGameNew(ClientPlayer sender)
- {
- Logger.Information("{0} - Player {1} ({2}) is joining.", CodeStr, sender.Client.Name, sender.Client.Id);
-
- // Store player.
- if (!_players.TryAdd(sender.Client.Id, sender))
- {
- throw new AmongUsException("Failed to add player to game.");
- }
-
- // Assign player to this game for future packets.
- sender.Game = this;
-
- // Assign hostId if none is set.
- if (HostId == -1)
- {
- HostId = sender.Client.Id;
- }
-
- if (HostId == sender.Client.Id)
- {
- sender.LimboState = LimboStates.NotLimbo;
- }
-
- using (var message = MessageWriter.Get(SendOption.Reliable))
- {
- WriteJoinedGameMessage(message, false, sender);
- WriteAlterGameMessage(message, false);
-
- sender.Client.Send(message);
-
- BroadcastJoinMessage(message, true, sender);
- }
- }
-
- private void HandleJoinGameNext(ClientPlayer sender)
- {
- Logger.Information("{0} - Player {1} ({2}) is rejoining.", CodeStr, sender.Client.Name, sender.Client.Id);
-
- if (sender.Client.Id == HostId)
- {
- GameState = GameStates.NotStarted;
- HandleJoinGameNew(sender);
-
- using (var message = MessageWriter.Get(SendOption.Reliable))
- {
- foreach (var (_, player) in _players.Where(x => x.Value != sender))
- {
- WriteJoinedGameMessage(message, true, player);
- WriteAlterGameMessage(message, false);
- player.Client.Send(message);
- }
- }
-
- return;
- }
-
- if (_players.Count >= 9)
- {
- sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameFull));
- return;
- }
-
- // Store player.
- if (!_players.TryAdd(sender.Client.Id, sender))
- {
- throw new AmongUsException("Failed to add player to game.");
- }
-
- // Assign player to this game for future packets.
- sender.Game = this;
-
- // Limbo, yes.
- sender.LimboState = LimboStates.WaitingForHost;
-
- using (var packet = MessageWriter.Get(SendOption.Reliable))
- {
- WriteWaitForHostMessage(packet, false, sender);
- sender.Client.Send(packet);
-
- BroadcastJoinMessage(packet, true, sender);
- }
- }
-
- private void WriteRemovePlayerMessage(MessageWriter message, bool clear, int playerId, DisconnectReason reason)
- {
- // Only a subset of DisconnectReason shows an unique message.
- // ExitGame, Banned and Kicked.
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.RemovePlayer);
- message.Write(Code);
- message.Write(playerId);
- message.Write(HostId);
- message.Write((byte) reason);
- message.EndMessage();
- }
-
- private void WriteJoinedGameMessage(MessageWriter message, bool clear, ClientPlayer player)
- {
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.JoinedGame);
- message.Write(Code);
- message.Write(player.Client.Id);
- message.Write(HostId);
- message.WritePacked(_players.Count - 1);
-
- foreach (var (_, p) in _players.Where(x => x.Value != player))
- {
- message.WritePacked(p.Client.Id);
- }
-
- message.EndMessage();
- }
-
- private void WriteAlterGameMessage(MessageWriter message, bool clear)
- {
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.AlterGame);
- message.Write(Code);
- message.Write((byte) AlterGameTags.ChangePrivacy);
- message.Write(IsPublic);
- message.EndMessage();
- }
-
- private void WriteKickPlayerMessage(MessageWriter message, bool clear, int playerId, bool isBan)
- {
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.KickPlayer);
- message.Write(Code);
- message.WritePacked(playerId);
- message.Write(isBan);
- message.EndMessage();
- }
-
- private void WriteWaitForHostMessage(MessageWriter message, bool clear, ClientPlayer player)
- {
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.WaitForHost);
- message.Write(Code);
- message.Write(player.Client.Id);
- message.EndMessage();
- }
-
- private void BroadcastJoinMessage(MessageWriter message, bool clear, ClientPlayer player)
- {
- if (clear)
- {
- message.Clear(SendOption.Reliable);
- }
-
- message.StartMessage((byte) RequestFlag.JoinGame);
- message.Write(Code);
- message.Write(player.Client.Id);
- message.Write(HostId);
- message.EndMessage();
-
- SendToAllExcept(message, player);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Concurrent;
-using AmongUs.Shared.Innersloth;
-using Serilog;
-
-namespace AmongUs.Server.Net
-{
- public class GameManager
- {
- private static readonly ILogger Logger = Log.ForContext<GameManager>();
-
- private readonly ConcurrentDictionary<int, Game> _games;
-
- public GameManager()
- {
- _games = new ConcurrentDictionary<int, Game>();
- }
-
- public Game Create(Client owner, GameOptionsData options)
- {
- var gameCode = GameCode.GenerateCode(6);
- var game = new Game(this, gameCode, options);
-
- if (_games.TryAdd(gameCode, game))
- {
- Logger.Debug("Created game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
- return game;
- }
-
- Logger.Warning("Failed to create game.");
- return null;
- }
-
- public Game Find(int gameCode)
- {
- _games.TryGetValue(gameCode, out var game);
- return game;
- }
-
- public void Remove(int gameCode)
- {
- Logger.Debug("Remove game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
- _games.TryRemove(gameCode, out _);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Server.Net
-{
- public enum GameStates : byte
- {
- NotStarted = 0,
- Started = 1,
- Ended = 2,
- Destroyed = 3
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Net;
-using AmongUs.Server.Extensions;
-using AmongUs.Server.Net.Response;
-using AmongUs.Shared.Innersloth.Data;
-using Hazel;
-using Hazel.Udp;
-using Serilog;
-using ILogger = Serilog.ILogger;
-
-namespace AmongUs.Server.Net
-{
- public class Matchmaker
- {
- private static readonly ILogger Logger = Log.ForContext<Matchmaker>();
-
- private readonly GameManager _gameManager;
- private readonly ClientManager _clientManager;
- private readonly UdpConnectionListener _connection;
-
- public Matchmaker(IPAddress ip, int port)
- {
- _gameManager = new GameManager();
- _clientManager = new ClientManager();
- _connection = new UdpConnectionListener(new IPEndPoint(ip, port), IPMode.IPv4, s =>
- {
- Logger.Warning("Log from Hazel: {0}", s);
- });
-
- _connection.NewConnection += OnNewConnection;
- }
-
- private void OnNewConnection(NewConnectionEventArgs e)
- {
- // Handshake.
- var clientVersion = e.HandshakeData.ReadInt32();
- var clientName = e.HandshakeData.ReadString();
-
- e.HandshakeData.Recycle();
-
- if (clientVersion != 50516550)
- {
- e.Connection.Send(new Message1DisconnectReason(DisconnectReason.IncorrectVersion));
- return;
- }
-
- // Register client.
- _clientManager.Add(new Client(_clientManager, _gameManager, _clientManager.NextId(), clientName, e.Connection));
- }
-
- public void Start()
- {
- _connection.Start();
- }
-
- public void Stop()
- {
- _connection.Dispose();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using AmongUs.Shared.Innersloth.Data;
-using Hazel;
-
-namespace AmongUs.Server.Net.Response
-{
- public class Message1DisconnectReason : MessageBase
- {
- private readonly DisconnectReason _reason;
- private readonly string _message;
-
- // Notes:
- // - Specifying no reason does something with ban minutes left.
- // - (?) You were disconnected because Among Us was suspended by another app.
- public Message1DisconnectReason(DisconnectReason reason, string message = null) : base(SendOption.Reliable, MessageFlag.DisconnectReason)
- {
- _reason = reason;
- _message = message;
- }
-
- protected override void WriteMessage(MessageWriter writer)
- {
- writer.Write((int) _reason);
-
- if (_reason == DisconnectReason.Custom)
- {
- writer.Write(_message);
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using Hazel;
-
-namespace AmongUs.Server.Net.Response
-{
- public abstract class MessageBase : IDisposable
- {
- private readonly MessageWriter _writer;
- private readonly MessageFlag _flag;
-
- protected MessageBase(SendOption option, MessageFlag flag)
- {
- _writer = MessageWriter.Get(option);
- _flag = flag;
- }
-
- public MessageWriter Write()
- {
- _writer.StartMessage((byte) _flag);
- WriteMessage(_writer);
- _writer.EndMessage();
- return _writer;
- }
-
- protected abstract void WriteMessage(MessageWriter writer);
-
- public void Dispose()
- {
- _writer.Dispose();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Server.Net.Response
-{
- public enum MessageFlag : byte
- {
- DisconnectReason = 1
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Net;
-using System.Threading;
-using AmongUs.Server.Net;
-using Serilog;
-
-namespace AmongUs.Server
-{
- internal static class Program
- {
- private static readonly ManualResetEvent QuitEvent = new ManualResetEvent(false);
-
- private static void Main(string[] args)
- {
- // Listen for CTRL+C.
- Console.CancelKeyPress += (sender, e) =>
- {
- e.Cancel = true;
- QuitEvent.Set();
- };
-
- // Configure logger.
- Log.Logger = new LoggerConfiguration()
-#if DEBUG
- .MinimumLevel.Verbose()
-#else
- .MinimumLevel.Information()
-#endif
- .WriteTo.Console()
- .CreateLogger();
-
- // Initialize matchmaker.
- var matchMaker = new Matchmaker(IPAddress.Any, 22023);
- matchMaker.Start();
- Log.Logger.Information("Matchmaker is running on *:22023.");
- QuitEvent.WaitOne();
- Log.Logger.Warning("Matchmaker is shutting down!");
- matchMaker.Stop();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- </PropertyGroup>
-
-</Project>
+++ /dev/null
-namespace AmongUs.Shared.Innersloth.Data
-{
- public enum AlterGameTags : byte
- {
- ChangePrivacy = 1
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Shared.Innersloth.Data
-{
- public enum DisconnectReason
- {
- ExitGame = 0,
- Destroy = 16,
- // The game you tried to join is full.
- // Check with the host to see if you can join next round.
- GameFull = 1,
- // The game you tried to join already started.
- // Check with the host to see if you can join next round.
- GameStarted = 2,
- // Could not find the game you're looking for.
- GameMissing = 3,
- IncorrectGame = 18,
- // For these a message can be given, specifying an empty message shows
- // "An unknown error disconnected you from the server."
- CustomMessage1 = 4,
- Custom = 8,
- // CustomMessage3 = 11,
- // CustomMessage4 = 12,
- // CustomMessage5 = 13,
- // CustomMessage6 = 14,
- // CustomMessage7 = 15,
- // You are running an older version of the game.
- // Please update to play with others.
- IncorrectVersion = 5,
- // You cannot rejoin that room.
- // You were banned
- Banned = 6,
- // You can rejoin if the room hasn't started
- // You were kicked
- Kicked = 7,
- // Server refused username: %USERNAME%
- InvalidName = 9,
- // You were banned for hacking.
- // Please stop.
- Hacking = 10,
- // You disconnected from the host.
- // If this happens often, check your WiFi strength.
- //
- // You disconnected from the server.
- // If this happens often, check your network strength.
- // This may also be a server issue.
- Error = 17,
- // The server stopped this game. Possibly due to inactivity.
- ServerRequest = 19,
- // The Among Us servers are overloaded.
- // Sorry! Please try again later!
- ServerFull = 20,
- FocusLostBackground = 207,
- IntentionalLeaving = 208,
- FocusLost = 209,
- NewConnection = 210
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace AmongUs.Shared.Innersloth.Data
-{
- [Flags]
- public enum GameKeywords : uint
- {
- All = 0,
- Other = 1,
- Spanish = 2,
- Korean = 4,
- Russian = 8,
- Portuguese = 16,
- Arabic = 32,
- Filipone = 64,
- Polish = 128,
- English = 256
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Shared.Innersloth.Data
-{
- public enum GameStates : byte
- {
- NotStarted,
- Started,
- Ended,
- Destroyed
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Shared.Innersloth.Data
-{
- public enum LimboStates
- {
- PreSpawn,
- NotLimbo,
- WaitingForHost,
- }
-}
\ No newline at end of file
+++ /dev/null
-namespace AmongUs.Shared.Innersloth.Data
-{
- public enum RequestFlag : byte
- {
- HostGame = 0,
- JoinGame = 1,
- StartGame = 2,
- RemoveGame = 3,
- RemovePlayer = 4,
- GameData = 5,
- GameDataTo = 6,
- JoinedGame = 7,
- EndGame = 8,
- GetGameList = 9,
- AlterGame = 10,
- KickPlayer = 11,
- WaitForHost = 12,
- Redirect = 13,
- ReselectServer = 14,
- GetGameListV2 = 16,
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Buffers.Binary;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace AmongUs.Shared.Innersloth
-{
- public static class GameCode
- {
- private const string V2 = "QWXRTYLPESDFGHUJKZOCVBINMA";
- private static readonly int[] V2Map = {
- 25,
- 21,
- 19,
- 10,
- 8,
- 11,
- 12,
- 13,
- 22,
- 15,
- 16,
- 6,
- 24,
- 23,
- 18,
- 7,
- 0,
- 3,
- 9,
- 4,
- 14,
- 20,
- 1,
- 2,
- 5,
- 17
- };
- private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider();
-
- public static string IntToGameName(int input)
- {
- // V2.
- if (input < -1)
- {
- return IntToGameNameV2(input);
- }
-
- // V1.
- Span<byte> code = stackalloc byte[4];
- BinaryPrimitives.WriteInt32LittleEndian(code, input);
- return Encoding.UTF8.GetString(code.Slice(0, 4));
- }
-
- private static string IntToGameNameV2(int input)
- {
- var a = input & 0x3FF;
- var b = (input >> 10) & 0xFFFFF;
-
- return new string(new []
- {
- V2[a % 26],
- V2[a / 26],
- V2[b % 26],
- V2[b / 26 % 26],
- V2[b / (26 * 26) % 26],
- V2[b / (26 * 26 * 26) % 26]
- });
- }
-
- public static int GameNameToInt(string code)
- {
- var upper = code.ToUpperInvariant();
- if (upper.Any(x => !char.IsLetter(x)))
- {
- return -1;
- }
-
- var len = code.Length;
- if (len == 6)
- {
- return GameNameToIntV2(upper);
- }
-
- if (len == 4)
- {
- return code[0] | ((code[1] | ((code[2] | (code[3] << 8)) << 8)) << 8);
- }
-
- return -1;
- }
-
- private static int GameNameToIntV2(string code)
- {
- var a = V2Map[code[0] - 65];
- var b = V2Map[code[1] - 65];
- var c = V2Map[code[2] - 65];
- var d = V2Map[code[3] - 65];
- var e = V2Map[code[4] - 65];
- var f = V2Map[code[5] - 65];
-
- var one = (a + 26 * b) & 0x3FF;
- var two = (c + 26 * (d + 26 * (e + 26 * f)));
-
- return (int) (one | ((two << 10) & 0x3FFFFC00) | 0x80000000);
- }
-
- public static int GenerateCode(int len)
- {
- if (len != 4 && len != 6)
- {
- throw new ArgumentException("should be 4 or 6", nameof(len));
- }
-
- // Generate random bytes.
- Span<byte> data = stackalloc byte[len];
- Random.GetBytes(data);
-
- // Convert to their char representation.
- Span<char> dataChar = stackalloc char[len];
- for (var i = 0; i < len; i++)
- {
- dataChar[i] = V2[V2Map[data[i] % 26]];
- }
-
- return GameNameToInt(new string(dataChar));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using AmongUs.Shared.Innersloth.Data;
-
-namespace AmongUs.Shared.Innersloth
-{
- public class GameOptionsData
- {
- public byte Version { get; set; }
- public byte MaxPlayers { get; set; }
- public GameKeywords Keywords { get; set; }
- public byte MapId { get; set; }
- public float PlayerSpeedMod { get; set; }
- public float CrewLightMod { get; set; }
- public float ImpostorLightMod { get; set; }
- public float KillCooldown { get; set; }
- public int NumCommonTasks { get; set; }
- public int NumLongTasks { get; set; }
- public int NumShortTasks { get; set; }
- public int NumEmergencyMeetings { get; set; }
- public int EmergencyCooldown { get; set; }
- public int NumImpostors { get; set; }
- public bool GhostsDoTasks { get; set; }
- public int KillDistance { get; set; }
- public int DiscussionTime { get; set; }
- public int VotingTime { get; set; }
- public bool ConfirmImpostor { get; set; }
- public bool VisualTasks { get; set; }
- public bool IsDefaults { get; set; }
-
- public void Serialize(BinaryWriter writer, byte version)
- {
- throw new NotImplementedException();
- }
-
- public static GameOptionsData Deserialize(byte[] bytes)
- {
- using (var stream = new MemoryStream(bytes))
- using (var reader = new BinaryReader(stream))
- {
- var result = new GameOptionsData();
-
- result.Version = reader.ReadByte();
- result.MaxPlayers = reader.ReadByte();
- result.Keywords = (GameKeywords) reader.ReadUInt32();
- result.MapId = reader.ReadByte();
- result.PlayerSpeedMod = reader.ReadSingle();
- result.CrewLightMod = reader.ReadSingle();
- result.ImpostorLightMod = reader.ReadSingle();
- result.KillCooldown = reader.ReadSingle();
- result.NumCommonTasks = reader.ReadByte();
- result.NumLongTasks = reader.ReadByte();
- result.NumShortTasks = reader.ReadByte();
- result.NumEmergencyMeetings = reader.ReadInt32();
- result.NumImpostors = reader.ReadByte();
- result.KillDistance = reader.ReadByte();
- result.DiscussionTime = reader.ReadInt32();
- result.VotingTime = reader.ReadInt32();
- result.IsDefaults = reader.ReadBoolean();
-
- if (result.Version > 1)
- {
- result.EmergencyCooldown = reader.ReadByte();
- }
-
- if (result.Version > 2)
- {
- result.ConfirmImpostor = reader.ReadBoolean();
- result.VisualTasks = reader.ReadBoolean();
- }
-
- return result;
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.Collections.Generic;
-using System.IO;
-
-namespace AmongUs.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);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System.IO;
-using System.Net;
-
-namespace AmongUs.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);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Text;
-
-namespace AmongUs.Shared.Utils
-{
- public static class HexUtils
- {
- public static string HexDump(byte[] bytes, int bytesPerLine = 16)
- {
- if (bytes == null) return "<null>";
- int bytesLength = bytes.Length;
-
- char[] HexChars = "0123456789ABCDEF".ToCharArray();
-
- int firstHexColumn =
- 8 // 8 characters for the address
- + 3; // 3 spaces
-
- int firstCharColumn = firstHexColumn
- + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
- + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
- + 2; // 2 spaces
-
- int lineLength = firstCharColumn
- + bytesPerLine // - characters to show the ascii value
- + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2)
-
- char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
- int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine;
- StringBuilder result = new StringBuilder(expectedLines * lineLength);
-
- for (int i = 0; i < bytesLength; i += bytesPerLine)
- {
- line[0] = HexChars[(i >> 28) & 0xF];
- line[1] = HexChars[(i >> 24) & 0xF];
- line[2] = HexChars[(i >> 20) & 0xF];
- line[3] = HexChars[(i >> 16) & 0xF];
- line[4] = HexChars[(i >> 12) & 0xF];
- line[5] = HexChars[(i >> 8) & 0xF];
- line[6] = HexChars[(i >> 4) & 0xF];
- line[7] = HexChars[(i >> 0) & 0xF];
-
- int hexColumn = firstHexColumn;
- int charColumn = firstCharColumn;
-
- for (int j = 0; j < bytesPerLine; j++)
- {
- if (j > 0 && (j & 7) == 0) hexColumn++;
- if (i + j >= bytesLength)
- {
- line[hexColumn] = ' ';
- line[hexColumn + 1] = ' ';
- line[charColumn] = ' ';
- }
- else
- {
- byte b = bytes[i + j];
- line[hexColumn] = HexChars[(b >> 4) & 0xF];
- line[hexColumn + 1] = HexChars[b & 0xF];
- line[charColumn] = (b < 32 ? '·' : (char)b);
- }
- hexColumn += 3;
- charColumn++;
- }
- result.Append(line);
- }
- return result.ToString();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
-
- <IsPackable>false</IsPackable>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
- <PackageReference Include="xunit" Version="2.4.0" />
- <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
- <PackageReference Include="coverlet.collector" Version="1.2.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\AmongUs.Shared\AmongUs.Shared.csproj" />
- </ItemGroup>
-
-</Project>
+++ /dev/null
-using AmongUs.Shared.Innersloth;
-using Xunit;
-
-namespace AmongUs.Tests
-{
- public class GameCodeTests
- {
- [Fact]
- public void CodeV1()
- {
- const string code = "ABCD";
- const int codeInt = 0x44434241;
-
- Assert.Equal(code, GameCode.IntToGameName(codeInt));
- Assert.Equal(codeInt, GameCode.GameNameToInt(code));
- }
-
- [Fact]
- public void CodeV2()
- {
- const string code = "ABCDEF";
- const int codeInt = -1943683525;
-
- Assert.Equal(code, GameCode.IntToGameName(codeInt));
- Assert.Equal(codeInt, GameCode.GameNameToInt(code));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>net452</TargetFramework>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="Pcap.Net.x64" Version="1.0.4.1" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\submodules\Hazel-Networking\Hazel\Hazel.csproj" />
- </ItemGroup>
-
-</Project>
+++ /dev/null
-using System;
-using System.Text;
-
-namespace AmongUs.Tools.Proxy
-{
- public static class HexUtils
- {
- public static string HexDump(byte[] bytes, int bytesPerLine = 16)
- {
- if (bytes == null) return "<null>";
- int bytesLength = bytes.Length;
-
- char[] HexChars = "0123456789ABCDEF".ToCharArray();
-
- int firstHexColumn =
- 8 // 8 characters for the address
- + 3; // 3 spaces
-
- int firstCharColumn = firstHexColumn
- + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
- + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
- + 2; // 2 spaces
-
- int lineLength = firstCharColumn
- + bytesPerLine // - characters to show the ascii value
- + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2)
-
- char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
- int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine;
- StringBuilder result = new StringBuilder(expectedLines * lineLength);
-
- for (int i = 0; i < bytesLength; i += bytesPerLine)
- {
- line[0] = HexChars[(i >> 28) & 0xF];
- line[1] = HexChars[(i >> 24) & 0xF];
- line[2] = HexChars[(i >> 20) & 0xF];
- line[3] = HexChars[(i >> 16) & 0xF];
- line[4] = HexChars[(i >> 12) & 0xF];
- line[5] = HexChars[(i >> 8) & 0xF];
- line[6] = HexChars[(i >> 4) & 0xF];
- line[7] = HexChars[(i >> 0) & 0xF];
-
- int hexColumn = firstHexColumn;
- int charColumn = firstCharColumn;
-
- for (int j = 0; j < bytesPerLine; j++)
- {
- if (j > 0 && (j & 7) == 0) hexColumn++;
- if (i + j >= bytesLength)
- {
- line[hexColumn] = ' ';
- line[hexColumn + 1] = ' ';
- line[charColumn] = ' ';
- }
- else
- {
- byte b = bytes[i + j];
- line[hexColumn] = HexChars[(b >> 4) & 0xF];
- line[hexColumn + 1] = HexChars[b & 0xF];
- line[charColumn] = (b < 32 ? '·' : (char)b);
- }
- hexColumn += 3;
- charColumn++;
- }
- result.Append(line);
- }
- return result.ToString();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Hazel;
-using Hazel.Udp;
-using PcapDotNet.Core;
-using PcapDotNet.Packets;
-
-namespace AmongUs.Tools.Proxy
-{
- internal static class Program
- {
- private const string DeviceName = "Intel(R) I211 Gigabit Network Connection";
-
- private static readonly Dictionary<byte, string> TagMap = new Dictionary<byte, string>
- {
- {0, "HostGame"},
- {1, "JoinGame"},
- {2, "StartGame"},
- {3, "RemoveGame"},
- {4, "RemovePlayer"},
- {5, "GameData"},
- {6, "GameDataTo"},
- {7, "JoinedGame"},
- {8, "EndGame"},
- {9, "GetGameList"},
- {10, "AlterGame"},
- {11, "KickPlayer"},
- {12, "WaitForHost"},
- {13, "Redirect"},
- {14, "ReselectServer"},
- {16, "GetGameListV2"}
- };
-
- private static void Main(string[] args)
- {
- var devices = LivePacketDevice.AllLocalMachine;
- if (devices.Count == 0)
- {
- Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
- return;
- }
-
- var device = devices.FirstOrDefault(x => x.Description.Contains(DeviceName));
- if (device == null)
- {
- Console.WriteLine("Unable to find configured device.");
- return;
- }
-
- using (var communicator = device.Open(65536, PacketDeviceOpenAttributes.Promiscuous, 1000))
- {
- using (var filter = communicator.CreateFilter("udp and port 22023"))
- {
- communicator.SetFilter(filter);
- }
-
- communicator.ReceivePackets(0, PacketHandler);
- }
- }
-
- private static void PacketHandler(Packet packet)
- {
- var ip = packet.Ethernet.IpV4;
- var ipSrc = ip.Source.ToString();
- var udp = ip.Udp;
-
- // True if this is our own packet.
- using (var stream = udp.Payload.ToMemoryStream())
- {
- var reader = MessageReader.Get(stream.ToArray());
- if (reader.Buffer[0] == (byte) SendOption.Reliable)
- {
- reader.Offset = 3;
- reader.Length = udp.Payload.Length - 3;
- reader.Position = 0;
- }
- else if (reader.Buffer[0] == (byte) UdpSendOption.Acknowledgement ||
- reader.Buffer[0] == (byte) UdpSendOption.Ping ||
- reader.Buffer[0] == (byte) UdpSendOption.Hello ||
- reader.Buffer[0] == (byte) UdpSendOption.Disconnect)
- {
- return;
- }
- else
- {
- reader.Offset = 1;
- reader.Length = udp.Payload.Length - 1;
- reader.Position = 0;
- }
-
- var isSent = ipSrc.StartsWith("192.");
-
- while (true)
- {
- if (reader.Position >= reader.Length)
- {
- break;
- }
-
- var message = reader.ReadMessage();
- if (isSent)
- {
- HandleToServer(ipSrc, message);
- }
- else
- {
- HandleToClient(ipSrc, message);
- }
-
- if (message.Position < message.Length)
- {
- Console.ForegroundColor = ConsoleColor.Red;
- Console.WriteLine("- Did not consume all bytes.");
- }
- }
- }
- }
-
- private static void HandleToClient(string source, MessageReader packet)
- {
- var tagName = TagMap.ContainsKey(packet.Tag) ? TagMap[packet.Tag] : "Unknown";
- Console.ForegroundColor = ConsoleColor.Cyan;
- Console.WriteLine($"{source,-15} Client received: {packet.Tag,-2} {tagName}");
-
- switch (packet.Tag)
- {
- case 14:
- case 13:
- packet.Position = packet.Length;
- break;
- case 0:
- Console.WriteLine("- GameCode " + packet.ReadInt32());
- break;
- case 5:
- case 6:
- Console.WriteLine(HexUtils.HexDump(packet.Buffer.Take(packet.Length).ToArray()));
- packet.Position = packet.Length;
- break;
- case 7:
- Console.WriteLine("- GameCode " + packet.ReadInt32());
- Console.WriteLine("- PlayerId " + packet.ReadInt32());
- Console.WriteLine("- Host " + packet.ReadInt32());
- var playerCount = packet.ReadPackedInt32();
- Console.WriteLine("- PlayerCount " + playerCount);
- for (var i = 0; i < playerCount; i++)
- {
- Console.WriteLine("- PlayerId " + packet.ReadPackedInt32());
- }
- break;
- case 10:
- Console.WriteLine("- GameCode " + packet.ReadInt32());
- Console.WriteLine("- Flag " + packet.ReadSByte());
- Console.WriteLine("- Value " + packet.ReadBoolean());
- break;
- }
- }
-
- private static void HandleToServer(string source, MessageReader packet)
- {
- var tagName = TagMap.ContainsKey(packet.Tag) ? TagMap[packet.Tag] : "Unknown";
- Console.ForegroundColor = ConsoleColor.White;
- Console.WriteLine($"{source,-15} Server received: {packet.Tag,-2} {tagName}");
-
- switch (packet.Tag)
- {
- case 0:
- Console.WriteLine("- GameInfo length " + packet.ReadBytesAndSize().Length);
- break;
- case 1:
- Console.WriteLine("- GameCode " + packet.ReadInt32());
- Console.WriteLine("- Unknown " + packet.ReadByte());
- break;
- case 5:
- case 6:
- Console.WriteLine("- GameCode " + packet.ReadInt32());
- Console.WriteLine(HexUtils.HexDump(packet.Buffer.Take(packet.Length).ToArray()));
- packet.Position = packet.Length;
- break;
- }
- }
- }
-}
+++ /dev/null
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30503.244
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AmongUs.Agent", "AmongUs.Agent\AmongUs.Agent.vcxproj", "{097BE795-88E9-4E7B-92F3-F6DDDBE3F012}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmongUs.Server", "AmongUs.Server\AmongUs.Server.csproj", "{1B0390AF-A4F3-4FE4-B093-708B0135C0B3}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmongUs.Client", "AmongUs.Client\AmongUs.Client.csproj", "{24F07173-D4A7-41C6-BA51-4516AB68343C}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmongUs.Shared", "AmongUs.Shared\AmongUs.Shared.csproj", "{1D109A96-FADF-41B9-A845-457A7A5C0C5A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "submodules", "submodules", "{DFA3CC6A-A27C-4713-876D-A55912C78C45}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel", "..\submodules\Hazel-Networking\Hazel\Hazel.csproj", "{02CFBD30-D77D-400F-94B2-700F60EFDD7F}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmongUs.Tests", "AmongUs.Tests\AmongUs.Tests.csproj", "{C1385C67-A7DD-46D2-80F6-FA428B85FB22}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{56DD9707-D811-4056-9E2C-8A9CC2479B07}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmongUs.Tools.Proxy", "AmongUs.Tools.Proxy\AmongUs.Tools.Proxy.csproj", "{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x86 = Debug|x86
- Release|Any CPU = Release|Any CPU
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|Any CPU.ActiveCfg = Debug|Win32
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|x86.ActiveCfg = Debug|Win32
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|x86.Build.0 = Debug|Win32
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|Any CPU.ActiveCfg = Release|Win32
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|x86.ActiveCfg = Release|Win32
- {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|x86.Build.0 = Release|Win32
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|x86.ActiveCfg = Debug|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|x86.Build.0 = Debug|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|Any CPU.Build.0 = Release|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|x86.ActiveCfg = Release|Any CPU
- {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|x86.Build.0 = Release|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|x86.Build.0 = Debug|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|Any CPU.Build.0 = Release|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|x86.ActiveCfg = Release|Any CPU
- {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|x86.Build.0 = Release|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|x86.Build.0 = Debug|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|Any CPU.Build.0 = Release|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|x86.ActiveCfg = Release|Any CPU
- {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|x86.Build.0 = Release|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|x86.Build.0 = Debug|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.Build.0 = Release|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|x86.ActiveCfg = Release|Any CPU
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|x86.Build.0 = Release|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|x86.Build.0 = Debug|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|x86.ActiveCfg = Release|Any CPU
- {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|x86.Build.0 = Release|Any CPU
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|x86.Build.0 = Debug|Any CPU
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {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
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {06245255-E585-483C-A4AD-71B7B568C6C8}
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {02CFBD30-D77D-400F-94B2-700F60EFDD7F} = {DFA3CC6A-A27C-4713-876D-A55912C78C45}
- {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D} = {56DD9707-D811-4056-9E2C-8A9CC2479B07}
- EndGlobalSection
-EndGlobal
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>16.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <ProjectGuid>{097be795-88e9-4e7b-92f3-f6dddbe3f012}</ProjectGuid>
+ <RootNamespace>AmongUsAgent</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v142</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(ProjectDir)build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)build\$(Platform)\$(Configuration)\Intermediate\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(ProjectDir)build\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>$(ProjectDir)build\$(Platform)\$(Configuration)\Intermediate\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="logger.cpp" />
+ <ClCompile Include="main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="logger.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="logger.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="logger.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+#include "logger.h"
+#include <cstdio>
+#include <Windows.h>
+
+HANDLE h_out = nullptr, h_old_out = nullptr;
+HANDLE h_err = nullptr, h_old_err = nullptr;
+HANDLE h_in = nullptr, h_old_in = nullptr;
+
+namespace Logger
+{
+ void Attach()
+ {
+ h_old_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ h_old_err = GetStdHandle(STD_ERROR_HANDLE);
+ h_old_in = GetStdHandle(STD_INPUT_HANDLE);
+
+ AllocConsole() && AttachConsole(GetCurrentProcessId());
+
+ h_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ h_err = GetStdHandle(STD_ERROR_HANDLE);
+ h_in = GetStdHandle(STD_INPUT_HANDLE);
+
+ SetConsoleMode(h_out,
+ ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
+
+ SetConsoleMode(h_in,
+ ENABLE_INSERT_MODE | ENABLE_EXTENDED_FLAGS |
+ ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE);
+ }
+
+ void Detach()
+ {
+ if (h_out && h_err && h_in) {
+ FreeConsole();
+
+ if (h_old_out)
+ {
+ SetStdHandle(STD_OUTPUT_HANDLE, h_old_out);
+ }
+
+ if (h_old_err)
+ {
+ SetStdHandle(STD_ERROR_HANDLE, h_old_err);
+ }
+
+ if (h_old_in)
+ {
+ SetStdHandle(STD_INPUT_HANDLE, h_old_in);
+ }
+ }
+ }
+
+ bool Print(const char* fmt, ...)
+ {
+ if (!h_out)
+ {
+ return false;
+ }
+
+ char buf[1024];
+ va_list va;
+
+ va_start(va, fmt);
+ _vsnprintf_s(buf, 1024, fmt, va);
+ va_end(va);
+
+ return !!WriteConsoleA(h_out, buf, static_cast<DWORD>(strlen(buf)), nullptr, nullptr);
+ }
+
+ char ReadKey()
+ {
+ if (!h_in)
+ {
+ return -1;
+ }
+
+ auto key = char{ 0 };
+ auto keysread = DWORD{ 0 };
+
+ ReadConsoleA(h_in, &key, 1, &keysread, nullptr);
+
+ return key;
+ }
+};
\ No newline at end of file
--- /dev/null
+#pragma once
+
+namespace Logger
+{
+ void Attach();
+ void Detach();
+ bool Print(const char* fmt, ...);
+ char ReadKey();
+};
\ No newline at end of file
--- /dev/null
+#include <Windows.h>
+#include "logger.h"
+
+DWORD WINAPI OnDllAttach(LPVOID base)
+{
+ Logger::Attach();
+ Logger::Print("[AmongUs.Agent] DLL Attached\n");
+
+ FreeLibraryAndExitThread(static_cast<HMODULE>(base), 1);
+}
+
+BOOL WINAPI OnDllDetach()
+{
+ Logger::Print("[AmongUs.Agent] DLL Detached\n");
+ Logger::Detach();
+
+ return true;
+}
+
+BOOL WINAPI DllMain(
+ const _In_ HINSTANCE hinstDll,
+ const _In_ DWORD fdwReason,
+ const _In_opt_ LPVOID lpvReserved
+)
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hinstDll);
+ CreateThread(nullptr, 0, OnDllAttach, hinstDll, 0, nullptr);
+ return true;
+
+ case DLL_PROCESS_DETACH:
+ if (lpvReserved == nullptr)
+ return OnDllDetach();
+ return true;
+ default:
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+using System;
+using System.IO;
+using Impostor.Shared.Innersloth;
+
+namespace Impostor.Client
+{
+ internal static class Program
+ {
+ private static void Main(string[] args)
+ {
+ var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..\\LocalLow");
+ var regionFile = Path.Combine(appData, "Innersloth", "Among Us", "regionInfo.dat");
+ var region = new RegionInfo("Private", "192.168.1.211", new []
+ {
+ new ServerInfo("Private-Master-1", "192.168.1.211", 22023)
+ });
+
+ using (var file = File.Open(regionFile, FileMode.Create, FileAccess.Write))
+ using (var writer = new BinaryWriter(file))
+ {
+ region.Serialize(writer);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Data
+{
+ public static class DisconnectMessages
+ {
+ 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.";
+
+ public const string Destroyed = "The game you tried to join is being destroyed. " +
+ "Please create a new game.";
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Runtime.Serialization;
+
+namespace Impostor.Server.Exceptions
+{
+ public class AmongUsException : Exception
+ {
+ public AmongUsException()
+ {
+ }
+
+ protected AmongUsException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ public AmongUsException(string? message) : base(message)
+ {
+ }
+
+ public AmongUsException(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Hazel;
+using Impostor.Server.Net.Response;
+
+namespace Impostor.Server.Extensions
+{
+ internal static class ConnectionExtensions
+ {
+ public static void Send(this Connection connection, MessageBase message)
+ {
+ using (message)
+ {
+ connection.Send(message.Write());
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\submodules\Hazel-Networking\Hazel\Hazel.csproj" />
+ <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+using System;
+using Hazel;
+using Impostor.Server.Data;
+using Impostor.Server.Extensions;
+using Impostor.Server.Net.Response;
+using Impostor.Shared.Innersloth;
+using Impostor.Shared.Innersloth.Data;
+using Serilog;
+using ILogger = Serilog.ILogger;
+
+namespace Impostor.Server.Net
+{
+ public class Client
+ {
+ private static readonly ILogger Logger = Log.ForContext<Client>();
+
+ private readonly ClientManager _clientManager;
+ private readonly GameManager _gameManager;
+
+ public Client(ClientManager clientManager, GameManager gameManager, int id, string name, Connection connection)
+ {
+ _clientManager = clientManager;
+ _gameManager = gameManager;
+ Id = id;
+ Name = name;
+ Connection = connection;
+ Connection.DataReceived += OnDataReceived;
+ Connection.Disconnected += OnDisconnected;
+ Player = new ClientPlayer(this);
+ }
+
+ public int Id { get; }
+ public string Name { get; }
+ public Connection Connection { get; }
+ public ClientPlayer Player { get; }
+
+ public void Send(MessageWriter writer)
+ {
+ Connection.Send(writer);
+ }
+
+ private bool IsPacketAllowed(MessageReader message, bool hostOnly)
+ {
+ var game = Player.Game;
+ if (game == null)
+ {
+ return false;
+ }
+
+ // GameCode must match code of the current game assigned to the player.
+ if (message.ReadInt32() != game.Code)
+ {
+ return false;
+ }
+
+ // Some packets should only be sent by the host of the game.
+ if (hostOnly)
+ {
+ if (game.HostId == Id)
+ {
+ return true;
+ }
+
+ Logger.Warning("[{0}] Client sent packet only allowed by the host ({1}).", Id, game.HostId);
+ return false;
+ }
+
+ return true;
+ }
+
+ private void OnDataReceived(DataReceivedEventArgs e)
+ {
+ try
+ {
+ while (true)
+ {
+ if (e.Message.Position >= e.Message.Length)
+ {
+ break;
+ }
+
+ OnMessageReceived(e.Message.ReadMessage(), e.SendOption);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "Exception caught in client data handler.");
+ Connection.Send(new Message1DisconnectReason(DisconnectReason.Custom, DisconnectMessages.Error));
+ }
+ }
+
+ private void OnMessageReceived(MessageReader message, SendOption sendOption)
+ {
+ var flag = (RequestFlag) message.Tag;
+
+ Logger.Verbose("[{0}] Server got {1}.", Id, flag);
+
+ switch (flag)
+ {
+ case RequestFlag.HostGame:
+ {
+ // Read game settings.
+ var gameInfoBytes = message.ReadBytesAndSize();
+ var gameInfo = GameOptionsData.Deserialize(gameInfoBytes);
+
+ // Create game.
+ var game = _gameManager.Create(this, gameInfo);
+ if (game == null)
+ {
+ Connection.Send(new Message1DisconnectReason(DisconnectReason.ServerFull));
+ return;
+ }
+
+ // Code in the packet below will be used in JoinGame.
+ using (var writer = MessageWriter.Get(SendOption.Reliable))
+ {
+ writer.StartMessage(0);
+ writer.Write(game.Code);
+ writer.EndMessage();
+
+ Connection.Send(writer);
+ }
+ break;
+ }
+
+ case RequestFlag.JoinGame:
+ {
+ var gameCode = message.ReadInt32();
+ var unknown = message.ReadByte();
+ var game = _gameManager.Find(gameCode);
+ if (game == null)
+ {
+ Connection.Send(new Message1DisconnectReason(DisconnectReason.GameMissing));
+ return;
+ }
+
+ game.HandleJoinGame(Player);
+ break;
+ }
+
+ case RequestFlag.StartGame:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ Player.Game.HandleStartGame(message);
+ break;
+ }
+
+ // No idea how this flag is triggered.
+ case RequestFlag.RemoveGame:
+ break;
+
+ case RequestFlag.RemovePlayer:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ var playerId = message.ReadPackedInt32();
+ var reason = message.ReadByte();
+
+ Player.Game.HandleRemovePlayer(playerId, (DisconnectReason) reason);
+ break;
+ }
+
+ case RequestFlag.GameData:
+ case RequestFlag.GameDataTo:
+ {
+ if (!IsPacketAllowed(message, false))
+ {
+ return;
+ }
+
+ // Broadcast packet to all other players.
+ using (var writer = MessageWriter.Get(sendOption))
+ {
+ if (flag == RequestFlag.GameDataTo)
+ {
+ var target = message.ReadPackedInt32();
+ writer.CopyFrom(message);
+ Player.Game.SendTo(writer, target);
+ }
+ else
+ {
+ writer.CopyFrom(message);
+ Player.Game.SendToAllExcept(writer, Player);
+ }
+ }
+ break;
+ }
+
+ case RequestFlag.EndGame:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ Player.Game.HandleEndGame(message);
+ break;
+ }
+
+ case RequestFlag.AlterGame:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ if (message.ReadByte() != (byte) AlterGameTags.ChangePrivacy)
+ {
+ return;
+ }
+
+ var isPublic = message.ReadByte() == 1;
+
+ Player.Game.HandleAlterGame(message, Player, isPublic);
+ break;
+ }
+
+ case RequestFlag.KickPlayer:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ var playerId = message.ReadPackedInt32();
+ var isBan = message.ReadBoolean();
+
+ Player.Game.HandleKickPlayer(playerId, isBan);
+ break;
+ }
+
+ default:
+ Logger.Warning("Server received unknown flag {0}.", flag);
+ break;
+ }
+
+ if (flag != RequestFlag.GameData &&
+ flag != RequestFlag.GameDataTo &&
+ flag != RequestFlag.EndGame &&
+ message.Position < message.Length)
+ {
+ Logger.Warning("Server did not consume all bytes from {0} ({1} < {2}).",
+ flag,
+ message.Position,
+ message.Length);
+ }
+ }
+
+ private void OnDisconnected(object sender, DisconnectedEventArgs e)
+ {
+ try
+ {
+ if (Player.Game != null)
+ {
+ Player.Game.HandleRemovePlayer(Id, DisconnectReason.ExitGame);
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "Exception caught in client disconnection.");
+ }
+
+ _clientManager.Remove(this);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Concurrent;
+using Impostor.Server.Exceptions;
+using Serilog;
+
+namespace Impostor.Server.Net
+{
+ public class ClientManager
+ {
+ private static readonly ILogger Logger = Log.ForContext<ClientManager>();
+
+ private readonly ConcurrentDictionary<int, Client> _clients;
+ private readonly object _idLock;
+ private int _idLast;
+
+ public ClientManager()
+ {
+ _clients = new ConcurrentDictionary<int, Client>();
+ _idLock = new object();
+ _idLast = 0;
+ }
+
+ // No idea what a good way for this is.
+ public int NextId()
+ {
+ lock (_idLock)
+ {
+ // 3 Attempts.
+ for (var i = 0; i < 3; i++)
+ {
+ // It is important that ids start from 1, a 0 id causes issues.
+ var result = ++_idLast;
+
+ if (_idLast == int.MaxValue)
+ {
+ _idLast = 0;
+ }
+
+ if (_clients.ContainsKey(_idLast))
+ {
+ continue;
+ }
+
+ return result;
+ }
+
+ throw new AmongUsException("Unable to generate a client id.");
+ }
+ }
+
+ public void Add(Client client)
+ {
+ Logger.Information("Client connected.");
+
+ _clients.TryAdd(client.Id, client);
+ }
+
+ public void Remove(Client client)
+ {
+ Logger.Information("Client disconnected.");
+
+ _clients.TryRemove(client.Id, out _);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Server.Net
+{
+ public class ClientPlayer
+ {
+ public ClientPlayer(Client client)
+ {
+ Client = client;
+ }
+
+ public Client Client { get; }
+ public Game Game { get; set; }
+ public LimboStates LimboState { get; set; }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using Hazel;
+using Impostor.Server.Data;
+using Impostor.Server.Exceptions;
+using Impostor.Server.Extensions;
+using Impostor.Server.Net.Response;
+using Impostor.Shared.Innersloth;
+using Impostor.Shared.Innersloth.Data;
+using Serilog;
+using ILogger = Serilog.ILogger;
+
+namespace Impostor.Server.Net
+{
+ public class Game
+ {
+ private static readonly ILogger Logger = Log.ForContext<Game>();
+
+ private readonly GameManager _gameManager;
+ private readonly ConcurrentDictionary<int, ClientPlayer> _players;
+ private readonly HashSet<IPAddress> _bannedIps;
+
+ public Game(GameManager gameManager, int code, GameOptionsData options)
+ {
+ _gameManager = gameManager;
+ _players = new ConcurrentDictionary<int, ClientPlayer>();
+ _bannedIps = new HashSet<IPAddress>();
+
+ Code = code;
+ CodeStr = GameCode.IntToGameName(code);
+ HostId = -1;
+ GameState = GameStates.NotStarted;
+ Options = options;
+ }
+
+ public int Code { get; }
+ public string CodeStr { get; }
+ public bool IsPublic { get; private set; }
+ public int HostId { get; private set; }
+ public GameStates GameState { get; private set; }
+ public GameOptionsData Options { get; }
+
+ public void SendToAllExcept(MessageWriter message, ClientPlayer sender)
+ {
+ foreach (var (_, player) in _players.Where(x => x.Value != sender))
+ {
+ if (player.Client.Connection.State != ConnectionState.Connected)
+ {
+ Logger.Warning("[{0}] Tried to sent data to a disconnected player ({1}).", sender?.Client.Id, player.Client.Id);
+ continue;
+ }
+
+ player.Client.Send(message);
+ }
+ }
+
+ public void SendTo(MessageWriter message, int playerId)
+ {
+ if (_players.TryGetValue(playerId, out var player))
+ {
+ if (player.Client.Connection.State != ConnectionState.Connected)
+ {
+ Logger.Warning("[{0}] Sending data to {1} failed, player is not connected.", CodeStr, player.Client.Id);
+ return;
+ }
+
+ player.Client.Send(message);
+ }
+ else
+ {
+ Logger.Warning("[{0}] Sending data to {1} failed, player does not exist.", CodeStr, playerId);
+ }
+ }
+
+ public void HandleStartGame(MessageReader message)
+ {
+ GameState = GameStates.Started;
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.CopyFrom(message);
+ SendToAllExcept(packet, null);
+ }
+ }
+
+ public void HandleJoinGame(ClientPlayer sender)
+ {
+ if (_bannedIps.Contains(sender.Client.Connection.EndPoint.Address))
+ {
+ sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.Banned));
+ return;
+ }
+
+ switch (GameState)
+ {
+ case GameStates.NotStarted:
+ HandleJoinGameNew(sender);
+ break;
+ case GameStates.Ended:
+ HandleJoinGameNext(sender);
+ break;
+ case GameStates.Started:
+ sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameStarted));
+ return;
+ case GameStates.Destroyed:
+ sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.Custom, DisconnectMessages.Destroyed));
+ return;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ public void HandleEndGame(MessageReader message)
+ {
+ GameState = GameStates.Ended;
+
+ // Broadcast end of the game.
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.CopyFrom(message);
+ SendToAllExcept(packet, null);
+ }
+
+ // Remove all players from this game.
+ foreach (var player in _players)
+ {
+ player.Value.Game = null;
+ }
+
+ _players.Clear();
+ }
+
+ public void HandleAlterGame(MessageReader message, ClientPlayer sender, bool isPublic)
+ {
+ IsPublic = isPublic;
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.CopyFrom(message);
+ SendToAllExcept(packet, sender);
+ }
+ }
+
+ public void HandleRemovePlayer(int playerId, DisconnectReason reason)
+ {
+ if (_players.TryRemove(playerId, out var player))
+ {
+ player.Game = null;
+ }
+
+ Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, player?.Client.Name, playerId);
+
+ // Game is empty, remove it.
+ if (_players.Count == 0)
+ {
+ GameState = GameStates.Destroyed;
+
+ // Remove instance reference.
+ _gameManager.Remove(Code);
+ return;
+ }
+
+ // Host migration.
+ if (HostId == playerId)
+ {
+ var newHost = _players.First().Value;
+ HostId = newHost.Client.Id;
+ Logger.Information("{0} - Assigned {1} ({2}) as new host.", CodeStr, newHost.Client.Name, newHost.Client.Id);
+ }
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ WriteRemovePlayerMessage(packet, false, playerId, reason);
+ SendToAllExcept(packet, player);
+ }
+ }
+
+ public void HandleKickPlayer(int playerId, bool isBan)
+ {
+ _players.TryGetValue(playerId, out var p);
+ Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, p?.Client.Name, playerId);
+
+ using (var message = MessageWriter.Get(SendOption.Reliable))
+ {
+ WriteKickPlayerMessage(message, false, playerId, isBan);
+ SendToAllExcept(message, null);
+
+ if (_players.TryRemove(playerId, out var player))
+ {
+ player.Game = null;
+
+ if (isBan)
+ {
+ _bannedIps.Add(player.Client.Connection.EndPoint.Address);
+ }
+ }
+
+ WriteRemovePlayerMessage(message, true, playerId, isBan
+ ? DisconnectReason.Banned
+ : DisconnectReason.Kicked);
+ SendToAllExcept(message, player);
+ }
+ }
+
+ private void HandleJoinGameNew(ClientPlayer sender)
+ {
+ Logger.Information("{0} - Player {1} ({2}) is joining.", CodeStr, sender.Client.Name, sender.Client.Id);
+
+ // Store player.
+ if (!_players.TryAdd(sender.Client.Id, sender))
+ {
+ throw new AmongUsException("Failed to add player to game.");
+ }
+
+ // Assign player to this game for future packets.
+ sender.Game = this;
+
+ // Assign hostId if none is set.
+ if (HostId == -1)
+ {
+ HostId = sender.Client.Id;
+ }
+
+ if (HostId == sender.Client.Id)
+ {
+ sender.LimboState = LimboStates.NotLimbo;
+ }
+
+ using (var message = MessageWriter.Get(SendOption.Reliable))
+ {
+ WriteJoinedGameMessage(message, false, sender);
+ WriteAlterGameMessage(message, false);
+
+ sender.Client.Send(message);
+
+ BroadcastJoinMessage(message, true, sender);
+ }
+ }
+
+ private void HandleJoinGameNext(ClientPlayer sender)
+ {
+ Logger.Information("{0} - Player {1} ({2}) is rejoining.", CodeStr, sender.Client.Name, sender.Client.Id);
+
+ if (sender.Client.Id == HostId)
+ {
+ GameState = GameStates.NotStarted;
+ HandleJoinGameNew(sender);
+
+ using (var message = MessageWriter.Get(SendOption.Reliable))
+ {
+ foreach (var (_, player) in _players.Where(x => x.Value != sender))
+ {
+ WriteJoinedGameMessage(message, true, player);
+ WriteAlterGameMessage(message, false);
+ player.Client.Send(message);
+ }
+ }
+
+ return;
+ }
+
+ if (_players.Count >= 9)
+ {
+ sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameFull));
+ return;
+ }
+
+ // Store player.
+ if (!_players.TryAdd(sender.Client.Id, sender))
+ {
+ throw new AmongUsException("Failed to add player to game.");
+ }
+
+ // Assign player to this game for future packets.
+ sender.Game = this;
+
+ // Limbo, yes.
+ sender.LimboState = LimboStates.WaitingForHost;
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ WriteWaitForHostMessage(packet, false, sender);
+ sender.Client.Send(packet);
+
+ BroadcastJoinMessage(packet, true, sender);
+ }
+ }
+
+ private void WriteRemovePlayerMessage(MessageWriter message, bool clear, int playerId, DisconnectReason reason)
+ {
+ // Only a subset of DisconnectReason shows an unique message.
+ // ExitGame, Banned and Kicked.
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.RemovePlayer);
+ message.Write(Code);
+ message.Write(playerId);
+ message.Write(HostId);
+ message.Write((byte) reason);
+ message.EndMessage();
+ }
+
+ private void WriteJoinedGameMessage(MessageWriter message, bool clear, ClientPlayer player)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.JoinedGame);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.Write(HostId);
+ message.WritePacked(_players.Count - 1);
+
+ foreach (var (_, p) in _players.Where(x => x.Value != player))
+ {
+ message.WritePacked(p.Client.Id);
+ }
+
+ message.EndMessage();
+ }
+
+ private void WriteAlterGameMessage(MessageWriter message, bool clear)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.AlterGame);
+ message.Write(Code);
+ message.Write((byte) AlterGameTags.ChangePrivacy);
+ message.Write(IsPublic);
+ message.EndMessage();
+ }
+
+ private void WriteKickPlayerMessage(MessageWriter message, bool clear, int playerId, bool isBan)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.KickPlayer);
+ message.Write(Code);
+ message.WritePacked(playerId);
+ message.Write(isBan);
+ message.EndMessage();
+ }
+
+ private void WriteWaitForHostMessage(MessageWriter message, bool clear, ClientPlayer player)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.WaitForHost);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.EndMessage();
+ }
+
+ private void BroadcastJoinMessage(MessageWriter message, bool clear, ClientPlayer player)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.JoinGame);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.Write(HostId);
+ message.EndMessage();
+
+ SendToAllExcept(message, player);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Concurrent;
+using Impostor.Shared.Innersloth;
+using Serilog;
+
+namespace Impostor.Server.Net
+{
+ public class GameManager
+ {
+ private static readonly ILogger Logger = Log.ForContext<GameManager>();
+
+ private readonly ConcurrentDictionary<int, Game> _games;
+
+ public GameManager()
+ {
+ _games = new ConcurrentDictionary<int, Game>();
+ }
+
+ public Game Create(Client owner, GameOptionsData options)
+ {
+ var gameCode = GameCode.GenerateCode(6);
+ var game = new Game(this, gameCode, options);
+
+ if (_games.TryAdd(gameCode, game))
+ {
+ Logger.Debug("Created game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
+ return game;
+ }
+
+ Logger.Warning("Failed to create game.");
+ return null;
+ }
+
+ public Game Find(int gameCode)
+ {
+ _games.TryGetValue(gameCode, out var game);
+ return game;
+ }
+
+ public void Remove(int gameCode)
+ {
+ Logger.Debug("Remove game with code {0} ({1}).", GameCode.IntToGameName(gameCode), gameCode);
+ _games.TryRemove(gameCode, out _);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Net
+{
+ public enum GameStates : byte
+ {
+ NotStarted = 0,
+ Started = 1,
+ Ended = 2,
+ Destroyed = 3
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Net;
+using Hazel;
+using Hazel.Udp;
+using Impostor.Server.Extensions;
+using Impostor.Server.Net.Response;
+using Impostor.Shared.Innersloth.Data;
+using Serilog;
+using ILogger = Serilog.ILogger;
+
+namespace Impostor.Server.Net
+{
+ public class Matchmaker
+ {
+ private static readonly ILogger Logger = Log.ForContext<Matchmaker>();
+
+ private readonly GameManager _gameManager;
+ private readonly ClientManager _clientManager;
+ private readonly UdpConnectionListener _connection;
+
+ public Matchmaker(IPAddress ip, int port)
+ {
+ _gameManager = new GameManager();
+ _clientManager = new ClientManager();
+ _connection = new UdpConnectionListener(new IPEndPoint(ip, port), IPMode.IPv4, s =>
+ {
+ Logger.Warning("Log from Hazel: {0}", s);
+ });
+
+ _connection.NewConnection += OnNewConnection;
+ }
+
+ private void OnNewConnection(NewConnectionEventArgs e)
+ {
+ // Handshake.
+ var clientVersion = e.HandshakeData.ReadInt32();
+ var clientName = e.HandshakeData.ReadString();
+
+ e.HandshakeData.Recycle();
+
+ if (clientVersion != 50516550)
+ {
+ e.Connection.Send(new Message1DisconnectReason(DisconnectReason.IncorrectVersion));
+ return;
+ }
+
+ // Register client.
+ _clientManager.Add(new Client(_clientManager, _gameManager, _clientManager.NextId(), clientName, e.Connection));
+ }
+
+ public void Start()
+ {
+ _connection.Start();
+ }
+
+ public void Stop()
+ {
+ _connection.Dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Hazel;
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Server.Net.Response
+{
+ public class Message1DisconnectReason : MessageBase
+ {
+ private readonly DisconnectReason _reason;
+ private readonly string _message;
+
+ // Notes:
+ // - Specifying no reason does something with ban minutes left.
+ // - (?) You were disconnected because Among Us was suspended by another app.
+ public Message1DisconnectReason(DisconnectReason reason, string message = null) : base(SendOption.Reliable, MessageFlag.DisconnectReason)
+ {
+ _reason = reason;
+ _message = message;
+ }
+
+ protected override void WriteMessage(MessageWriter writer)
+ {
+ writer.Write((int) _reason);
+
+ if (_reason == DisconnectReason.Custom)
+ {
+ writer.Write(_message);
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using Hazel;
+
+namespace Impostor.Server.Net.Response
+{
+ public abstract class MessageBase : IDisposable
+ {
+ private readonly MessageWriter _writer;
+ private readonly MessageFlag _flag;
+
+ protected MessageBase(SendOption option, MessageFlag flag)
+ {
+ _writer = MessageWriter.Get(option);
+ _flag = flag;
+ }
+
+ public MessageWriter Write()
+ {
+ _writer.StartMessage((byte) _flag);
+ WriteMessage(_writer);
+ _writer.EndMessage();
+ return _writer;
+ }
+
+ protected abstract void WriteMessage(MessageWriter writer);
+
+ public void Dispose()
+ {
+ _writer.Dispose();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Server.Net.Response
+{
+ public enum MessageFlag : byte
+ {
+ DisconnectReason = 1
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Net;
+using System.Threading;
+using Impostor.Server.Net;
+using Serilog;
+
+namespace Impostor.Server
+{
+ internal static class Program
+ {
+ private static readonly ManualResetEvent QuitEvent = new ManualResetEvent(false);
+
+ private static void Main(string[] args)
+ {
+ // Listen for CTRL+C.
+ Console.CancelKeyPress += (sender, e) =>
+ {
+ e.Cancel = true;
+ QuitEvent.Set();
+ };
+
+ // Configure logger.
+ Log.Logger = new LoggerConfiguration()
+#if DEBUG
+ .MinimumLevel.Verbose()
+#else
+ .MinimumLevel.Information()
+#endif
+ .WriteTo.Console()
+ .CreateLogger();
+
+ // Initialize matchmaker.
+ var matchMaker = new Matchmaker(IPAddress.Any, 22023);
+ matchMaker.Start();
+ Log.Logger.Information("Matchmaker is running on *:22023.");
+ QuitEvent.WaitOne();
+ Log.Logger.Warning("Matchmaker is shutting down!");
+ matchMaker.Stop();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+ </PropertyGroup>
+
+</Project>
--- /dev/null
+namespace Impostor.Shared.Innersloth.Data
+{
+ public enum AlterGameTags : byte
+ {
+ ChangePrivacy = 1
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Shared.Innersloth.Data
+{
+ public enum DisconnectReason
+ {
+ ExitGame = 0,
+ Destroy = 16,
+ // The game you tried to join is full.
+ // Check with the host to see if you can join next round.
+ GameFull = 1,
+ // The game you tried to join already started.
+ // Check with the host to see if you can join next round.
+ GameStarted = 2,
+ // Could not find the game you're looking for.
+ GameMissing = 3,
+ IncorrectGame = 18,
+ // For these a message can be given, specifying an empty message shows
+ // "An unknown error disconnected you from the server."
+ CustomMessage1 = 4,
+ Custom = 8,
+ // CustomMessage3 = 11,
+ // CustomMessage4 = 12,
+ // CustomMessage5 = 13,
+ // CustomMessage6 = 14,
+ // CustomMessage7 = 15,
+ // You are running an older version of the game.
+ // Please update to play with others.
+ IncorrectVersion = 5,
+ // You cannot rejoin that room.
+ // You were banned
+ Banned = 6,
+ // You can rejoin if the room hasn't started
+ // You were kicked
+ Kicked = 7,
+ // Server refused username: %USERNAME%
+ InvalidName = 9,
+ // You were banned for hacking.
+ // Please stop.
+ Hacking = 10,
+ // You disconnected from the host.
+ // If this happens often, check your WiFi strength.
+ //
+ // You disconnected from the server.
+ // If this happens often, check your network strength.
+ // This may also be a server issue.
+ Error = 17,
+ // The server stopped this game. Possibly due to inactivity.
+ ServerRequest = 19,
+ // The Among Us servers are overloaded.
+ // Sorry! Please try again later!
+ ServerFull = 20,
+ FocusLostBackground = 207,
+ IntentionalLeaving = 208,
+ FocusLost = 209,
+ NewConnection = 210
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Impostor.Shared.Innersloth.Data
+{
+ [Flags]
+ public enum GameKeywords : uint
+ {
+ All = 0,
+ Other = 1,
+ Spanish = 2,
+ Korean = 4,
+ Russian = 8,
+ Portuguese = 16,
+ Arabic = 32,
+ Filipone = 64,
+ Polish = 128,
+ English = 256
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Shared.Innersloth.Data
+{
+ public enum GameStates : byte
+ {
+ NotStarted,
+ Started,
+ Ended,
+ Destroyed
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Shared.Innersloth.Data
+{
+ public enum LimboStates
+ {
+ PreSpawn,
+ NotLimbo,
+ WaitingForHost,
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Impostor.Shared.Innersloth.Data
+{
+ public enum RequestFlag : byte
+ {
+ HostGame = 0,
+ JoinGame = 1,
+ StartGame = 2,
+ RemoveGame = 3,
+ RemovePlayer = 4,
+ GameData = 5,
+ GameDataTo = 6,
+ JoinedGame = 7,
+ EndGame = 8,
+ GetGameList = 9,
+ AlterGame = 10,
+ KickPlayer = 11,
+ WaitForHost = 12,
+ Redirect = 13,
+ ReselectServer = 14,
+ GetGameListV2 = 16,
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Buffers.Binary;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Impostor.Shared.Innersloth
+{
+ public static class GameCode
+ {
+ private const string V2 = "QWXRTYLPESDFGHUJKZOCVBINMA";
+ private static readonly int[] V2Map = {
+ 25,
+ 21,
+ 19,
+ 10,
+ 8,
+ 11,
+ 12,
+ 13,
+ 22,
+ 15,
+ 16,
+ 6,
+ 24,
+ 23,
+ 18,
+ 7,
+ 0,
+ 3,
+ 9,
+ 4,
+ 14,
+ 20,
+ 1,
+ 2,
+ 5,
+ 17
+ };
+ private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider();
+
+ public static string IntToGameName(int input)
+ {
+ // V2.
+ if (input < -1)
+ {
+ return IntToGameNameV2(input);
+ }
+
+ // V1.
+ Span<byte> code = stackalloc byte[4];
+ BinaryPrimitives.WriteInt32LittleEndian(code, input);
+ return Encoding.UTF8.GetString(code.Slice(0, 4));
+ }
+
+ private static string IntToGameNameV2(int input)
+ {
+ var a = input & 0x3FF;
+ var b = (input >> 10) & 0xFFFFF;
+
+ return new string(new []
+ {
+ V2[a % 26],
+ V2[a / 26],
+ V2[b % 26],
+ V2[b / 26 % 26],
+ V2[b / (26 * 26) % 26],
+ V2[b / (26 * 26 * 26) % 26]
+ });
+ }
+
+ public static int GameNameToInt(string code)
+ {
+ var upper = code.ToUpperInvariant();
+ if (upper.Any(x => !char.IsLetter(x)))
+ {
+ return -1;
+ }
+
+ var len = code.Length;
+ if (len == 6)
+ {
+ return GameNameToIntV2(upper);
+ }
+
+ if (len == 4)
+ {
+ return code[0] | ((code[1] | ((code[2] | (code[3] << 8)) << 8)) << 8);
+ }
+
+ return -1;
+ }
+
+ private static int GameNameToIntV2(string code)
+ {
+ var a = V2Map[code[0] - 65];
+ var b = V2Map[code[1] - 65];
+ var c = V2Map[code[2] - 65];
+ var d = V2Map[code[3] - 65];
+ var e = V2Map[code[4] - 65];
+ var f = V2Map[code[5] - 65];
+
+ var one = (a + 26 * b) & 0x3FF;
+ var two = (c + 26 * (d + 26 * (e + 26 * f)));
+
+ return (int) (one | ((two << 10) & 0x3FFFFC00) | 0x80000000);
+ }
+
+ public static int GenerateCode(int len)
+ {
+ if (len != 4 && len != 6)
+ {
+ throw new ArgumentException("should be 4 or 6", nameof(len));
+ }
+
+ // Generate random bytes.
+ Span<byte> data = stackalloc byte[len];
+ Random.GetBytes(data);
+
+ // Convert to their char representation.
+ Span<char> dataChar = stackalloc char[len];
+ for (var i = 0; i < len; i++)
+ {
+ dataChar[i] = V2[V2Map[data[i] % 26]];
+ }
+
+ return GameNameToInt(new string(dataChar));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Shared.Innersloth
+{
+ public class GameOptionsData
+ {
+ public byte Version { get; set; }
+ public byte MaxPlayers { get; set; }
+ public GameKeywords Keywords { get; set; }
+ public byte MapId { get; set; }
+ public float PlayerSpeedMod { get; set; }
+ public float CrewLightMod { get; set; }
+ public float ImpostorLightMod { get; set; }
+ public float KillCooldown { get; set; }
+ public int NumCommonTasks { get; set; }
+ public int NumLongTasks { get; set; }
+ public int NumShortTasks { get; set; }
+ public int NumEmergencyMeetings { get; set; }
+ public int EmergencyCooldown { get; set; }
+ public int NumImpostors { get; set; }
+ public bool GhostsDoTasks { get; set; }
+ public int KillDistance { get; set; }
+ public int DiscussionTime { get; set; }
+ public int VotingTime { get; set; }
+ public bool ConfirmImpostor { get; set; }
+ public bool VisualTasks { get; set; }
+ public bool IsDefaults { get; set; }
+
+ public void Serialize(BinaryWriter writer, byte version)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static GameOptionsData Deserialize(byte[] bytes)
+ {
+ using (var stream = new MemoryStream(bytes))
+ using (var reader = new BinaryReader(stream))
+ {
+ var result = new GameOptionsData();
+
+ result.Version = reader.ReadByte();
+ result.MaxPlayers = reader.ReadByte();
+ result.Keywords = (GameKeywords) reader.ReadUInt32();
+ result.MapId = reader.ReadByte();
+ result.PlayerSpeedMod = reader.ReadSingle();
+ result.CrewLightMod = reader.ReadSingle();
+ result.ImpostorLightMod = reader.ReadSingle();
+ result.KillCooldown = reader.ReadSingle();
+ result.NumCommonTasks = reader.ReadByte();
+ result.NumLongTasks = reader.ReadByte();
+ result.NumShortTasks = reader.ReadByte();
+ result.NumEmergencyMeetings = reader.ReadInt32();
+ result.NumImpostors = reader.ReadByte();
+ result.KillDistance = reader.ReadByte();
+ result.DiscussionTime = reader.ReadInt32();
+ result.VotingTime = reader.ReadInt32();
+ result.IsDefaults = reader.ReadBoolean();
+
+ if (result.Version > 1)
+ {
+ result.EmergencyCooldown = reader.ReadByte();
+ }
+
+ if (result.Version > 2)
+ {
+ result.ConfirmImpostor = reader.ReadBoolean();
+ result.VisualTasks = reader.ReadBoolean();
+ }
+
+ return result;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.Collections.Generic;
+using System.IO;
+
+namespace Impostor.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);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System.IO;
+using System.Net;
+
+namespace Impostor.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);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Text;
+
+namespace Impostor.Shared.Utils
+{
+ public static class HexUtils
+ {
+ public static string HexDump(byte[] bytes, int bytesPerLine = 16)
+ {
+ if (bytes == null) return "<null>";
+ int bytesLength = bytes.Length;
+
+ char[] HexChars = "0123456789ABCDEF".ToCharArray();
+
+ int firstHexColumn =
+ 8 // 8 characters for the address
+ + 3; // 3 spaces
+
+ int firstCharColumn = firstHexColumn
+ + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
+ + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
+ + 2; // 2 spaces
+
+ int lineLength = firstCharColumn
+ + bytesPerLine // - characters to show the ascii value
+ + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2)
+
+ char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
+ int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine;
+ StringBuilder result = new StringBuilder(expectedLines * lineLength);
+
+ for (int i = 0; i < bytesLength; i += bytesPerLine)
+ {
+ line[0] = HexChars[(i >> 28) & 0xF];
+ line[1] = HexChars[(i >> 24) & 0xF];
+ line[2] = HexChars[(i >> 20) & 0xF];
+ line[3] = HexChars[(i >> 16) & 0xF];
+ line[4] = HexChars[(i >> 12) & 0xF];
+ line[5] = HexChars[(i >> 8) & 0xF];
+ line[6] = HexChars[(i >> 4) & 0xF];
+ line[7] = HexChars[(i >> 0) & 0xF];
+
+ int hexColumn = firstHexColumn;
+ int charColumn = firstCharColumn;
+
+ for (int j = 0; j < bytesPerLine; j++)
+ {
+ if (j > 0 && (j & 7) == 0) hexColumn++;
+ if (i + j >= bytesLength)
+ {
+ line[hexColumn] = ' ';
+ line[hexColumn + 1] = ' ';
+ line[charColumn] = ' ';
+ }
+ else
+ {
+ byte b = bytes[i + j];
+ line[hexColumn] = HexChars[(b >> 4) & 0xF];
+ line[hexColumn + 1] = HexChars[b & 0xF];
+ line[charColumn] = (b < 32 ? '·' : (char)b);
+ }
+ hexColumn += 3;
+ charColumn++;
+ }
+ result.Append(line);
+ }
+ return result.ToString();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using Impostor.Shared.Innersloth;
+using Xunit;
+
+namespace Impostor.Tests
+{
+ public class GameCodeTests
+ {
+ [Fact]
+ public void CodeV1()
+ {
+ const string code = "ABCD";
+ const int codeInt = 0x44434241;
+
+ Assert.Equal(code, GameCode.IntToGameName(codeInt));
+ Assert.Equal(codeInt, GameCode.GameNameToInt(code));
+ }
+
+ [Fact]
+ public void CodeV2()
+ {
+ const string code = "ABCDEF";
+ const int codeInt = -1943683525;
+
+ Assert.Equal(code, GameCode.IntToGameName(codeInt));
+ Assert.Equal(codeInt, GameCode.GameNameToInt(code));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netcoreapp3.1</TargetFramework>
+
+ <IsPackable>false</IsPackable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+ <PackageReference Include="xunit" Version="2.4.0" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
+ <PackageReference Include="coverlet.collector" Version="1.2.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Impostor.Shared\Impostor.Shared.csproj" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+using System;
+using System.Text;
+
+namespace Impostor.Tools.Proxy
+{
+ public static class HexUtils
+ {
+ public static string HexDump(byte[] bytes, int bytesPerLine = 16)
+ {
+ if (bytes == null) return "<null>";
+ int bytesLength = bytes.Length;
+
+ char[] HexChars = "0123456789ABCDEF".ToCharArray();
+
+ int firstHexColumn =
+ 8 // 8 characters for the address
+ + 3; // 3 spaces
+
+ int firstCharColumn = firstHexColumn
+ + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
+ + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
+ + 2; // 2 spaces
+
+ int lineLength = firstCharColumn
+ + bytesPerLine // - characters to show the ascii value
+ + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2)
+
+ char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray();
+ int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine;
+ StringBuilder result = new StringBuilder(expectedLines * lineLength);
+
+ for (int i = 0; i < bytesLength; i += bytesPerLine)
+ {
+ line[0] = HexChars[(i >> 28) & 0xF];
+ line[1] = HexChars[(i >> 24) & 0xF];
+ line[2] = HexChars[(i >> 20) & 0xF];
+ line[3] = HexChars[(i >> 16) & 0xF];
+ line[4] = HexChars[(i >> 12) & 0xF];
+ line[5] = HexChars[(i >> 8) & 0xF];
+ line[6] = HexChars[(i >> 4) & 0xF];
+ line[7] = HexChars[(i >> 0) & 0xF];
+
+ int hexColumn = firstHexColumn;
+ int charColumn = firstCharColumn;
+
+ for (int j = 0; j < bytesPerLine; j++)
+ {
+ if (j > 0 && (j & 7) == 0) hexColumn++;
+ if (i + j >= bytesLength)
+ {
+ line[hexColumn] = ' ';
+ line[hexColumn + 1] = ' ';
+ line[charColumn] = ' ';
+ }
+ else
+ {
+ byte b = bytes[i + j];
+ line[hexColumn] = HexChars[(b >> 4) & 0xF];
+ line[hexColumn + 1] = HexChars[b & 0xF];
+ line[charColumn] = (b < 32 ? '·' : (char)b);
+ }
+ hexColumn += 3;
+ charColumn++;
+ }
+ result.Append(line);
+ }
+ return result.ToString();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net452</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Pcap.Net.x64" Version="1.0.4.1" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\submodules\Hazel-Networking\Hazel\Hazel.csproj" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Hazel;
+using Hazel.Udp;
+using PcapDotNet.Core;
+using PcapDotNet.Packets;
+
+namespace Impostor.Tools.Proxy
+{
+ internal static class Program
+ {
+ private const string DeviceName = "Intel(R) I211 Gigabit Network Connection";
+
+ private static readonly Dictionary<byte, string> TagMap = new Dictionary<byte, string>
+ {
+ {0, "HostGame"},
+ {1, "JoinGame"},
+ {2, "StartGame"},
+ {3, "RemoveGame"},
+ {4, "RemovePlayer"},
+ {5, "GameData"},
+ {6, "GameDataTo"},
+ {7, "JoinedGame"},
+ {8, "EndGame"},
+ {9, "GetGameList"},
+ {10, "AlterGame"},
+ {11, "KickPlayer"},
+ {12, "WaitForHost"},
+ {13, "Redirect"},
+ {14, "ReselectServer"},
+ {16, "GetGameListV2"}
+ };
+
+ private static void Main(string[] args)
+ {
+ var devices = LivePacketDevice.AllLocalMachine;
+ if (devices.Count == 0)
+ {
+ Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
+ return;
+ }
+
+ var device = devices.FirstOrDefault(x => x.Description.Contains(DeviceName));
+ if (device == null)
+ {
+ Console.WriteLine("Unable to find configured device.");
+ return;
+ }
+
+ using (var communicator = device.Open(65536, PacketDeviceOpenAttributes.Promiscuous, 1000))
+ {
+ using (var filter = communicator.CreateFilter("udp and port 22023"))
+ {
+ communicator.SetFilter(filter);
+ }
+
+ communicator.ReceivePackets(0, PacketHandler);
+ }
+ }
+
+ private static void PacketHandler(Packet packet)
+ {
+ var ip = packet.Ethernet.IpV4;
+ var ipSrc = ip.Source.ToString();
+ var udp = ip.Udp;
+
+ // True if this is our own packet.
+ using (var stream = udp.Payload.ToMemoryStream())
+ {
+ var reader = MessageReader.Get(stream.ToArray());
+ if (reader.Buffer[0] == (byte) SendOption.Reliable)
+ {
+ reader.Offset = 3;
+ reader.Length = udp.Payload.Length - 3;
+ reader.Position = 0;
+ }
+ else if (reader.Buffer[0] == (byte) UdpSendOption.Acknowledgement ||
+ reader.Buffer[0] == (byte) UdpSendOption.Ping ||
+ reader.Buffer[0] == (byte) UdpSendOption.Hello ||
+ reader.Buffer[0] == (byte) UdpSendOption.Disconnect)
+ {
+ return;
+ }
+ else
+ {
+ reader.Offset = 1;
+ reader.Length = udp.Payload.Length - 1;
+ reader.Position = 0;
+ }
+
+ var isSent = ipSrc.StartsWith("192.");
+
+ while (true)
+ {
+ if (reader.Position >= reader.Length)
+ {
+ break;
+ }
+
+ var message = reader.ReadMessage();
+ if (isSent)
+ {
+ HandleToServer(ipSrc, message);
+ }
+ else
+ {
+ HandleToClient(ipSrc, message);
+ }
+
+ if (message.Position < message.Length)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("- Did not consume all bytes.");
+ }
+ }
+ }
+ }
+
+ private static void HandleToClient(string source, MessageReader packet)
+ {
+ var tagName = TagMap.ContainsKey(packet.Tag) ? TagMap[packet.Tag] : "Unknown";
+ Console.ForegroundColor = ConsoleColor.Cyan;
+ Console.WriteLine($"{source,-15} Client received: {packet.Tag,-2} {tagName}");
+
+ switch (packet.Tag)
+ {
+ case 14:
+ case 13:
+ packet.Position = packet.Length;
+ break;
+ case 0:
+ Console.WriteLine("- GameCode " + packet.ReadInt32());
+ break;
+ case 5:
+ case 6:
+ Console.WriteLine(HexUtils.HexDump(packet.Buffer.Take(packet.Length).ToArray()));
+ packet.Position = packet.Length;
+ break;
+ case 7:
+ Console.WriteLine("- GameCode " + packet.ReadInt32());
+ Console.WriteLine("- PlayerId " + packet.ReadInt32());
+ Console.WriteLine("- Host " + packet.ReadInt32());
+ var playerCount = packet.ReadPackedInt32();
+ Console.WriteLine("- PlayerCount " + playerCount);
+ for (var i = 0; i < playerCount; i++)
+ {
+ Console.WriteLine("- PlayerId " + packet.ReadPackedInt32());
+ }
+ break;
+ case 10:
+ Console.WriteLine("- GameCode " + packet.ReadInt32());
+ Console.WriteLine("- Flag " + packet.ReadSByte());
+ Console.WriteLine("- Value " + packet.ReadBoolean());
+ break;
+ }
+ }
+
+ private static void HandleToServer(string source, MessageReader packet)
+ {
+ var tagName = TagMap.ContainsKey(packet.Tag) ? TagMap[packet.Tag] : "Unknown";
+ Console.ForegroundColor = ConsoleColor.White;
+ Console.WriteLine($"{source,-15} Server received: {packet.Tag,-2} {tagName}");
+
+ switch (packet.Tag)
+ {
+ case 0:
+ Console.WriteLine("- GameInfo length " + packet.ReadBytesAndSize().Length);
+ break;
+ case 1:
+ Console.WriteLine("- GameCode " + packet.ReadInt32());
+ Console.WriteLine("- Unknown " + packet.ReadByte());
+ break;
+ case 5:
+ case 6:
+ Console.WriteLine("- GameCode " + packet.ReadInt32());
+ Console.WriteLine(HexUtils.HexDump(packet.Buffer.Take(packet.Length).ToArray()));
+ packet.Position = packet.Length;
+ break;
+ }
+ }
+ }
+}
--- /dev/null
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30503.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Imposter.Agent", "Imposter.Agent\Imposter.Agent.vcxproj", "{097BE795-88E9-4E7B-92F3-F6DDDBE3F012}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imposter.Server", "Imposter.Server\Imposter.Server.csproj", "{1B0390AF-A4F3-4FE4-B093-708B0135C0B3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imposter.Client", "Imposter.Client\Imposter.Client.csproj", "{24F07173-D4A7-41C6-BA51-4516AB68343C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imposter.Shared", "Imposter.Shared\Imposter.Shared.csproj", "{1D109A96-FADF-41B9-A845-457A7A5C0C5A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "submodules", "submodules", "{DFA3CC6A-A27C-4713-876D-A55912C78C45}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel", "..\submodules\Hazel-Networking\Hazel\Hazel.csproj", "{02CFBD30-D77D-400F-94B2-700F60EFDD7F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imposter.Tests", "Imposter.Tests\Imposter.Tests.csproj", "{C1385C67-A7DD-46D2-80F6-FA428B85FB22}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{56DD9707-D811-4056-9E2C-8A9CC2479B07}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Imposter.Tools.Proxy", "Imposter.Tools.Proxy\Imposter.Tools.Proxy.csproj", "{D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|x86.ActiveCfg = Debug|Win32
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Debug|x86.Build.0 = Debug|Win32
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|Any CPU.ActiveCfg = Release|Win32
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|x86.ActiveCfg = Release|Win32
+ {097BE795-88E9-4E7B-92F3-F6DDDBE3F012}.Release|x86.Build.0 = Release|Win32
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Debug|x86.Build.0 = Debug|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|x86.ActiveCfg = Release|Any CPU
+ {1B0390AF-A4F3-4FE4-B093-708B0135C0B3}.Release|x86.Build.0 = Release|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Debug|x86.Build.0 = Debug|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|x86.ActiveCfg = Release|Any CPU
+ {24F07173-D4A7-41C6-BA51-4516AB68343C}.Release|x86.Build.0 = Release|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Debug|x86.Build.0 = Debug|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|x86.ActiveCfg = Release|Any CPU
+ {1D109A96-FADF-41B9-A845-457A7A5C0C5A}.Release|x86.Build.0 = Release|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|x86.Build.0 = Debug|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|x86.ActiveCfg = Release|Any CPU
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|x86.Build.0 = Release|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Debug|x86.Build.0 = Debug|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|x86.ActiveCfg = Release|Any CPU
+ {C1385C67-A7DD-46D2-80F6-FA428B85FB22}.Release|x86.Build.0 = Release|Any CPU
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Debug|x86.Build.0 = Debug|Any CPU
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {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
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {06245255-E585-483C-A4AD-71B7B568C6C8}
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {02CFBD30-D77D-400F-94B2-700F60EFDD7F} = {DFA3CC6A-A27C-4713-876D-A55912C78C45}
+ {D16B8DE9-8EE2-4F6A-9352-305D5AB0233D} = {56DD9707-D811-4056-9E2C-8A9CC2479B07}
+ EndGlobalSection
+EndGlobal