Compare commits

...
105 Commits
Author SHA1 Message Date
js6pak d1ffdb95e1 Update appveyor.yml
crappveyor
2022-06-11 15:41:48 +00:00
miniduikboot 4d11cf91ad Remove Auth- and AnnouncementsServer sections (#484) 2022-06-03 18:58:00 +00:00
miniduikboot 84aba52198 Remove 75% of the question marks of PR #481 (#482) 2022-06-03 18:57:47 +00:00
js6pakandminiduikboot 6e4bb71289 Add Serilog.Settings.Configuration (#468)
* Add Serilog.Settings.Configuration

* Add docs section for logging

* Elaborate Serilog documentation

Co-authored-by: miniduikboot <mini@duikbo.at>
2022-06-03 20:31:52 +02:00
js6pak 8fec1a211e Merge pull request #483 from js6pak/fix-disconnect-reason
Fix non custom disconnect reasons
2022-06-03 20:31:10 +02:00
js6pak 85a2fce6c1 Fix non custom disconnect reasons 2022-06-03 20:17:59 +02:00
miniduikboot 224fdd1d5b Allow adding custom tasks on standard maps (#481)
This is needed for mods that add tasks to standard maps, but also mods
that add custom maps: because their custom netobjects don't get
registered Impostor thinks people are still on the old map when people
switch to the custom map after playing a game.
2022-05-25 19:53:32 +02:00
miniduikboot 380ba46f11 Upgrade to .NET 6 (#479)
* Upgrade to .NET 6

.NET 5 has gone EOL, so move to a supported version.

* Upgrade Docker container as well
* Update docs for .NET 6
* Re-add hack for System.IO.Pipelines
* Bump dependency versions
* Update the deps of the other projects too
* Only declare TargetFramework once
* Dockerfile: also copy over Directory.Build.props
* Cake: pick up target framework automatically
* Upgrade Cake as well
2022-05-25 19:52:22 +02:00
miniduikboot aeefc40ef1 Merge pull request #474 from SubmergedAmongUs/master
Add handling for null ShipStatus
2022-04-20 18:49:37 +02:00
AlexejheroYTB 743b8ed5cb Remove null check for ShipStatus known to not be null 2022-04-20 08:07:59 +03:00
AlexejheroYTB ed008872f6 Add handling for null ShipStatus 2022-04-16 17:11:58 +03:00
js6pak 4797d899af Merge pull request #471 from Impostor/feature/2022.3.29 2022-04-09 12:42:51 +00:00
js6pak c834013bd3 Simplify Message07JoinedGameS2CPre20220202 2022-04-07 16:58:33 +02:00
js6pak 70c180fb19 Make GameOptionsData de/serialization less confusing 2022-04-07 11:58:16 +02:00
miniduikboot 729368d777 Do not add ProductUserId/Friendcode on <2.24
Because it is fine to add data to the end of messages the new fields
could be added to Message 1 just fine, but because Message 7 contains a
loop, we need to send a different message to old clients there.
2022-04-05 20:05:06 +02:00
miniduikboot 5c06d1dd4d fixup! Add empty ProductUserId and FriendCode to packets 2022-04-05 19:52:25 +02:00
miniduikboot 446e5b8705 Add empty ProductUserId and FriendCode to packets
They are not sent to the server and no clue how to retrieve them out of band.
2022-04-05 19:52:25 +02:00
miniduikboot ae00ff9295 Barebones port 2022-04-05 19:52:25 +02:00
miniduikboot 400592b99c Merge pull request #467 from miniduikboot/fix/race-dc-send-all-except
Do not broadcast messages to disconnected clients
2022-03-26 10:35:54 +01:00
miniduikboot 9906c409da Do not broadcast messages to disconnected clients
This method is used when players have disconnected. If multiple players
disconnect at once, this method leads to exceptions.
2022-03-17 21:35:08 +01:00
miniduikboot c26d03a8bc Merge pull request #464 from miniduikboot/fix/custom-dc-msg
Fix/custom dc msg
2022-02-28 18:04:47 +01:00
miniduikboot ca48d672d5 Reword obsoletion message 2022-02-26 20:33:02 +01:00
miniduikboot 0b431dc5fa Change DisconnectAsync to use extension instead 2022-02-26 20:31:45 +01:00
miniduikboot 0b0c8274aa Turn DisconnectConnection into an extension 2022-02-26 20:31:23 +01:00
miniduikboot 36f8255e26 Fixup 608414326d
Oops, I accidentally forgot to remove the previous line when adding the
file to my commit
2022-02-26 19:08:57 +01:00
miniduikboot 919b094018 Deprecate JoinGame errors
Starting from version 2021.11.9, join errors no longer exist, instead a
client will show the message "An unknown error disconnected you from the
server". As a workaround, the connection is disconnected entirely when
an error occurs, which allows us to show a custom message.
2022-02-26 19:08:57 +01:00
js6pak 6125fd7006 Merge pull request #465 from miniduikboot/fix/2-28-bump 2022-02-26 17:26:54 +01:00
miniduikboot a3c0c8cacf Bump network version 2022-02-25 07:05:22 +01:00
miniduikboot b252bf2ee7 Merge pull request #460 from miniduikboot/feature/queryplatformids
Add QueryPlatformIds
2022-02-14 22:12:47 +01:00
miniduikboot 9b5a63e030 Add QueryPlatformIds
Some platforms (notable the Microsoft Store version) ask for this before connecting.
2022-02-02 22:17:47 +01:00
miniduikboot 6a8f089576 Merge pull request #455 from miniduikboot/fix/i18n-nicknames
Fix/i18n nicknames
2022-01-27 19:23:30 +01:00
miniduikboot d994323083 Merge pull request #458 from miniduikboot/fix/ac-engineer-snapto
AC: Allow engineers to move to a different vent.
2022-01-27 19:22:59 +01:00
miniduikboot 3f374da44b Partial revert of cc8d5d2: Readd length check
Oversized nicknames can cause bigger issues, so it is desireable to
still block them. .Length counts codepoints, not bytes, which means
that 10 character Japanese nicknames, which are configurable ingame,
should still be allowed.
2022-01-27 18:24:02 +01:00
miniduikboot bfd2efa716 AC: Allow engineers to move to a different vent. 2022-01-27 18:10:46 +01:00
miniduikboot 6867dec7ee Merge pull request #453 from miniduikboot/fix/ga-half-cooldown
AC CheckMurder: Require half of cooldown
2022-01-14 23:06:28 +01:00
miniduikboot 7132dc2d38 Add comment to clarify why this exists 2022-01-14 19:47:58 +01:00
miniduikboot 682bfac558 Merge pull request #452 from miniduikboot/fix/host-migrate
Before this commit, when the host of a game disconnects, the InnerPlayerInfo from the player is removed, which causes the new host to disconnect because of a deserialization error. This PR fixes this issue
2022-01-14 18:21:16 +01:00
miniduikboot cc8d5d2a45 Remove name length/validity checks from JoinGame
Length and validity are checked also checked by the anticheat. In case
Impostor goes out of sync with the allowed characters again, this can
just be fixed by disabling the Anticheat.

Check for NullOrWhiteSpace is kept because an empty username could be annoying
2022-01-10 20:54:45 +01:00
miniduikboot e6a35344c3 Sync IsCharAllowed with Innersloth implementation 2022-01-10 20:54:45 +01:00
miniduikboot db52b48cd6 AC CheckMurder: Require half of cooldown
When the Guardian Angel protects a player, the impostor that just missed
their kill gets back half of their cooldown. Keeping track of whether or
not the player was protected is more difficult due to how the Guardian
Angel is implemented, so instead we halve the minimum time between kill
in order to minimize false positives.
2022-01-08 19:20:11 +01:00
miniduikboot 50a526a02d Clamp taskCount to the length of the task list
New tasks cannot be created at this point because we don't know the
underlying tasks. So we try to maintain the tasks we do know of.
2022-01-08 17:42:05 +01:00
miniduikboot 608414326d Do not destroy PlayerControl if game is starting
This player may still get assigned tasks, which could lead to
deserialization problems.
2022-01-08 17:41:52 +01:00
miniduikboot d8bcb52c90 Mark game as started when RPC SetRole is called
This used to be done when SetInfected was called, but this RPC is no
longer used since 2021.11.9 in favor of this RPC.
2022-01-08 17:41:14 +01:00
miniduikboot d7d7547182 Merge pull request #442 from Impostor/2021.11.9
2021.11.9 and 2021.12.14 support
2021-12-21 23:27:38 +01:00
js6pak 64e27c3e21 Set RoleType in PlayerInfo.Deserialize 2021-12-15 15:36:01 +01:00
js6pak a0b433500c Don't do public methods in interfaces 2021-12-15 15:35:46 +01:00
js6pak c51a1f9c1f Readd HandleSet{Pet/Hat/Skin} 2021-12-15 15:33:01 +01:00
js6pak e15ac51dcf Implement cosmetic rpc methods 2021-12-15 15:22:18 +01:00
js6pak bde3a22f6e Document IClient.PlatformSpecificData 2021-12-15 14:52:22 +01:00
js6pak 6118e6b7b8 Change RoleOptionsData floats to be bytes as they can't ever be floats anyway 2021-12-15 14:51:07 +01:00
miniduikboot 32ecfedfba Add initial support for 2021.12.14 2021-12-14 21:38:26 +01:00
miniduikboot b6fe79cc8b Add RPC's for CheckProtect and ProtectPlayer 2021-11-22 23:04:56 +01:00
miniduikboot aeaffc849a Implement new MurderPlayer logic
In 2021.11.9, the host now uses MurderPlayer instead of impostors.
Impostor now use CheckMurder to request the host to kill a player. This
allows the host to prevent the target of the Guardian Angel from dying.

However, if the impostor that is checking the kill is also the host, AU
internally processes the CheckMurder RPC and sends the MurderPlayer RPC
first, *then* it sends the CheckMurder RPC.
2021-11-22 22:12:34 +01:00
miniduikboot a15f1b6a4c Add platform data to GameList 2021-11-15 22:55:57 +01:00
js6pak afb7051218 Fix ServerReplay 2021-11-13 19:51:52 +01:00
miniduikboot 8a0e9e876e Bump to 1.7 2021-11-12 23:12:47 +01:00
miniduikboot 22f1d74cdf Allow Shapeshifters to use RPC Shapeshift 2021-11-12 23:12:47 +01:00
miniduikboot e6d38f4b72 Allow Engineers to vent 2021-11-12 22:57:29 +01:00
miniduikboot 93c80c1412 Implement Rpc 44: SetRole 2021-11-12 22:56:35 +01:00
miniduikboot c3e0157209 Add name/psd/level to JoinGame- and JoinedGameS2C
Two players can now connect to the same lobby
2021-11-12 21:11:48 +01:00
miniduikboot 0769752407 Remove initial state flag from InnerGameData 2021-11-10 23:04:21 +01:00
miniduikboot fba1a894cc Implement SetActivePodType 2021-11-10 21:23:00 +01:00
js6pak 4d1aec9070 WIP 2021-11-10 20:46:56 +01:00
js6pak e0c7086973 RIP AnnouncementsServer 2021-11-10 13:52:15 +01:00
js6pak 125da11d61 [skip ci] Auto deploy github releases 2021-08-05 14:24:51 +02:00
js6pak d86efc1345 Merge pull request #431 from miniduikboot/feature/2021.6.30 2021-07-30 20:41:57 +02:00
js6pak 301a851ec3 Handle all handshake versions 2021-07-27 17:42:11 +02:00
js6pak 86c27402e2 Language and chat mode api 2021-07-27 16:59:09 +02:00
miniduikboot c63eb3b2d1 Improve docs (#435) 2021-07-25 19:53:43 +00:00
miniduikboot a1dac9f8b7 Address feedback 2021-07-25 20:22:02 +02:00
miniduikboot 3065e8df48 Check client version when parsing C2S handshake
2021.6.30 added new fields, which aren't present on old clients.

This commit makes sure players on old versions are disconnected properly
instead of the server erroring internally.
2021-07-21 21:53:22 +02:00
miniduikboot dc52e478dc Disable ownership check for BootFromVent 2021-07-21 20:46:33 +02:00
miniduikboot eb86c3c683 Implement RPC 35: Update System 2021-07-21 20:46:33 +02:00
miniduikboot e7c83769ef Implement RPC 34: Boot from vent 2021-07-21 20:46:33 +02:00
miniduikboot c6b8066794 Bump to 1.6 because new net version 2021-07-21 20:46:33 +02:00
miniduikboot 1ec5a050ad Add Vent Cleaning task
As these spawn in various rooms under the same task id the name of this
task does not include a room name
2021-07-21 20:46:33 +02:00
miniduikboot 1e7d132f31 Add Airship Lounge/Showers Empty Garbage tasks
Also Calibrate Distributor moved up
2021-07-21 20:46:33 +02:00
miniduikboot 9543134ef3 Add two extra fields to handshake 2021-07-21 20:46:33 +02:00
miniduikboot 389c72caa6 Bump client version 2021-07-21 20:46:33 +02:00
miniduikbootandjs6pak 66b4d860a9 Improve error when client version is unsupported (#432)
* Improve error when client version is unsupported

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

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

* Rewrite versionCompare switch case

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

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

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

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "1.1.0",
"version": "2.2.0",
"commands": [
"dotnet-cake"
]
+3 -3
View File
@@ -9,14 +9,14 @@ We are always looking for people to help improve Impostor.
- Don't commit unnecessary changes to the codebase or debugging code.
- Write meaningful commits or squash them.
- Please try to follow the code style of the rest of the codebase. An `.editorconfig` file has been provided to keep consistency.
- Stylecop is also enabled: please make sure your PR compiles without warnings.
## Pull requests
- Only make pull requests to the `dev` branch.
- Only implement one feature per pull request to keep it easy to understand.
- Expect comments or questions on your pull request from the project maintainers. We try to keep the code as consistent and maintainable as possible.
- Each pull request should come from a new branch in your fork, it should have a meaningful name.
- Each pull request should come from a new branch in your fork, it should have a meaningful name.
- We try to respond to pull requests as fast as possible. If you think we might have missed it, let us know on [Discord](https://discord.gg/Mk3w6Tb).
If you have any questions, let us know.
If you have any questions, let us know.
+4 -3
View File
@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:5.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0 AS build
# See for all possible platforms
# https://github.com/containerd/containerd/blob/master/platforms/platforms.go#L17
@@ -12,6 +12,7 @@ WORKDIR /source
COPY src/Impostor.Server/Impostor.Server.csproj ./src/Impostor.Server/Impostor.Server.csproj
COPY src/Impostor.Api/Impostor.Api.csproj ./src/Impostor.Api/Impostor.Api.csproj
COPY src/Impostor.Hazel/Hazel/Hazel.csproj ./src/Impostor.Hazel/Hazel/Hazel.csproj
COPY src/Directory.Build.props ./src/Directory.Build.props
RUN case "$TARGETARCH" in \
amd64) NETCORE_PLATFORM='linux-x64';; \
@@ -35,8 +36,8 @@ RUN case "$TARGETARCH" in \
dotnet publish -c release -o /app -r "$NETCORE_PLATFORM" -p:VersionSuffix="$VERSIONSUFFIX" --no-restore ./src/Impostor.Server/Impostor.Server.csproj
# Final image.
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime:5.0
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime:6.0
WORKDIR /app
COPY --from=build /app ./
EXPOSE 22023/udp
ENTRYPOINT ["./Impostor.Server"]
ENTRYPOINT ["./Impostor.Server"]
+6 -36
View File
@@ -3,14 +3,11 @@
[![Discord](https://img.shields.io/badge/Discord-chat-blue?style=flat-square)](https://discord.gg/Mk3w6Tb)
[![AppVeyor](https://img.shields.io/appveyor/build/Impostor/Impostor/master?style=flat-square)](https://ci.appveyor.com/project/Impostor/Impostor/branch/master)
Impostor is one of the first **Among Us** private servers, written in C#.
Impostor is the first working **Among Us** private server, written in C#.
| Impostor version | Among Us version | Experimental | Download |
|-|-|-|-|
| 1.1.0 | 2020.09.07 - 2020.09.22 | No | [![Download](https://img.shields.io/badge/Download-v1.1.0-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.1.0) |
| 1.2.2 | 2020.09.22 - 2020.11.17 | No | [![Download](https://img.shields.io/badge/Download-v1.2.2-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.2.2) |
| 1.3.0 | 2021.3.5 | No | [![Download](https://img.shields.io/badge/Download-v1.3.0-blue?style=flat-square)](https://github.com/Impostor/Impostor/releases/tag/v1.3.0) |
| 1.4.0 | 2021.3.31 - 2021.4.2 | Yes | [![Download](https://img.shields.io/badge/Download-master-blue?style=flat-square)](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts) |
As Among Us is actively updated, you may want to use the latest build from the master branch if you want to play on the latest version of Among Us that supported by Impostor.
If you want to play on an older version of Among Us, you can download old releases from [here](https://github.com/Impostor/Impostor/releases/), however these versions are less supported.
## Features
@@ -22,34 +19,7 @@ Impostor is one of the first **Among Us** private servers, written in C#.
### Client
If you just want to play on a server hosted by someone else, you need to follow these instructions.
#### Windows
1. Find the [latest release](https://github.com/AeonLucid/Impostor/releases/latest).
2. Download `Impostor-Patcher-win-x64.zip`.
3. Extract the zip.
4. Run `Impostor.Patcher.exe`.
5. Follow the instructions inside the application.
![Client](docs/images/client.jpg)
If you do not wish to execute any programs. Follow the instructions in [this website](https://impostor.github.io/Impostor)
#### Android
##### Android 10 and below.
1. Go to [this website](https://impostor.github.io/Impostor) **(ON YOUR MOBILE DEVICE)**
2. Follow the instructions listed there.
##### Android 11.
1. Connect your phone to a computer. Go to [this website](https://impostor.github.io/Impostor) on the computer and follow the steps 1 and 2 to generate a `regionInfo.dat` file.
2. Instead of following the next steps, open the phone's internal storage on your computer and navigate to `/sdcard/Android/data/com.innersloth.spacemafia/files`.
3. Copy the generated `regionInfo.dat` file into the `files` folder you just navigated to.
#### iOS
iOS devices need to be jailbroken in order to connect to Impostor servers.
If you just want to play on a server hosted by someone else, you need to configure your client to connect to an Impostor server. Please go to [this website](https://impostor.github.io/Impostor) to set up your client by following the instructions for your OS there.
### Server
@@ -57,7 +27,7 @@ See the [docs](docs/Running-the-server.md) for instructions on how to set it up.
## Troubleshooting
See [TROUBLESHOOTING](docs/TROUBLESHOOTING.md) to solve issues the Impostor client or the server.
See [TROUBLESHOOTING](docs/TROUBLESHOOTING.md) to solve issues with the Impostor client or the server.
## Contributing
+22 -10
View File
@@ -20,11 +20,12 @@ image: Ubuntu2004
install:
- git submodule update --init --recursive
- ps: dotnet tool restore
- dotnet tool restore
build_script:
- ps: dotnet cake build.cake --bootstrap
- ps: dotnet cake build.cake --pack
- pwsh: if ($env:APPVEYOR_REPO_TAG_NAME) { Update-AppveyorBuild -Version "$($env:APPVEYOR_REPO_TAG_NAME -replace "v")" }
- dotnet cake build.cake --bootstrap
- dotnet cake build.cake --pack
test: off
@@ -34,13 +35,24 @@ artifacts:
- path: ./build/*.nupkg
- path: ./build/*.snupkg
# deploy:
# - provider: NuGet
# artifact: /.*(\.|\.s)nupkg/
# on:
# branch: dev
# api_key:
# secure: 4OHXl+m1KVi60hB1j54MpdP8tVv0UNfkdF4rVP+2AhAjN4a2MVT+Bl90gJZ96xHF
deploy:
# - provider: NuGet
# artifact: /.*(\.|\.s)nupkg/
# on:
# branch: master
# api_key:
# secure: 4OHXl+m1KVi60hB1j54MpdP8tVv0UNfkdF4rVP+2AhAjN4a2MVT+Bl90gJZ96xHF
- provider: GitHub
tag: $(APPVEYOR_REPO_TAG_NAME)
release: $(APPVEYOR_PROJECT_NAME) $(APPVEYOR_BUILD_VERSION)
description: 'DRAFT'
auth_token:
secure: vyQ+zijv9g99wEgIdSC8uPSlnFvH3KZiD0yE7mNA3r3z2OY4lTEF6bpuUvtjoaM3
artifact: /.*/
draft: true
on:
APPVEYOR_REPO_TAG: true
only_commits:
files:
+13 -22
View File
@@ -1,6 +1,6 @@
#addin "nuget:?package=SharpZipLib&Version=1.3.1"
#addin "nuget:?package=Cake.Compression&Version=0.2.6"
#addin "nuget:?package=Cake.FileHelpers&Version=4.0.1"
#addin "nuget:?package=SharpZipLib&Version=1.3.3"
#addin "nuget:?package=Cake.Compression&Version=0.3.0"
#addin "nuget:?package=Cake.FileHelpers&Version=5.0.0"
var buildId = EnvironmentVariable("GITHUB_RUN_NUMBER") ?? EnvironmentVariable("APPVEYOR_BUILD_VERSION");
var buildRelease = EnvironmentVariable("APPVEYOR_REPO_TAG") == "true";
@@ -10,7 +10,7 @@ var buildDir = MakeAbsolute(Directory("./build"));
var target = Argument("target", "Deploy");
var configuration = Argument("configuration", "Release");
var msbuildSettings = new DotNetCoreMSBuildSettings();
var msbuildSettings = new DotNetMSBuildSettings();
if (buildRelease)
{
@@ -31,10 +31,9 @@ private void ImpostorPublish(string name, string project, string runtime, bool i
var projBuildDir = buildDir.Combine(name + "_" + runtime);
var projBuildName = name + "_" + buildVersion + "_" + runtime;
DotNetCorePublish(project, new DotNetCorePublishSettings {
DotNetPublish(project, new DotNetPublishSettings {
Configuration = configuration,
NoRestore = true,
Framework = "net5.0",
Runtime = runtime,
SelfContained = false,
PublishSingleFile = true,
@@ -64,7 +63,7 @@ private void ImpostorPublishNF(string name, string project) {
var projBuildDir = buildDir.Combine(name + "_" + runtime);
var projBuildZip = buildDir.CombineWithFilePath(name + "_" + buildVersion + "_" + runtime + ".zip");
DotNetCorePublish(project, new DotNetCorePublishSettings {
DotNetPublish(project, new DotNetPublishSettings {
Configuration = configuration,
NoRestore = true,
Framework = "net472",
@@ -90,18 +89,17 @@ Task("Clean")
Task("Restore")
.Does(() => {
DotNetCoreRestore("./src/Impostor.sln");
DotNetRestore("./src/Impostor.sln");
});
Task("Replay")
.Does(() => {
// D:\Projects\GitHub\Impostor\Impostor\src\Impostor.Tools.ServerReplay\sessions
DotNetCoreRun(
DotNetRun(
"./src/Impostor.Tools.ServerReplay/Impostor.Tools.ServerReplay.csproj",
"./src/Impostor.Tools.ServerReplay/sessions", new DotNetCoreRunSettings {
"./src/Impostor.Tools.ServerReplay/sessions", new DotNetRunSettings {
Configuration = configuration,
NoRestore = true,
Framework = "net5.0"
});
});
@@ -111,16 +109,9 @@ Task("Build")
.IsDependentOn("Replay")
.Does(() => {
// Tests.
DotNetCoreBuild("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetCoreBuildSettings {
DotNetBuild("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetBuildSettings {
Configuration = configuration,
});
// Client.
ImpostorPublishNF("Impostor-Patcher", "./src/Impostor.Patcher/Impostor.Patcher.WinForms/Impostor.Patcher.WinForms.csproj");
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "win-x64");
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "osx-x64");
ImpostorPublish("Impostor-Patcher-Cli", "./src/Impostor.Patcher/Impostor.Patcher.Cli/Impostor.Patcher.Cli.csproj", "linux-x64");
// Server.
ImpostorPublish("Impostor-Server", "./src/Impostor.Server/Impostor.Server.csproj", "win-x64", true);
@@ -130,7 +121,7 @@ Task("Build")
ImpostorPublish("Impostor-Server", "./src/Impostor.Server/Impostor.Server.csproj", "linux-arm64", true);
// API.
DotNetCorePack("./src/Impostor.Api/Impostor.Api.csproj", new DotNetCorePackSettings {
DotNetPack("./src/Impostor.Api/Impostor.Api.csproj", new DotNetPackSettings {
Configuration = configuration,
OutputDirectory = buildDir,
IncludeSource = true,
@@ -142,7 +133,7 @@ Task("Build")
Task("Test")
.IsDependentOn("Build")
.Does(() => {
DotNetCoreTest("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetCoreTestSettings {
DotNetTest("./src/Impostor.Tests/Impostor.Tests.csproj", new DotNetTestSettings {
Configuration = configuration,
NoBuild = true
});
@@ -158,4 +149,4 @@ Task("Deploy")
// EXECUTION
//////////////////////////////////////////////////////////////////////
RunTarget(target);
RunTarget(target);
+8 -15
View File
@@ -1,19 +1,20 @@
# Building from source
The solution contains two main projects, the Impostor server and patcher. The server is built using [.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0) and the winforms patcher is with [.NET Framework 4.7.2](https://dotnet.microsoft.com/download/dotnet-framework/net472).
The solution contains the Impostor server and its dependencies, like Hazel and the plugin API. The server is built using [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0).
## Cloning Impostor
You need to clone Impostor using git.
You need to clone Impostor using git:
```bash
git clone https://github.com/AeonLucid/Impostor.git
git clone https://github.com/Impostor/Impostor.git
```
## Building the server
### Dependencies
- [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0)
- [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0)
- [Rider](https://www.jetbrains.com/rider/) or [Visual Studio](https://visualstudio.microsoft.com/vs/) (Optional, only if you want the full IDE experience)
### Build using the CLI
@@ -21,16 +22,8 @@ git clone https://github.com/AeonLucid/Impostor.git
```bash
cd src/Impostor.Server/
dotnet build
# Or if you want a single file, ready for production: (change linux-x64 to win-x64 if you use Windows)
dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true
```
To setup the server, please look at [Running the server](Running-the-server.md).
## Building the winforms patcher
### Dependencies
* [.NET Framework 4.7.2 Developer Pack](https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net472-developer-pack-offline-installer) or [Mono](https://www.mono-project.com/download/)
### Build using the CLI
```bash
cd src/Impostor.Patcher/Impostor.Patcher.WinForms
dotnet build
```
+7 -3
View File
@@ -1,11 +1,15 @@
# Frequently Answered Questions
## What is this?
The Impostor project is a reverse engineered and open sourced server for the game Among Us. The game itself is developed by [InnerSloth](http://www.innersloth.com/) while this project is maintained by the community. This project was built out of frustration for the lack of server availability in certain regions and the inability for the core developer, [AeonLucid](https://github.com/AeonLucid), to join a public game. As of this time, it has not been officially endorsed by the studio.
The Impostor project is a reverse engineered and open source server for the game Among Us. The game itself is developed by [Innersloth](http://www.innersloth.com/) while this project is maintained by the community. This project was built out of frustration for the lack of server availability in certain regions and the inability for the core developer, [AeonLucid](https://github.com/AeonLucid), to join a public game. As of this time, it has not been officially endorsed by the studio.
## Can this be used with the mobile version of the game?
Yes, Impostor can be used with both the Android and iOS\* versions of the game.
###### \* In order to play on an Impostor server with iOS, you _must_ have a jailbroken device.
Yes, Impostor can be used with both the Android and iOS versions of the game. You can add a custom server via a link, you can do this using the [site](https://impostor.github.io/Impostor)
## How can I get started?
See [Setting up your Server](Running-the-server.md) for more information on running the server and [Client Setup](https://impostor.github.io/Impostor/) for helping your friends join in!
If you're having issues with the server software, check the [Troubleshooting page](TROUBLESHOOTING.md).
+16 -15
View File
@@ -1,30 +1,31 @@
# Running the server
There are currently two modes to run the Impostor server in. The first way is the simplest one and is the one you should probably use. The other way will distribute players across other servers and is a more advanced configuration.
There are currently two modes to run the Impostor server. The first way, Single Server, is the simplest one, can handle up to a few hundred simultaneous players, and is the one you should probably use. Multi-Server mode will distribute players across other servers and is intended for advanced users.
## Single server
### Without docker
1. Install the **.NET 5.0 runtime**.
- [Windows x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.0-windows-x64-installer)
- [Linux x64](https://docs.microsoft.com/en-us/dotnet/core/install/linux)
- [macOS x64](https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-5.0.0-macos-x64-installer)
2. Find the [latest master release](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts).
3. Download either the Windows or the Linux version.
### Without Docker
1. Install the [.NET 6.0 runtime](https://dotnet.microsoft.com/download/6.0). Installing the SDK also works, but is not necessary unless you plan on developing Impostor or Impostor plugins. If you're asked to pick between a console, desktop or server runtime, the console runtime is enough.
2. Find the [latest release](https://github.com/Impostor/Impostor/releases) or the [latest CI build](https://ci.appveyor.com/project/Impostor/Impostor/branch/master/artifacts).
3. Download the version for your OS (linux/win/osx). Impostor is built for multiple CPU-architectures, you most likely want the x64 version, unless you are running on a Raspberry Pi or another device with an ARM processor.
4. Extract the zip.
5. Modify `config.json` to your liking. Documentation can be found [here](Server-configuration.md) *(this step is mandatory if you want to expose this server to other devices)*
6. Run `Impostor.Server.exe` (Windows) / `Impostor.Server` (Linux)
5. Modify `config.json` to your liking. Documentation can be found [here](Server-configuration.md) _(this step is mandatory if you want to expose this server to other devices)_
6. Run `Impostor.Server` (Linux/macOS) or `Impostor.Server.exe` (Windows)
### Using docker
### Using Docker
[![Docker Image](https://img.shields.io/docker/v/aeonlucid/impostor?sort=semver)](https://hub.docker.com/repository/docker/aeonlucid/impostor)
[![Docker Image](https://img.shields.io/docker/v/aeonlucid/impostor/nightly)](https://hub.docker.com/repository/docker/aeonlucid/impostor)
[![Docker Image](https://img.shields.io/docker/v/aeonlucid/impostor?sort=semver)](https://hub.docker.com/r/aeonlucid/impostor)
[![Docker Image](https://img.shields.io/docker/v/aeonlucid/impostor/nightly)](https://hub.docker.com/r/aeonlucid/impostor)
After installing Docker, you can just start a Docker container with `docker run`:
```
docker run -p 22023:22023/udp aeonlucid/impostor:nightly
```
### Using docker-compose
### Using Docker Compose
```
version: '3.4'
@@ -42,7 +43,7 @@ services:
## Multiple servers
Follow the steps from the single server on two or more servers.
Follow the steps from the single server on two or more servers. You should only need to set this up if you have a very large server and can no longer fit everyone on one server: if this is your first time installing Impostor, use a single server.
### Master server
+69 -42
View File
@@ -1,35 +1,83 @@
# Server configuration
Some information about all the possible configurations. Click [here](https://github.com/AeonLucid/Impostor/blob/master/src/Impostor.Server/config.full.json) to see all the possible config options.
Some information about all the possible configurations. Click [here](https://github.com/Impostor/Impostor/blob/master/src/Impostor.Server/config-full.json) to see all the possible config options.
## Options
### Required Server Configuration
| Key | Default | Description |
|-|-|-|
| **PublicIp** | `127.0.0.1` | This needs to the public IPv4 address of the server which you give to others to connect. You can find your IPv4 address [on this website](http://whatismyip.host/). Unless you are only planning to use Impostor privately, on your local network, you should change this to your public ip. It is also possible to use hostnames instead of IPv4 addresses, which will be resolved to IPv4 addresses. |
| **PublicPort** | `22023` | The public port of the server which you give to others to connect. (**This is the external port you configure on your router when port forwarding.**) Usually `22023`. |
| **ListenIp** | `0.0.0.0` | The network interface to listen on. If you do not know what to put here, use `0.0.0.0`. Since 1.2.2 it is also possible to use hostnames instead of IPv4 addresses, these must resolve to a valid IPv4 address. |
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. |
| Key | Default | Description |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **PublicIp** | `127.0.0.1` | This needs to the public IPv4 address of the server which you give to others to connect. You can find your IPv4 address [on this website](http://whatismyip.host/). Unless you are only planning to use Impostor privately, on your local network, you should change this to your public ip. It is also possible to use hostnames instead of IPv4 addresses, which will be resolved to IPv4 addresses. |
| **PublicPort** | `22023` | The public port of the server which you give to others to connect. (**This is the external port you configure on your router when port forwarding.**) Usually `22023`. |
| **ListenIp** | `0.0.0.0` | The network interface to listen on. If you do not know what to put here, use `0.0.0.0`. Since 1.2.2 it is also possible to use hostnames instead of IPv4 addresses, these must resolve to a valid IPv4 address. |
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. |
### AntiCheat
| Key | Default | Value |
|-|-|-|
| **Enabled** | `true` | Whether the anticheat should be enabled. |
| **BanIpFromGame** | `true` | When a player is caught hacking, they will be kicked from the server. If this value is set to `true`, the player will be banned instead and will not be able to rejoin that specific game. **(Setting this to false does not disable the anti-cheat!)** |
Impostor has an Anticheat that makes it possible to kick cheaters from games automatically. Note that the anticheat is tuned on the vanilla version of the game, so client-side modifications could trigger the Anticheat if you're playing with them.
| Key | Default | Value |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Enabled** | `true` | Whether the anticheat should be enabled. |
| **BanIpFromGame** | `true` | When anticheat is enabled and a player is caught hacking, they will be kicked from the server. If this value is set to `true`, the player will be banned instead and will not be able to rejoin that specific game. |
### Debug
The Debug configuration is used to enable the game recorder. This is mostly useful when developing Impostor.
| Key | Default | Value |
| ----------------------- | ------- | -------------------------------------------- |
| **GameRecorderEnabled** | `false` | Enables the Game Recorder. |
| **GameRecorderPath** | _empty_ | Path where the recorded games will be saved. |
### ServerRedirector
In a multi-node setup these need to be specified.
| Key | Default | Value |
|-|-|-|
| **Enabled** | `false` | Whether the server runs in multi-node setup. If this is `false`, all other options in this section do not have any effect. |
| **Master** | `false` | Whether the current server is a master. A master is responsible for redirecting clients to nodes |
| **Locator** | | Fill in either `Redis` or `UdpMasterEndpoint` to choose which method to use for locating other nodes. This must be the same across all servers. |
| **>Redis** | | Format `127.0.0.1.6379`, you can also use a password like so: `127.0.0.1.6379,password=value`. |
| **>UdpMasterEndpoint** | | On the master, this value acts as a listen ip and port. On a node, this should be the public ip and port of the master. Format `127.0.0.1:32320`. |
| **Nodes** | | An array containing public ips and ports of nodes. Only needs to be set on the master. See above for an example. |
In a multi-node setup these values need to be specified. Note that most people do not need to run a Multi-node setup.
| Key | Default | Value |
| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Enabled** | `false` | Whether the server runs in multi-node setup. If this is `false`, all other options in this section do not have any effect. |
| **Master** | `false` | Whether the current server is a master. A master is responsible for redirecting clients to nodes |
| **Locator** | | Fill in either `Redis` or `UdpMasterEndpoint` to choose which method to use for locating other nodes. This must be the same across all servers. |
| **>Redis** | | Format `127.0.0.1.6379`, you can also use a password like so: `127.0.0.1.6379,password=value`. |
| **>UdpMasterEndpoint** | | On the master, this value acts as a listen ip and port. On a node, this should be the public ip and port of the master. Format `127.0.0.1:32320`. |
| **Nodes** | | An array containing public ips and ports of nodes. Only needs to be set on the master. See above for an example. |
### Serilog (Logging)
Impostor's log framework, Serilog, can be configured in the config file. You can change its default log level and you can add additional sinks.
| Key | Default | Value |
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **MinimumLevel** | `Information` | Minimum log level for a message to be printed. If a log entry is as severe or more severe than this level, it will be printed. From most severe to least severe: `Fatal`,`Error`, `Warning`, `Information`, `Debug`, `Verbose` |
| **Using** | `[]` | List of additional Serilog Sinks assemblies to load. |
| **WriteTo** | `[]` | Additional logging sinks. See the Serilog documentation or the example in this section. Serilog |
For more information, check the [Serilog.Settings.Configuration](https://github.com/serilog/serilog-settings-configuration) documentation.
For example, to add logging to a file, you should add the following snippet to your configuration:
```json
"Serilog": {
"Using": [
"Serilog.Sinks.File"
],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "logs/log.txt",
"rollingInterval": "Day"
}
}
]
}
```
Next to that, you also need to copy over Serilog.Sinks.File.dll from [NuGet](https://www.nuget.org/packages/Serilog.Sinks.File/). See the [Serilog.Sinks.File documentation](https://github.com/serilog/serilog-sinks-file#json-appsettingsjson-configuration) for a list of parameters that can be configured.
Other Serilog sinks are also supported, but are out of scope for this documentation.
## Config providers
@@ -37,30 +85,9 @@ In a multi-node setup these need to be specified.
The simplest option to configure is by using the `config.json` file next to the server executable. For all possible options see the [config-full.json](https://github.com/Impostor/Impostor/blob/dev/src/Impostor.Server/config-full.json) file.
### Command line arguments
TODO
```
Server:PublicIp=127.0.0.1
Server:PublicPort=22023
Server:ListenIp=0.0.0.0
Server:ListenPort=22023
AntiCheat:Enabled=true
AntiCheat:BanIpFromGame=true
ServerRedirector:Enabled=false
ServerRedirector:Master=true
ServerRedirector:Locator:Redis=127.0.0.1.6379
ServerRedirector:Locator:UdpMasterEndpoint=127.0.0.1:32320
ServerRedirector:Nodes:0:Ip=127.0.0.1
ServerRedirector:Nodes:0:Port=22024
ServerRedirector:Nodes:1:Ip=127.0.0.1
ServerRedirector:Nodes:1:Port=22025
```
### Environment variables
TODO
If you're unable to edit the config.json file, you can instead set Environment variables to configure Impostor. The general pattern for each variable is `IMPOSTOR_SectionName__VariableName`. For example, to disable the anticheat, you can set the environment variable `IMPOSTOR_AntiCheat__Enabled=false`. Here are some more examples:
```
IMPOSTOR_Server__PublicIp=127.0.0.1
+38 -9
View File
@@ -1,28 +1,57 @@
# Troubleshooting
If you're reading this, something went wrong.
Don't worry though, as this is the most thorough guide to help you!
## `./Impostor.Server: line 1: ELF: not found` (plus other errors)
No idea where you got that system. But we clearly do **NOT** support it.
## `cannot execute binary file: Exec format error`
Please check that you have downloaded the right version of Impostor, as we mantain two CPU architectures (x64 and ARM).
Unless you are running Impostor on a SBC (Single-Board Computer), like the Raspberry Pi, you most likely want to use the x64 version.
Please check that you have downloaded the right version of Impostor, as we mantain two CPU architectures (x64 and ARM).
Unless you are running Impostor on a computer like the Raspberry Pi, you most likely want to use the x64 version.
## `./Impostor.Server: Permission denied`
This is an error related to Linux file permissions.
Some files do not hold their executable bit (the permission that allows them to run) during a download.
You can solve this by doing: `chmod +x Impostor.Server`
## `You are using an older version of the game`
You are using an older version of Impostor. The game does not really check who is outdated and blames it on the user.
Make sure you got the latest working version of Impostor (probably in AppVeyor, not Github).
## Disconnected with `Your client is too new, please update your Impostor to play`
## `You disconnected from the server. Reliable Packet 1 ...`
Please double-check that you have followed the [Server Configuration](Server-configuration.md) correctly.
**NOTE: Your public ip does not start with `127` nor `192`**
This happens when your game is too new for your copy of Impostor to support.
If you're using an Impostor release, check if there is a newer release available that supports your version. If this doesn't exist, you can try the [latest build from Appveyor](https://ci.appveyor.com/project/Impostor/Impostor/branch/master).
If even that build doesn't work, you can try looking through the Pull Requests to see if there is a pending pull request that aims to update it to the next version, but note these may be unstable and may contain malicious code, as they haven't been vetted by the Impostor development team yet.
## Disconnected with `Please update your game to play on this server`
This happens when you're using a version of Impostor that is too new for your game version. You can download an older version from [the GitHub release page](https://github.com/Impostor/Impostor/releases). We however don't support older versions of Impostor with new API's and bugfixes so we recommend to update your game instead.
## Disconnected with `You are using an older version of the game`
You are using a version of Impostor that is not designed for the version of the game you're playing. The game does not really check who is outdated and blames it on the user. Newer versions (v1.5.0+) do warn correctly and send the correct message.
Look at which version of the game you're playing, which you can see in the top left corner of the main menu, then download an Impostor version for that game. Every [release on the release page](https://github.com/Impostor/Impostor/releases) shows which version of the game it is compatible with. If your game version is newer than the latest release, check if the [latest build from AppVeyor works](https://ci.appveyor.com/project/Impostor/Impostor/branch/master).
## Disconnected with `You disconnected from the server. Reliable Packet 1 ...`
Please double-check that you have followed the [Server Configuration](Server-configuration.md) correctly.
**NOTE: Your public ip does not start with `10`, `127` or `192`**
Also check if the port Impostor (ListenPort) is listening on is correctly port-forwarded for UDP (or TCP/UDP).
## `Could not load file or assembly...`
Please check that you only have **working** plugins in the `plugins` folder.
Please check that you only have **working** plugins in the `plugins` folder.
This error can be caused by non-plugin files or plugins that are not working correctly.
## My question is not yet answered and I'm still having problems!
That's unfortunate. Join the [Impostor Discord](https://discord.gg/Mk3w6Tb), ask your question there and we'll try to help you out. Note that we're not always available, so it may take some time to get an answer. To make answering your question easier, please add the following details:
- Which version of Impostor are you using?
- Which version or Among Us are you using?
- Which operating system are you running Impostor on?
- If you have console logs, attaching these will also help.
+9 -12
View File
@@ -19,24 +19,24 @@ Impostor has support for plugins. This document will help you to setup a develop
- [Impostor versions](#impostor-versions)
- [9. Missing/invalid data or want more functions?](#9-missinginvalid-data-or-want-more-functions)
## 1. Install .NET Core SDK
## 1. Install .NET SDK
Download and install the latest .NET Core SDK.
Download and install the latest .NET SDK.
https://dotnet.microsoft.com/download
## 2. Create a C# project
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 5, which includes:
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 6, which includes:
- .NET Standard 2.0
- .NET Standard 2.1
- .NET Core 3.1
- .NET 5
- .NET 6
For more information about compatibility, see https://docs.microsoft.com/en-us/dotnet/standard/net-standard.
> At the moment of writing this document, I recommend you to use **.NET Standard 2.1** until .NET 5 is released officially. This should give you enough functionality. If not, upgrade to .NET Core 3.1.
> At the moment of writing this document, I recommend you to use **.NET Standard 2.1**. This should give you enough functionality. If not, upgrade to .NET 6.
When the project has been created, you should have `Class.cs` and `Project.csproj` files. Your `Project.csproj` should look something like this.
@@ -62,16 +62,13 @@ Make sure to get a prerelease if you are writing a plugin for a dev release of t
1. Right click your project.
2. Click `Manage NuGet Packages`.
3. Click `Browse`.
4. Next to the search bar, enable `Include prerelease`.
5. Search for `Impostor.Api`.
6. Click the `Impostor.Api` result and press install on the right side.
4. Search for `Impostor.Api`.
5. Click the `Impostor.Api` result and press install on the right side.
### Dotnet CLI
> Make sure to grab the latest (pre-)release version from NuGet [here](https://www.nuget.org/packages/Impostor.Api).
1. Open your project folder in command prompt / bash.
2. Run `dotnet add package Impostor.Api -v "1.2.0-ci.58"`.
2. Run `dotnet add package Impostor.Api`.
## 4. The plugin class
@@ -340,4 +337,4 @@ Create an issue:
- [Suggest a function](https://github.com/Impostor/Impostor/issues/new?template=3--api-suggestion.md)
- [Data is invalid](https://github.com/Impostor/Impostor/issues/new?template=4--api-invalid.md)
- [Data is unavailable](https://github.com/Impostor/Impostor/issues/new?template=5--api-missing.md)
- [Other](https://github.com/Impostor/Impostor/issues/new?template=6--api-other.md)
- [Other](https://github.com/Impostor/Impostor/issues/new?template=6--api-other.md)
+6
View File
@@ -144,6 +144,12 @@ dotnet_naming_style.end_in_async.word_separator =
# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}'
dotnet_diagnostic.RS2008.severity = none
# IDE0005: Remove unnecessary import
dotnet_diagnostic.IDE0005.severity = warning
# IDE0007: Use `var` instead of explicit type
dotnet_diagnostic.IDE0007.severity = warning
# IDE0035: Remove unreachable code
dotnet_diagnostic.IDE0035.severity = warning
+3 -1
View File
@@ -1,7 +1,9 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.4.0</VersionPrefix>
<TargetFramework>net6.0</TargetFramework>
<VersionPrefix>1.7.0</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup>
@@ -1,43 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Events.Announcements
{
/// <summary>
/// Event fired after client requests a announcement.
/// </summary>
public interface IAnnouncementRequestEvent : IEvent
{
public interface IResponse
{
/// <summary>
/// Gets or sets FreeWeekendState, currently unused by the client.
/// </summary>
public FreeWeekendState FreeWeekendState { get; set; }
/// <summary>
/// Gets or sets a value indicating whether announcement should be loaded from client's cache, can save some bytes.
/// </summary>
public bool UseCached { get; set; }
/// <summary>
/// Gets or sets announcement, should be null when <see cref="UseCached" /> is set to true.
/// </summary>
public Announcement? Announcement { get; set; }
}
/// <summary>
/// Gets client's last announcement id.
/// </summary>
public int Id { get; }
/// <summary>
/// Gets client's language.
/// </summary>
public Language Language { get; }
/// <summary>
/// Gets or sets plugin made response.
/// </summary>
public IResponse Response { get; set; }
}
}
@@ -2,10 +2,12 @@ using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Events.Player
{
public enum VoteType : sbyte
public enum VoteType : byte
{
ForceSkip = -2,
Skip = -1,
HasNotVoted = 255,
Missed = 254,
Skipped = 253,
Dead = 252,
Player = 0,
}
@@ -16,6 +16,13 @@ namespace Impostor.Api
return original[0];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ReadInt16(this ref ReadOnlySpan<byte> input)
{
var original = Advance<short>(ref input);
return BinaryPrimitives.ReadInt16LittleEndian(original);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReadInt32(this ref ReadOnlySpan<byte> input)
{
+2 -2
View File
@@ -27,8 +27,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Nullable" Version="1.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -1,14 +0,0 @@
namespace Impostor.Api.Innersloth
{
public readonly struct Announcement
{
public readonly int Id;
public readonly string Message;
public Announcement(int id, string message)
{
Id = id;
Message = message;
}
}
}
@@ -14,5 +14,11 @@
Brown = 9,
Cyan = 10,
Lime = 11,
Maroon = 12,
Rose = 13,
Banana = 14,
Gray = 15,
Tan = 16,
Coral = 17,
}
}
@@ -1,121 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum HatType : uint
{
NoHat = 0,
Astronaut = 1,
BaseballCap = 2,
BrainSlug = 3,
BushHat = 4,
CaptainsHat = 5,
DoubleTopHat = 6,
Flowerpot = 7,
Goggles = 8,
HardHat = 9,
Military = 10,
PaperHat = 11,
PartyHat = 12,
Police = 13,
Stethescope = 14,
TopHat = 15,
TowelWizard = 16,
Ushanka = 17,
Viking = 18,
WallCap = 19,
Snowman = 20,
Reindeer = 21,
Lights = 22,
Santa = 23,
Tree = 24,
Present = 25,
Candycanes = 26,
ElfHat = 27,
NewYears2018 = 28,
WhiteHat = 29,
Crown = 30,
Eyebrows = 31,
HaloHat = 32,
HeroCap = 33,
PipCap = 34,
PlungerHat = 35,
ScubaHat = 36,
StickminHat = 37,
StrawHat = 38,
TenGallonHat = 39,
ThirdEyeHat = 40,
ToiletPaperHat = 41,
Toppat = 42,
BlackFedora = 43,
SkiGoggles = 44,
Headphones = 45,
MaskHat = 46,
PaperMask = 47,
Security = 48,
StrapHat = 49,
Banana = 50,
Beanie = 51,
Bear = 52,
Cheese = 53,
Cherry = 54,
Egg = 55,
GreenFedora = 56,
Flamingo = 57,
FlowerPin = 58,
Helmet = 59,
Plant = 60,
BatEyes = 61,
BatWings = 62,
Horns = 63,
Mohawk = 64,
Pumpkin = 65,
ScaryBag = 66,
Witch = 67,
Wolf = 68,
Pirate = 69,
Plague = 70,
Machete = 71,
Fred = 72,
MinerCap = 73,
WinterHat = 74,
Archae = 75,
Antenna = 76,
Balloon = 77,
BirdNest = 78,
BlackBelt = 79,
Caution = 80,
Chef = 81,
CopHat = 82,
DoRag = 83,
DumSticker = 84,
Fez = 85,
GeneralHat = 86,
GreyThing = 87,
HunterCap = 88,
JungleHat = 89,
MiniCrewmate = 90,
NinjaMask = 91,
RamHorns = 92,
SnowCrewmate = 93,
GeoffHat = 94,
DaveCap = 95,
EllieRose = 96,
SvenHat = 97,
BurtHat = 98,
EllryMohawk = 99,
ThomasMonoc = 100,
Wizard = 101,
Fredrick = 102,
MrMacbeth = 103,
ToppatHenry = 104,
ToppatEllie = 105,
GeoffreyPlumb = 106,
AngryEyebrows = 107,
ChocolateIce = 108,
HeartPin = 109,
Ponytail = 110,
RubberGlove = 111,
UnicornHorn = 112,
Zipper = 113,
RightHandMan = 114,
}
}
@@ -1,18 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum PetType : uint
{
NoPet = 0,
Alien = 1,
Crewmate = 2,
Doggy = 3,
Stickmin = 4,
Hamster = 5,
Robot = 6,
Ufo = 7,
Ellie = 8,
Squig = 9,
Bedcrab = 10,
Glitch = 11,
}
}
@@ -0,0 +1,56 @@
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth.Customization
{
public class PlayerOutfit
{
public ColorType Color { get; internal set; } = (ColorType)(-1);
public string HatId { get; internal set; } = "missing";
public string PetId { get; internal set; } = "missing";
public string SkinId { get; internal set; } = "missing";
public string VisorId { get; internal set; } = "missing";
public string NamePlateId { get; internal set; } = "missing";
public string PlayerName { get; internal set; } = "missing";
public bool IsIncomplete
{
get
{
if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
{
return NamePlateId == "missing";
}
return true;
}
}
public void Serialize(IMessageWriter writer)
{
writer.Write(PlayerName);
writer.WritePacked((int)Color);
writer.Write(HatId);
writer.Write(PetId);
writer.Write(SkinId);
writer.Write(VisorId);
writer.Write(NamePlateId);
}
public void Deserialize(IMessageReader reader)
{
PlayerName = reader.ReadString();
Color = (ColorType)reader.ReadPackedInt32();
HatId = reader.ReadString();
PetId = reader.ReadString();
SkinId = reader.ReadString();
VisorId = reader.ReadString();
NamePlateId = reader.ReadString();
}
}
}
@@ -0,0 +1,8 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum PlayerOutfitType
{
Default,
Shapeshifted,
}
}
@@ -1,25 +0,0 @@
namespace Impostor.Api.Innersloth.Customization
{
public enum SkinType : uint
{
None = 0,
Astro = 1,
Capt = 2,
Mech = 3,
Military = 4,
Police = 5,
Science = 6,
SuitB = 7,
SuitW = 8,
Wall = 9,
Hazmat = 10,
Security = 11,
Tarmac = 12,
Miner = 13,
Winter = 14,
Archae = 15,
Prisoner = 16,
CCC = 17,
RightHandMan = 18,
}
}
@@ -1,12 +0,0 @@
using System;
namespace Impostor.Api.Innersloth
{
[Flags]
public enum FreeWeekendState : byte
{
NotFree,
FreeMIRA,
FreePolus,
}
}
+12 -5
View File
@@ -6,14 +6,21 @@ namespace Impostor.Api.Innersloth
public enum GameKeywords : uint
{
All = 0,
Other = 1,
Spanish = 2,
English = 256,
SpanishLA = 2,
Brazilian = 2048,
Portuguese = 16,
Korean = 4,
Russian = 8,
Portuguese = 16,
Dutch = 4096,
Filipino = 64,
French = 8192,
German = 16384,
Italian = 32768,
Japanese = 512,
SpanishEU = 1024,
Arabic = 32,
Filipone = 64,
Polish = 128,
English = 256,
Other = 1,
}
}
+34 -9
View File
@@ -9,7 +9,7 @@ namespace Impostor.Api.Innersloth
/// <summary>
/// The latest major version of the game client.
/// </summary>
public const int LatestVersion = 4;
public const int LatestVersion = 5;
/// <summary>
/// Gets or sets host's version of the game.
@@ -124,6 +124,11 @@ namespace Impostor.Api.Innersloth
/// </summary>
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
/// <summary>
/// Gets or sets role options.
/// </summary>
public RoleOptionsData RoleOptions { get; set; } = new RoleOptionsData();
/// <summary>
/// Gets or sets a value indicating whether the GameOptions are the default ones.
/// </summary>
@@ -166,24 +171,34 @@ namespace Impostor.Api.Innersloth
writer.Write((uint)VotingTime);
writer.Write((bool)IsDefaults);
if (version > 1)
if (version >= 2)
{
writer.Write((byte)EmergencyCooldown);
}
if (version > 2)
if (version >= 3)
{
writer.Write((bool)ConfirmImpostor);
writer.Write((bool)VisualTasks);
}
if (version > 3)
if (version >= 4)
{
writer.Write((bool)AnonymousVotes);
writer.Write((byte)TaskBarUpdate);
}
if (version > 4)
if (version >= 5)
{
RoleOptions.Serialize(writer);
}
if (version >= 6)
{
// Nothing was changed in V6
}
if (version > 6)
{
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
}
@@ -228,24 +243,34 @@ namespace Impostor.Api.Innersloth
IsDefaults = bytes.ReadBoolean();
if (Version > 1)
if (Version >= 2)
{
EmergencyCooldown = bytes.ReadByte();
}
if (Version > 2)
if (Version >= 3)
{
ConfirmImpostor = bytes.ReadBoolean();
VisualTasks = bytes.ReadBoolean();
}
if (Version > 3)
if (Version >= 4)
{
AnonymousVotes = bytes.ReadBoolean();
TaskBarUpdate = (TaskBarUpdate)bytes.ReadByte();
}
if (Version > 4)
if (Version >= 5)
{
RoleOptions = RoleOptionsData.Deserialize(bytes);
}
if (Version >= 6)
{
// Nothing was changed in V6
}
if (Version > 6)
{
throw new ImpostorException($"Unknown GameOptionsData version {Version}.");
}
+13 -3
View File
@@ -1,10 +1,20 @@
namespace Impostor.Api.Innersloth
{
public class GameVersion
public static class GameVersion
{
public static int GetVersion(int year, int month, int day, int rev = 0)
public static int GetVersion(int year, int month, int day, int revision = 0)
{
return (year * 25000) + (month * 1800) + (day * 50) + rev;
return (year * 25000) + (month * 1800) + (day * 50) + revision;
}
public static void ParseVersion(int version, out int year, out int month, out int day, out int revision)
{
year = version / 25000;
version %= 25000;
month = version / 1800;
version %= 1800;
day = version / 50;
revision = version % 50;
}
}
}
+9 -1
View File
@@ -3,9 +3,17 @@ namespace Impostor.Api.Innersloth
public enum Language
{
English,
Spanish,
SpanishLA,
Brazilian,
Portuguese,
Korean,
Russian,
Dutch,
Filipino,
French,
German,
Italian,
Japanese,
SpanishEU,
}
}
@@ -36,6 +36,7 @@ namespace Impostor.Api.Innersloth.Maps
{
new AirshipTask(AirshipTask.Ids.ElectricalFixWiring, TaskTypes.FixWiring, TaskCategories.CommonTask),
new AirshipTask(AirshipTask.Ids.MeetingRoomEnterIDCode, TaskTypes.EnterIdCode, TaskCategories.CommonTask),
new AirshipTask(AirshipTask.Ids.ElectricalCalibrateDistributor, TaskTypes.CalibrateDistributor, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.ElectricalResetBreakers, TaskTypes.ResetBreakers, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.VaultRoomDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.BrigDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
@@ -50,8 +51,9 @@ namespace Impostor.Api.Innersloth.Maps
new AirshipTask(AirshipTask.Ids.MainHallDevelopPhotos, TaskTypes.DevelopPhotos, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.CargoBayFuelEngines, TaskTypes.FuelEngines, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.SecurityRewindTapes, TaskTypes.RewindTapes, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.LoungeEmptyGarbage, TaskTypes.EmptyGarbage, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.ShowersEmptyGarbage, TaskTypes.EmptyGarbage, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.VaultRoomPolishRuby, TaskTypes.PolishRuby, TaskCategories.ShortTask),
new AirshipTask(AirshipTask.Ids.ElectricalCalibrateDistributor, TaskTypes.CalibrateDistributor, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.CockpitStabilizeSteering, TaskTypes.StabilizeSteering, TaskCategories.ShortTask),
new AirshipTask(AirshipTask.Ids.ArmoryDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
new AirshipTask(AirshipTask.Ids.CockpitDownloadData, TaskTypes.UploadData, TaskCategories.LongTask),
@@ -74,6 +76,7 @@ namespace Impostor.Api.Innersloth.Maps
new AirshipTask(AirshipTask.Ids.MainHallDecontaminate, TaskTypes.Decontaminate, TaskCategories.ShortTask),
new AirshipTask(AirshipTask.Ids.KitchenMakeBurger, TaskTypes.MakeBurger, TaskCategories.ShortTask),
new AirshipTask(AirshipTask.Ids.ShowersFixShower, TaskTypes.FixShower, TaskCategories.ShortTask),
new AirshipTask(AirshipTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
};
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
@@ -59,6 +59,7 @@ namespace Impostor.Api.Innersloth.Maps
new MiraTask(MiraTask.Ids.OfficeProcessData, TaskTypes.ProcessData, TaskCategories.ShortTask),
new MiraTask(MiraTask.Ids.LaunchpadRunDiagnostics, TaskTypes.RunDiagnostics, TaskCategories.LongTask),
new MiraTask(MiraTask.Ids.ReactorUnlockManifolds, TaskTypes.UnlockManifolds, TaskCategories.ShortTask),
new MiraTask(MiraTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
};
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
@@ -65,6 +65,7 @@ namespace Impostor.Api.Innersloth.Maps
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToNavigation, TaskTypes.DivertPower, TaskCategories.ShortTask),
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToLowerEngine, TaskTypes.DivertPower, TaskCategories.ShortTask),
new SkeldTask(SkeldTask.Ids.ElectricalDivertPowerToSecurity, TaskTypes.DivertPower, TaskCategories.ShortTask),
new SkeldTask(SkeldTask.Ids.CleanVent, TaskTypes.VentCleaning, TaskCategories.ShortTask),
};
Tasks = tasks.ToDictionary(x => x.Id, x => x).AsReadOnly();
@@ -15,44 +15,47 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
{
ElectricalFixWiring = 0,
MeetingRoomEnterIDCode = 1,
ElectricalResetBreakers = 2,
VaultRoomDownloadData = 3,
BrigDownloadData = 4,
CargoBayDownloadData = 5,
GapRoomDownloadData = 6,
RecordsDownloadData = 7,
CargoBayUnlockSafe = 8,
VentilationStartFans = 9,
MainHallEmptyGarbage = 10,
MedicalEmptyGarbage = 11,
KitchenEmptyGarbage = 12,
MainHallDevelopPhotos = 13,
CargoBayFuelEngines = 14,
SecurityRewindTapes = 15,
VaultRoomPolishRuby = 16,
ElectricalCalibrateDistributor = 17,
CockpitStabilizeSteering = 18,
ArmoryDownloadData = 19,
CockpitDownloadData = 20,
CommsDownloadData = 21,
MedicalDownloadData = 22,
ViewingDeckDownloadData = 23,
ElectricalDivertPowerToArmory = 24,
ElectricalDivertPowerToCockpit = 25,
ElectricalDivertPowerToGapRoom = 26,
ElectricalDivertPowerToMainHall = 27,
ElectricalDivertPowerToMeetingRoom = 28,
ElectricalDivertPowerToShowers = 29,
ElectricalDivertPowerToEngine = 30,
ShowersPickUpTowels = 31,
LoungeCleanToilet = 32,
VaultRoomDressMannequin = 33,
RecordsSortRecords = 34,
ArmoryPutAwayPistols = 35,
ArmoryPutAwayRifles = 36,
MainHallDecontaminate = 37,
KitchenMakeBurger = 38,
ShowersFixShower = 39,
ElectricalCalibrateDistributor = 2,
ElectricalResetBreakers = 3,
VaultRoomDownloadData = 4,
BrigDownloadData = 5,
CargoBayDownloadData = 6,
GapRoomDownloadData = 7,
RecordsDownloadData = 8,
CargoBayUnlockSafe = 9,
VentilationStartFans = 10,
MainHallEmptyGarbage = 11,
MedicalEmptyGarbage = 12,
KitchenEmptyGarbage = 13,
MainHallDevelopPhotos = 14,
CargoBayFuelEngines = 15,
SecurityRewindTapes = 16,
LoungeEmptyGarbage = 17,
ShowersEmptyGarbage = 18,
VaultRoomPolishRuby = 19,
CockpitStabilizeSteering = 20,
ArmoryDownloadData = 21,
CockpitDownloadData = 22,
CommsDownloadData = 23,
MedicalDownloadData = 24,
ViewingDeckDownloadData = 25,
ElectricalDivertPowerToArmory = 26,
ElectricalDivertPowerToCockpit = 27,
ElectricalDivertPowerToGapRoom = 28,
ElectricalDivertPowerToMainHall = 29,
ElectricalDivertPowerToMeetingRoom = 30,
ElectricalDivertPowerToShowers = 31,
ElectricalDivertPowerToEngine = 32,
ShowersPickUpTowels = 33,
LoungeCleanToilet = 34,
VaultRoomDressMannequin = 35,
RecordsSortRecords = 36,
ArmoryPutAwayPistols = 37,
ArmoryPutAwayRifles = 38,
MainHallDecontaminate = 39,
KitchenMakeBurger = 40,
ShowersFixShower = 41,
CleanVent = 42,
}
public Ids Id { get; }
@@ -39,6 +39,7 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
OfficeProcessData = 23,
LaunchpadRunDiagnostics = 24,
ReactorUnlockManifolds = 25,
CleanVent = 26,
}
public Ids Id { get; }
@@ -42,6 +42,7 @@ namespace Impostor.Api.Innersloth.Maps.Tasks
ElectricalDivertPowerToNavigation = 26,
ElectricalDivertPowerToLowerEngine = 27,
ElectricalDivertPowerToSecurity = 28,
CleanVent = 29,
}
public Ids Id { get; }
@@ -0,0 +1,50 @@
using System;
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth
{
public class PlatformSpecificData
{
public PlatformSpecificData(Platforms platform, string platformName, ulong? xboxPlatformId = null, ulong? psnPlatformId = null)
{
Platform = platform;
PlatformName = platformName;
XboxPlatformId = xboxPlatformId;
PsnPlatformId = psnPlatformId;
}
public PlatformSpecificData(IMessageReader reader)
{
Platform = (Platforms)reader.Tag;
PlatformName = reader.ReadString();
XboxPlatformId = Platform == Platforms.Xbox ? reader.ReadUInt64() : null;
PsnPlatformId = Platform == Platforms.Playstation ? reader.ReadUInt64() : null;
}
public Platforms Platform { get; }
public string PlatformName { get; }
public ulong? XboxPlatformId { get; }
public ulong? PsnPlatformId { get; }
public void Serialize(IMessageWriter writer)
{
writer.StartMessage((byte)Platform);
writer.Write(PlatformName);
switch (Platform)
{
case Platforms.Xbox:
writer.Write(XboxPlatformId ?? throw new NullReferenceException($"{nameof(XboxPlatformId)} shouldn't be null when {nameof(Platform)} is Xbox"));
break;
case Platforms.Playstation:
writer.Write(PsnPlatformId ?? throw new NullReferenceException($"{nameof(PsnPlatformId)} shouldn't be null when {nameof(Platform)} is Playstation"));
break;
}
writer.EndMessage();
}
}
}
+2
View File
@@ -11,5 +11,7 @@ namespace Impostor.Api.Innersloth
IPhone = 6,
Android = 7,
Switch = 8,
Xbox = 9,
Playstation = 10,
}
}
-50
View File
@@ -1,50 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace Impostor.Api.Innersloth
{
public class RegionInfo
{
public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers)
{
Name = name;
Ping = ping;
Servers = servers;
}
public string Name { get; }
public string Ping { get; }
public IReadOnlyList<ServerInfo> Servers { get; }
public static RegionInfo Deserialize(BinaryReader reader)
{
var unknown = reader.ReadInt32();
var name = reader.ReadString();
var ping = reader.ReadString();
var servers = new List<ServerInfo>();
var serverCount = reader.ReadInt32();
for (var i = 0; i < serverCount; i++)
{
servers.Add(ServerInfo.Deserialize(reader));
}
return new RegionInfo(name, ping, servers);
}
public void Serialize(BinaryWriter writer)
{
writer.Write(0);
writer.Write(Name);
writer.Write(Ping);
writer.Write(Servers.Count);
foreach (var server in Servers)
{
server.Serialize(writer);
}
}
}
}
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.IO;
using Impostor.Api.Net.Messages;
namespace Impostor.Api.Innersloth
{
public class RoleOptionsData
{
public bool ShapeshifterLeaveSkin { get; set; }
public byte ShapeshifterCooldown { get; set; } = 10;
public byte ShapeshifterDuration { get; set; } = 30;
public byte ScientistCooldown { get; set; } = 15;
public byte ScientistBatteryCharge { get; set; } = 5;
public byte GuardianAngelCooldown { get; set; } = 60;
public bool ImpostorsCanSeeProtect { get; set; }
public byte ProtectionDurationSeconds { get; set; } = 10;
public byte EngineerCooldown { get; set; } = 30;
public byte EngineerInVentMaxTime { get; set; } = 15;
public Dictionary<RoleTypes, RoleRate> RoleRates { get; } = new Dictionary<RoleTypes, RoleRate>();
public static RoleOptionsData Deserialize(ReadOnlySpan<byte> span)
{
var roleOptionsData = new RoleOptionsData();
var num = span.ReadInt32();
for (var i = 0; i < num; i++)
{
var key = (RoleTypes)span.ReadInt16();
var roleRate = new RoleRate(span.ReadByte(), span.ReadByte());
roleOptionsData.RoleRates[key] = roleRate;
}
roleOptionsData.ShapeshifterLeaveSkin = span.ReadBoolean();
roleOptionsData.ShapeshifterCooldown = span.ReadByte();
roleOptionsData.ShapeshifterDuration = span.ReadByte();
roleOptionsData.ScientistCooldown = span.ReadByte();
roleOptionsData.GuardianAngelCooldown = span.ReadByte();
roleOptionsData.EngineerCooldown = span.ReadByte();
roleOptionsData.EngineerInVentMaxTime = span.ReadByte();
roleOptionsData.ScientistBatteryCharge = span.ReadByte();
roleOptionsData.ProtectionDurationSeconds = span.ReadByte();
roleOptionsData.ImpostorsCanSeeProtect = span.ReadBoolean();
return roleOptionsData;
}
public static RoleOptionsData Deserialize(IMessageReader reader)
{
var roleOptionsData = new RoleOptionsData();
var num = reader.ReadPackedInt32();
for (var i = 0; i < num; i++)
{
var key = (RoleTypes)reader.ReadInt16();
var roleRate = new RoleRate(reader.ReadByte(), reader.ReadByte());
roleOptionsData.RoleRates[key] = roleRate;
}
roleOptionsData.ShapeshifterLeaveSkin = reader.ReadBoolean();
roleOptionsData.ShapeshifterCooldown = reader.ReadByte();
roleOptionsData.ShapeshifterDuration = reader.ReadByte();
roleOptionsData.ScientistCooldown = reader.ReadByte();
roleOptionsData.GuardianAngelCooldown = reader.ReadByte();
roleOptionsData.EngineerCooldown = reader.ReadByte();
roleOptionsData.EngineerInVentMaxTime = reader.ReadByte();
roleOptionsData.ScientistBatteryCharge = reader.ReadByte();
roleOptionsData.ProtectionDurationSeconds = reader.ReadByte();
roleOptionsData.ImpostorsCanSeeProtect = reader.ReadBoolean();
return roleOptionsData;
}
public void Serialize(IMessageWriter writer)
{
writer.WritePacked(RoleRates.Count);
foreach (var roleRate in RoleRates)
{
writer.Write((ushort)roleRate.Key);
writer.Write((byte)roleRate.Value.MaxCount);
writer.Write((byte)roleRate.Value.Chance);
}
writer.Write(ShapeshifterLeaveSkin);
writer.Write(ShapeshifterCooldown);
writer.Write(ShapeshifterDuration);
writer.Write(ScientistCooldown);
writer.Write(GuardianAngelCooldown);
writer.Write(EngineerCooldown);
writer.Write(EngineerInVentMaxTime);
writer.Write(ScientistBatteryCharge);
writer.Write(ProtectionDurationSeconds);
writer.Write(ImpostorsCanSeeProtect);
}
public void Serialize(BinaryWriter writer)
{
writer.Write(RoleRates.Count);
foreach (var roleRate in RoleRates)
{
writer.Write((ushort)roleRate.Key);
writer.Write((byte)roleRate.Value.MaxCount);
writer.Write((byte)roleRate.Value.Chance);
}
writer.Write(ShapeshifterLeaveSkin);
writer.Write(ShapeshifterCooldown);
writer.Write(ShapeshifterDuration);
writer.Write(ScientistCooldown);
writer.Write(GuardianAngelCooldown);
writer.Write(EngineerCooldown);
writer.Write(EngineerInVentMaxTime);
writer.Write(ScientistBatteryCharge);
writer.Write(ProtectionDurationSeconds);
writer.Write(ImpostorsCanSeeProtect);
}
public readonly struct RoleRate
{
public readonly int MaxCount;
public readonly int Chance;
public RoleRate(int maxCount, int chance)
{
MaxCount = maxCount;
Chance = chance;
}
}
}
}
+12
View File
@@ -0,0 +1,12 @@
namespace Impostor.Api.Innersloth
{
public enum RoleTypes : ushort
{
Crewmate,
Impostor,
Scientist,
Engineer,
GuardianAngel,
Shapeshifter,
}
}
-39
View File
@@ -1,39 +0,0 @@
using System.IO;
using System.Net;
namespace Impostor.Api.Innersloth
{
public class ServerInfo
{
public ServerInfo(string name, string ip, ushort port)
{
Name = name;
Ip = ip;
Port = port;
}
public string Name { get; }
public string Ip { get; }
public ushort Port { get; }
public static ServerInfo Deserialize(BinaryReader reader)
{
var name = reader.ReadString();
var ip = new IPAddress(reader.ReadBytes(4)).ToString();
var port = reader.ReadUInt16();
var unknown = reader.ReadInt32();
return new ServerInfo(name, ip, port);
}
public void Serialize(BinaryWriter writer)
{
writer.Write(Name);
writer.Write(IPAddress.Parse(Ip).GetAddressBytes());
writer.Write(Port);
writer.Write(0);
}
}
}
+1
View File
@@ -62,5 +62,6 @@ namespace Impostor.Api.Innersloth
GetBiggolSword = 57,
PutAwayRifles = 58,
StopCharles = 59,
VentCleaning = 60,
}
}
+13 -1
View File
@@ -4,7 +4,19 @@
{
public static bool IsCharAllowed(char i)
{
return i == ' ' || (i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9') || (i >= 'À' && i <= 'ÿ') || (i >= 'Ѐ' && i <= 'џ') || (i >= 'ㄱ' && i <= 'ㆎ') || (i >= '가' && i <= '힣');
return i == ' ' ||
(i >= 'A' && i <= 'Z') ||
(i >= 'a' && i <= 'z') ||
(i >= '0' && i <= '9') ||
// U+00C0 to U+00FF (Latin-1 Supplement)
(i >= 'À' && i <= 'ÿ') ||
// U+0400 to U+045F (Cyrillic)
(i >= 'Ѐ' && i <= 'џ') ||
// U+2C61 to U+D7A3 (CJK)
(i >= 'ⱡ' && i <= '힣');
}
}
}
+21 -1
View File
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
using Impostor.Api.Net.Messages;
@@ -27,6 +27,16 @@ namespace Impostor.Api.Net
/// </remarks>
string Name { get; }
/// <summary>
/// Gets the language.
/// </summary>
Language Language { get; }
/// <summary>
/// Gets the quick chat mode.
/// </summary>
QuickChatModes ChatMode { get; }
/// <summary>
/// Gets the connection of the client.
/// </summary>
@@ -55,6 +65,16 @@ namespace Impostor.Api.Net
/// </summary>
IClientPlayer? Player { get; }
/// <summary>
/// Gets the version of the game the client is using.
/// </summary>
int GameVersion { get; }
/// <summary>
/// Gets platform specific data of the <see cref="IClient" />.
/// </summary>
PlatformSpecificData PlatformSpecificData { get; }
ValueTask<bool> ReportCheatAsync(CheatContext context, string message);
ValueTask HandleMessageAsync(IMessageReader message, MessageType messageType);
@@ -49,25 +49,25 @@ namespace Impostor.Api.Net.Inner.Objects
/// Sets the hat of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="hatType">An hat for the player.</param>
/// <param name="hatId">An hat for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetHatAsync(HatType hatType);
ValueTask SetHatAsync(string hatId);
/// <summary>
/// Sets the pet of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="petType">A pet for the player.</param>
/// <param name="petId">A pet for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetPetAsync(PetType petType);
ValueTask SetPetAsync(string petId);
/// <summary>
/// Sets the skin of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="skinType">A skin for the player.</param>
/// <param name="skinId">A skin for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetSkinAsync(SkinType skinType);
ValueTask SetSkinAsync(string skinId);
/// <summary>
/// Send a chat message as the current <see cref="IInnerPlayerControl" />.
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization;
@@ -12,26 +12,6 @@ namespace Impostor.Api.Net.Inner.Objects
/// </summary>
string PlayerName { get; }
/// <summary>
/// Gets the color of the player.
/// </summary>
ColorType Color { get; }
/// <summary>
/// Gets the hat of the player.
/// </summary>
HatType Hat { get; }
/// <summary>
/// Gets the pet of the player.
/// </summary>
PetType Pet { get; }
/// <summary>
/// Gets the skin of the player.
/// </summary>
SkinType Skin { get; }
/// <summary>
/// Gets a value indicating whether the player is an impostor.
/// </summary>
@@ -42,6 +22,12 @@ namespace Impostor.Api.Net.Inner.Objects
/// </summary>
bool IsDead { get; }
Dictionary<PlayerOutfitType, PlayerOutfit> Outfits { get; }
PlayerOutfitType CurrentOutfitType { get; set; }
PlayerOutfit CurrentOutfit { get; }
/// <summary>
/// Gets the reason why the player is dead in the current game.
/// </summary>
@@ -50,5 +36,7 @@ namespace Impostor.Api.Net.Inner.Objects
IEnumerable<ITaskInfo> Tasks { get; }
DateTimeOffset LastMurder { get; }
uint PlayerLevel { get; }
}
}
@@ -7,7 +7,7 @@ namespace Impostor.Api.Net.Inner.Objects
{
uint Id { get; }
ITask Task { get; }
ITask? Task { get; }
bool Complete { get; }
+14 -3
View File
@@ -11,15 +11,12 @@
SetName = 6,
CheckColor = 7,
SetColor = 8,
SetHat = 9,
SetSkin = 10,
ReportDeadBody = 11,
MurderPlayer = 12,
SendChat = 13,
StartMeeting = 14,
SetScanner = 15,
SendChatNote = 16,
SetPet = 17,
SetStartCounter = 18,
EnterVent = 19,
ExitVent = 20,
@@ -34,5 +31,19 @@
SetTasks = 29,
ClimbLadder = 31,
UsePlatform = 32,
SendQuickChat = 33,
BootFromVent = 34,
UpdateSystem = 35,
SetLevel = 38,
SetHat = 39,
SetSkin = 40,
SetPet = 41,
SetVisor = 42,
SetNamePlate = 43,
SetRole = 44,
ProtectPlayer = 45,
Shapeshift = 46,
CheckMurder = 47,
CheckProtect = 48,
}
}
@@ -1,15 +0,0 @@
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message00UseCache
{
public static void Serialize(IMessageWriter writer)
{
writer.StartMessage(AnnouncementsMessageFlags.UseCache);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader)
{
}
}
}
@@ -1,19 +0,0 @@
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message01Update
{
public static void Serialize(IMessageWriter writer, int id, string message)
{
writer.StartMessage(AnnouncementsMessageFlags.SetUpdate);
writer.WritePacked(id);
writer.Write(message);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader, out int id, out string message)
{
id = reader.ReadPackedInt32();
message = reader.ReadString();
}
}
}
@@ -1,19 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Announcements
{
public static class Message02SetFreeWeekend
{
public static void Serialize(IMessageWriter writer, FreeWeekendState state)
{
writer.StartMessage(AnnouncementsMessageFlags.SetFreeWeekend);
writer.Write((byte)state);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader, out FreeWeekendState state)
{
state = (FreeWeekendState)reader.ReadByte();
}
}
}
@@ -1,15 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Announcements
{
public class MessageHello
{
public static void Deserialize(IMessageReader reader, out int announcementVersion, out int id, out Language language)
{
reader.ReadByte(); // SendOption header, probably added by accident
announcementVersion = reader.ReadPackedInt32();
id = reader.ReadPackedInt32();
language = (Language)reader.ReadPackedInt32();
}
}
}
@@ -1,9 +0,0 @@
namespace Impostor.Api.Net.Messages
{
public class AnnouncementsMessageFlags
{
public const byte UseCache = 0;
public const byte SetUpdate = 1;
public const byte SetFreeWeekend = 2;
}
}
@@ -1,15 +0,0 @@
namespace Impostor.Api.Net.Messages.Auth
{
public static class Message01Complete
{
public static void Serialize(IMessageWriter writer, uint nonce)
{
writer.WritePacked(nonce);
}
public static void Deserialize(IMessageReader reader, out uint nonce)
{
nonce = reader.ReadUInt32();
}
}
}
@@ -1,14 +0,0 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Auth
{
public static class MessageHandshake
{
public static void Deserialize(IMessageReader reader, out int clientVersion, out Platforms platform, out string clientId)
{
clientVersion = reader.ReadInt32();
platform = (Platforms)reader.ReadByte();
clientId = reader.ReadString();
}
}
}
@@ -1,12 +1,56 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.C2S
{
public static class HandshakeC2S
{
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out uint lastNonceReceived)
public static void Deserialize(IMessageReader reader, out int clientVersion, out string name, out Language language, out QuickChatModes chatMode, out PlatformSpecificData? platformSpecificData)
{
clientVersion = reader.ReadInt32();
name = reader.ReadString();
lastNonceReceived = reader.ReadUInt32();
if (clientVersion >= Version.V1)
{
reader.ReadUInt32(); // lastNonceReceived, always 0 since 2021.11.9
}
if (clientVersion >= Version.V2)
{
language = (Language)reader.ReadUInt32();
chatMode = (QuickChatModes)reader.ReadByte();
}
else
{
language = Language.English;
chatMode = QuickChatModes.FreeChatOrQuickChat;
}
if (clientVersion >= Version.V3)
{
using var platformReader = reader.ReadMessage();
platformSpecificData = new PlatformSpecificData(platformReader);
reader.ReadInt32(); // crossplayFlags, not used yet
}
else
{
platformSpecificData = null;
}
if (clientVersion >= Version.V4)
{
reader.ReadByte(); // purpose unknown, seems hardcoded to 0
}
}
private static class Version
{
public static readonly int V1 = GameVersion.GetVersion(2021, 4, 25);
public static readonly int V2 = GameVersion.GetVersion(2021, 6, 30);
public static readonly int V3 = GameVersion.GetVersion(2021, 11, 9);
public static readonly int V4 = GameVersion.GetVersion(2021, 12, 14);
}
}
}
@@ -8,6 +8,7 @@ namespace Impostor.Api.Net.Messages.C2S
{
writer.StartMessage(MessageFlags.HostGame);
gameOptionsData.Serialize(writer);
writer.Write(int.MaxValue); // crossplayFlags
writer.EndMessage();
}
@@ -15,12 +16,11 @@ namespace Impostor.Api.Net.Messages.C2S
/// Deserialize a packet.
/// </summary>
/// <param name="reader"><see cref="IMessageReader" /> with <see cref="IMessageReader.Tag" /> 0.</param>
/// <param name="chatMode">The chat type selected in the client of the player.</param>
/// <returns>Deserialized <see cref="GameOptionsData" />.</returns>
public static GameOptionsData Deserialize(IMessageReader reader, out QuickChatModes chatMode)
public static GameOptionsData Deserialize(IMessageReader reader)
{
var gameOptionsData = GameOptionsData.DeserializeCreate(reader);
chatMode = (QuickChatModes)reader.ReadByte();
reader.ReadInt32(); // crossplayFlags, not used yet
return gameOptionsData;
}
@@ -13,6 +13,7 @@ namespace Impostor.Api.Net.Messages.C2S
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
{
gameCode = reader.ReadInt32();
reader.ReadBoolean(); // no crossplay
}
}
}
@@ -12,9 +12,15 @@ namespace Impostor.Api.Net.Messages.C2S
public static void Deserialize(IMessageReader reader, out GameOptionsData options, out QuickChatModes chatMode)
{
reader.ReadPackedInt32(); // Hardcoded 0.
var version = reader.ReadPackedInt32();
if (version != 2)
{
throw new NotSupportedException($"Version {version} of {nameof(Message16GetGameListC2S)} is not supported");
}
options = GameOptionsData.DeserializeCreate(reader);
chatMode = (QuickChatModes)reader.ReadByte();
reader.ReadInt32(); // crossplayFlags, not used yet
}
}
}
@@ -0,0 +1,17 @@
using System;
namespace Impostor.Api.Net.Messages.C2S
{
public class Message21SetActivePodType
{
public static void Serialize(IMessageWriter writer)
{
throw new NotImplementedException();
}
public static void Deserialize(IMessageReader reader, out string podType)
{
podType = reader.ReadString();
}
}
}
@@ -0,0 +1,18 @@
using System;
using Impostor.Api.Games;
namespace Impostor.Api.Net.Messages.C2S
{
public class Message22QueryPlatformIdsC2S
{
public static void Serialize(IMessageWriter writer)
{
throw new NotImplementedException();
}
public static void Deserialize(IMessageReader reader, out GameCode gameCode)
{
gameCode = reader.ReadInt32();
}
}
}
@@ -48,6 +48,10 @@ namespace Impostor.Api.Net.Messages
int ReadInt32();
ulong ReadUInt64();
long ReadInt64();
float ReadSingle();
string ReadString(int length);
@@ -68,6 +68,18 @@ namespace Impostor.Api.Net.Messages
/// <param name="value">Value to write.</param>
void Write(int value);
/// <summary>
/// Writes an ulong to the message.
/// </summary>
/// <param name="value">Value to write.</param>
void Write(ulong value);
/// <summary>
/// Writes an ulong to the message.
/// </summary>
/// <param name="value">Value to write.</param>
void Write(long value);
/// <summary>
/// Writes a float to the message.
/// </summary>
@@ -16,7 +16,13 @@
public const byte WaitForHost = 12;
public const byte Redirect = 13;
public const byte ReselectServer = 14;
public const byte GetGameList = 9;
public const byte GetGameListV2 = 16;
public const byte ReportPlayer = 17;
public const byte QuickMatch = 18;
public const byte QuickMatchHost = 19;
public const byte SetGameSession = 20;
public const byte SetActivePodType = 21;
public const byte QueryPlatformIds = 22;
public const byte QueryLobbyInfo = 23;
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc09SetHat
{
public static void Serialize(IMessageWriter writer, HatType hat)
{
writer.WritePacked((uint)hat);
}
public static void Deserialize(IMessageReader reader, out HatType hat)
{
hat = (HatType)reader.ReadPackedUInt32();
}
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc10SetSkin
{
public static void Serialize(IMessageWriter writer, SkinType skin)
{
writer.WritePacked((uint)skin);
}
public static void Deserialize(IMessageReader reader, out SkinType skin)
{
skin = (SkinType)reader.ReadPackedUInt32();
}
}
}
@@ -1,17 +0,0 @@
using Impostor.Api.Innersloth.Customization;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc17SetPet
{
public static void Serialize(IMessageWriter writer, PetType pet)
{
writer.WritePacked((uint)pet);
}
public static void Deserialize(IMessageReader reader, out PetType pet)
{
pet = (PetType)reader.ReadPackedUInt32();
}
}
}
@@ -1,5 +1,3 @@
using System;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc23VotingComplete
@@ -11,9 +9,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
writer.Write(tie);
}
public static void Deserialize(IMessageReader reader, out ReadOnlyMemory<byte> states, out byte playerId, out bool tie)
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie)
{
states = reader.ReadBytesAndSize();
var length = reader.ReadPackedInt32();
states = new IMessageReader[length];
for (var i = 0; i < length; i++)
{
states[i] = reader.ReadMessage();
}
playerId = reader.ReadByte();
tie = reader.ReadBoolean();
}
@@ -8,10 +8,10 @@ namespace Impostor.Api.Net.Messages.Rpcs
writer.Write(suspectPlayerId);
}
public static void Deserialize(IMessageReader reader, out byte playerId, out sbyte suspectPlayerId)
public static void Deserialize(IMessageReader reader, out byte playerId, out byte suspectPlayerId)
{
playerId = reader.ReadByte();
suspectPlayerId = reader.ReadSByte();
suspectPlayerId = reader.ReadByte();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc34BootFromVent
{
public static void Serialize(IMessageWriter writer, int ventId)
{
writer.WritePacked(ventId);
}
public static void Deserialize(IMessageReader reader, out int ventId)
{
ventId = reader.ReadPackedInt32();
}
}
}
@@ -0,0 +1,27 @@
using Impostor.Api.Games;
using Impostor.Api.Innersloth;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc35UpdateSystem
{
public static void Serialize(IMessageWriter writer, SystemTypes systemType, IInnerPlayerControl playerControl, ushort sequenceId, byte state, byte ventId)
{
writer.Write((byte)systemType);
writer.Write(playerControl.NetId);
writer.Write(sequenceId);
writer.Write(state);
writer.Write(ventId);
}
public static void Deserialize(IMessageReader reader, IGame game, out SystemTypes systemType, out IInnerPlayerControl? playerControl, out ushort sequenceId, out byte state, out byte ventId)
{
systemType = (SystemTypes)reader.ReadByte();
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
sequenceId = reader.ReadUInt16();
state = reader.ReadByte();
ventId = reader.ReadByte();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc39SetHat
{
public static void Serialize(IMessageWriter writer, string hat)
{
writer.Write(hat);
}
public static void Deserialize(IMessageReader reader, out string hat)
{
hat = reader.ReadString();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc40SetSkin
{
public static void Serialize(IMessageWriter writer, string skin)
{
writer.Write(skin);
}
public static void Deserialize(IMessageReader reader, out string skin)
{
skin = reader.ReadString();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc41SetPet
{
public static void Serialize(IMessageWriter writer, string pet)
{
writer.Write(pet);
}
public static void Deserialize(IMessageReader reader, out string pet)
{
pet = reader.ReadString();
}
}
}
@@ -0,0 +1,17 @@
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc44SetRole
{
public static void Serialize(IMessageWriter writer, RoleTypes role)
{
writer.Write((ushort)role);
}
public static void Deserialize(IMessageReader reader, out RoleTypes role)
{
role = (RoleTypes)reader.ReadUInt16();
}
}
}
@@ -0,0 +1,21 @@
using Impostor.Api.Games;
using Impostor.Api.Innersloth.Customization;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc45ProtectPlayer
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, ColorType color)
{
writer.Write(playerControl.NetId);
writer.Write((byte)color);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out ColorType color)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
color = (ColorType)reader.ReadByte();
}
}
}
@@ -0,0 +1,20 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc46Shapeshift
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl, bool shouldAnimate)
{
writer.Write(playerControl.NetId);
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl, out bool shouldAnimate)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,18 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc47CheckMurder
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
{
writer.Write(playerControl.NetId);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
}
}
}
@@ -0,0 +1,18 @@
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc48CheckProtect
{
public static void Serialize(IMessageWriter writer, IInnerPlayerControl playerControl)
{
writer.Write(playerControl.NetId);
}
public static void Deserialize(IMessageReader reader, IGame game, out IInnerPlayerControl? playerControl)
{
playerControl = reader.ReadNetObject<IInnerPlayerControl>(game);
}
}
}
@@ -1,11 +1,11 @@
using System;
using System;
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.S2C
{
public class Message01JoinGameS2C
{
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId)
public static void SerializeJoin(IMessageWriter writer, bool clear, int gameCode, IClientPlayer player, int hostId)
{
if (clear)
{
@@ -14,11 +14,19 @@ namespace Impostor.Api.Net.Messages.S2C
writer.StartMessage(MessageFlags.JoinGame);
writer.Write(gameCode);
writer.Write(playerId);
writer.Write(player.Client.Id);
writer.Write(hostId);
writer.Write(player.Client.Name);
player.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(player.Character?.PlayerInfo.PlayerLevel ?? 1);
// ProductUserId and FriendCode are not yet known, so set them to an empty string
writer.Write(string.Empty);
writer.Write(string.Empty);
writer.EndMessage();
}
[Obsolete("JoinGame errors are no longer used by 2021.11.9 and up, disconnect clients instead.")]
public static void SerializeError(IMessageWriter writer, bool clear, DisconnectReason reason, string? message = null)
{
if (clear)
@@ -1,10 +1,10 @@
using System;
using System;
namespace Impostor.Api.Net.Messages.S2C
{
public static class Message07JoinedGameS2C
{
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, int[] otherPlayerIds)
public static void Serialize(IMessageWriter writer, bool clear, int gameCode, int playerId, int hostId, IClientPlayer[] otherPlayers, bool post20220202 = true)
{
if (clear)
{
@@ -15,11 +15,21 @@ namespace Impostor.Api.Net.Messages.S2C
writer.Write(gameCode);
writer.Write(playerId);
writer.Write(hostId);
writer.WritePacked(otherPlayerIds.Length);
writer.WritePacked(otherPlayers.Length);
foreach (var id in otherPlayerIds)
foreach (var ply in otherPlayers)
{
writer.WritePacked(id);
writer.WritePacked(ply.Client.Id);
writer.Write(ply.Client.Name);
ply.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(ply.Character?.PlayerInfo.PlayerLevel ?? 1);
if (post20220202)
{
// ProductUserId and FriendCode are not yet known, so set them to an empty string
writer.Write(string.Empty);
writer.Write(string.Empty);
}
}
writer.EndMessage();
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Impostor.Api.Games;
@@ -25,6 +25,9 @@ namespace Impostor.Api.Net.Messages.S2C
writer.Write((byte)game.Options.Map);
writer.Write((byte)game.Options.NumImpostors);
writer.Write((byte)game.Options.MaxPlayers);
var platform = game.Host?.Client.PlatformSpecificData;
writer.Write((byte)(platform?.Platform ?? 0));
writer.Write(platform?.PlatformName ?? string.Empty);
writer.EndMessage();
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Impostor.Api.Games;
using Impostor.Api.Innersloth;
namespace Impostor.Api.Net.Messages.S2C
{
public class Message22QueryPlatformIdsS2C
{
public static void Serialize(IMessageWriter writer, GameCode gameCode, IEnumerable<PlatformSpecificData> playerSpecificDatas)
{
writer.StartMessage(MessageFlags.QueryPlatformIds);
writer.Write(gameCode);
foreach (var playerSpecificData in playerSpecificDatas)
{
playerSpecificData.Serialize(writer);
}
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader)
{
throw new NotImplementedException();
}
}
}
@@ -39,7 +39,7 @@ namespace Impostor.Api.Net.Messages.S2C
if (hasReason)
{
var inner = reader.ReadMessage();
using var inner = reader.ReadMessage();
reason = (DisconnectReason)inner.ReadByte();
message = reason == DisconnectReason.Custom ? inner.ReadString() : null;
}
@@ -9,7 +9,7 @@ namespace Impostor.Benchmarks.Data
{
public class MessageReader_Bytes_Pooled
{
private static ConcurrentQueue<MessageReader_Bytes_Pooled> _readers;
private static readonly ConcurrentQueue<MessageReader_Bytes_Pooled> _readers;
static MessageReader_Bytes_Pooled()
{
@@ -11,7 +11,7 @@ namespace Impostor.Benchmarks.Data
{
public MessageType SendOption { get; private set; }
private Stack<int> messageStarts = new Stack<int>();
private readonly Stack<int> _messageStarts = new Stack<int>();
public MessageWriter(byte[] buffer)
{
@@ -78,7 +78,7 @@ namespace Impostor.Benchmarks.Data
///
public void StartMessage(byte typeFlag)
{
messageStarts.Push(this.Position);
_messageStarts.Push(this.Position);
this.Position += 2; // Skip for size
this.Write(typeFlag);
}
@@ -86,7 +86,7 @@ namespace Impostor.Benchmarks.Data
///
public void EndMessage()
{
var lastMessageStart = messageStarts.Pop();
var lastMessageStart = _messageStarts.Pop();
var length = (ushort)(this.Position - lastMessageStart - 3); // Minus length and type byte
this.Buffer[lastMessageStart] = (byte)length;
this.Buffer[lastMessageStart + 1] = (byte)(length >> 8);
@@ -95,13 +95,13 @@ namespace Impostor.Benchmarks.Data
///
public void CancelMessage()
{
this.Position = this.messageStarts.Pop();
this.Position = this._messageStarts.Pop();
this.Length = this.Position;
}
public void Clear(MessageType sendOption)
{
this.messageStarts.Clear();
this._messageStarts.Clear();
this.SendOption = sendOption;
this.Buffer[0] = (byte)sendOption;
switch (sendOption)
@@ -293,7 +293,7 @@ namespace Impostor.Benchmarks.Data
this.Write(msg.Buffer, offset, msg.Length - offset);
}
public unsafe static bool IsLittleEndian()
public static unsafe bool IsLittleEndian()
{
byte b;
unsafe
@@ -2,12 +2,11 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
</ItemGroup>
<ItemGroup>
@@ -2,7 +2,6 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
@@ -10,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
</ItemGroup>
</Project>
@@ -1,9 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
<ProjectReference Include="..\Impostor.Hazel\Hazel\Hazel.csproj" />
@@ -1,8 +0,0 @@
<Project>
<PropertyGroup>
<AssemblyTitle>Impostor</AssemblyTitle>
<Product>Impostor</Product>
<Copyright>Copyright © AeonLucid 2020</Copyright>
<Version>1.0.0</Version>
</PropertyGroup>
</Project>
@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Impostor.Cli</AssemblyName>
<TargetFramework>net5.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;linux-arm64;osx-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Impostor.Patcher.Shared\Impostor.Patcher.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20478.1" />
</ItemGroup>
</Project>
@@ -1,88 +0,0 @@
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Threading.Tasks;
using Impostor.Patcher.Shared;
using Impostor.Patcher.Shared.Events;
namespace Impostor.Patcher.Cli
{
internal static class Program
{
private static readonly AmongUsModifier Modifier = new AmongUsModifier();
internal static Task<int> Main(string[] args)
{
var rootCommand = new RootCommand
{
new Option<string>(
"--address",
"IP Address of the server, will prompt if not specified"
),
new Option<string>(
"--name",
() => AmongUsModifier.DefaultRegionName,
"Name for server region"
),
};
rootCommand.Handler = CommandHandler.Create<string, string>((address, name) =>
{
Modifier.RegionName = name;
Modifier.Error += ModifierOnError;
Modifier.Saved += ModifierOnSaved;
Console.WriteLine("Welcome to Impostor");
if (Modifier.TryLoadRegionInfo(out var regionInfo))
{
Console.WriteLine($"Currently selected region: {regionInfo.Name} ({regionInfo.Ping}, {regionInfo.Servers.Count} server(s))");
}
if (address != null)
{
return Modifier.SaveIpAsync(address);
}
return PromptAsync();
});
return rootCommand.InvokeAsync(args);
}
private static void ModifierOnSaved(object sender, SavedEventArgs e)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("The IP Address was saved, please (re)start Among Us.");
Console.ResetColor();
}
private static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor();
}
private static void ModifierOnError(object sender, ErrorEventArgs e)
{
WriteError(e.Message);
}
private static async Task PromptAsync()
{
Console.WriteLine("Please enter in the IP Address of the server you would like to use for Among Us");
Console.WriteLine("If you want to stop playing on the server, simply select another region");
while (true)
{
Console.Write("> ");
if (await Modifier.SaveIpAsync(Console.ReadLine()))
{
return;
}
}
}
}
}
@@ -1,249 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Gameloop.Vdf;
using Gameloop.Vdf.Linq;
using Impostor.Patcher.Shared.Events;
using Impostor.Patcher.Shared.Innersloth;
using ErrorEventArgs = Impostor.Patcher.Shared.Events.ErrorEventArgs;
namespace Impostor.Patcher.Shared
{
public class AmongUsModifier
{
private const uint AppId = 945360;
public const string DefaultRegionName = "Impostor";
public const ushort DefaultPort = 22023;
private readonly string _amongUsDir;
private readonly string _regionFile;
public string RegionName { get; set; } = DefaultRegionName;
public AmongUsModifier()
{
var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "LocalLow");
if (!Directory.Exists(appData))
{
appData = FindProtonAppData();
}
if (appData == null)
return;
_amongUsDir = Path.Combine(appData, "Innersloth", "Among Us");
_regionFile = Path.Combine(_amongUsDir, "regionInfo.dat");
}
private string FindProtonAppData()
{
string steamApps;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".steam", "steam", "steamapps");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
steamApps = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Steam", "steamapps");
}
else
{
return null;
}
if (!Directory.Exists(steamApps))
return null;
var libraries = new List<string>
{
steamApps,
};
var vdf = Path.Combine(steamApps, "libraryfolders.vdf");
if (File.Exists(vdf))
{
var libraryFolders = VdfConvert.Deserialize(File.ReadAllText(vdf));
foreach (var libraryFolder in libraryFolders.Value.Children<VProperty>())
{
if (!int.TryParse(libraryFolder.Key, out _))
continue;
libraries.Add(Path.Combine(libraryFolder.Value.Value<string>(), "steamapps"));
}
}
foreach (var library in libraries)
{
var path = Path.Combine(library, "compatdata", AppId.ToString(), "pfx", "drive_c", "users", "steamuser", "AppData", "LocalLow");
if (Directory.Exists(path))
{
return path;
}
}
return null;
}
public async Task<bool> SaveIpAsync(string input)
{
// Filter out whitespace.
input = input.Trim();
// Split port from ip.
// Only IPv4 is supported so just do it simple.
var ip = string.Empty;
var port = DefaultPort;
var parts = input.Split(':');
if (parts.Length >= 1)
{
ip = parts[0];
}
if (parts.Length >= 2)
{
ushort.TryParse(parts[1], out port);
}
// Check if a valid IP address was entered.
if (!IPAddress.TryParse(ip, out var ipAddress))
{
// Attempt to resolve DNS.
try
{
var hostAddresses = await Dns.GetHostAddressesAsync(ip);
if (hostAddresses.Length == 0)
{
OnError("Invalid IP Address entered");
return false;
}
// Use first IPv4 result.
ipAddress = hostAddresses.First(x => x.AddressFamily == AddressFamily.InterNetwork);
}
catch (SocketException)
{
OnError("Failed to resolve hostname.");
return false;
}
}
// Only IPv4.
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
{
OnError("Invalid IP Address entered, only IPv4 is allowed.");
return false;
}
var result = WriteIp(ipAddress, port);
OnSaved(ip, port);
return result;
}
/// <summary>
/// Writes an IP Address to the Among Us region file.
/// </summary>
/// <param name="ipAddress">The IPv4 address to write.</param>
/// <param name="port"></param>
private bool WriteIp(IPAddress ipAddress, ushort port)
{
if (ipAddress == null ||
ipAddress.AddressFamily != AddressFamily.InterNetwork)
{
throw new ArgumentException(nameof(ipAddress));
}
if (!Directory.Exists(_amongUsDir))
{
OnError("Among Us directory was not found, is it installed? Try running it once.");
return false;
}
using (var file = File.Open(_regionFile, FileMode.Create, FileAccess.Write))
using (var writer = new BinaryWriter(file))
{
var ip = ipAddress.ToString();
var region = new RegionInfo(RegionName, ip, new[]
{
new ServerInfo($"{RegionName}-Master-1", ip, port),
});
region.Serialize(writer);
return true;
}
}
/// <summary>
/// Loads the existing region info from the Among Us.
/// </summary>
public bool TryLoadRegionInfo(out RegionInfo regionInfo)
{
regionInfo = null;
if (!File.Exists(_regionFile))
{
return false;
}
using (var file = File.Open(_regionFile, FileMode.Open, FileAccess.Read))
using (var reader = new BinaryReader(file))
{
try
{
regionInfo = RegionInfo.Deserialize(reader);
return true;
}
catch (Exception exception)
{
OnError("Couldn't parse region info\n" + exception);
return false;
}
}
}
/// <summary>
/// Loads the existing IP Address from the Among Us region file
/// if it was set by Impostor before.
/// </summary>
public bool TryLoadIp(out string ipAddress)
{
ipAddress = null;
if (!TryLoadRegionInfo(out var regionInfo))
{
return false;
}
if ((regionInfo.Name == RegionName || regionInfo.Name == DefaultRegionName) && regionInfo.Servers.Count >= 1)
{
ipAddress = regionInfo.Servers.ElementAt(0).Ip;
return true;
}
return false;
}
private void OnError(string message)
{
Error?.Invoke(this, new ErrorEventArgs(message));
}
private void OnSaved(string ipAddress, ushort port)
{
Saved?.Invoke(this, new SavedEventArgs(ipAddress, port));
}
public event EventHandler<ErrorEventArgs> Error;
public event EventHandler<SavedEventArgs> Saved;
}
}
@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Impostor.Patcher.Shared
{
public class Configuration
{
private const string FileRecentIps = @"recent_ips.txt";
private const int MaxRecentIps = 5;
private readonly string _baseDir;
private readonly string _recentIpsPath;
private readonly List<string> _recentIps;
public Configuration()
{
var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
_baseDir = Path.Combine(appData, "Impostor");
_recentIpsPath = Path.Combine(_baseDir, FileRecentIps);
_recentIps = new List<string>();
}
public IReadOnlyList<string> RecentIps => _recentIps;
public void Load()
{
if (File.Exists(_recentIpsPath))
{
_recentIps.AddRange(File.ReadAllLines(_recentIpsPath));
}
}
public void Save()
{
Directory.CreateDirectory(_baseDir);
if (!Directory.Exists(_baseDir))
{
return;
}
if (_recentIps.Count > 0)
{
File.WriteAllLines(_recentIpsPath, _recentIps);
}
}
public void AddIp(string ip)
{
if (_recentIps.Contains(ip))
{
_recentIps.Remove(ip);
}
_recentIps.Insert(0, ip);
if (_recentIps.Count > MaxRecentIps)
{
_recentIps.RemoveAt(MaxRecentIps);
}
}
}
}
@@ -1,14 +0,0 @@
using System;
namespace Impostor.Patcher.Shared.Events
{
public class ErrorEventArgs : EventArgs
{
public ErrorEventArgs(string message)
{
Message = message;
}
public string Message { get; }
}
}
@@ -1,16 +0,0 @@
using System;
namespace Impostor.Patcher.Shared.Events
{
public class SavedEventArgs : EventArgs
{
public SavedEventArgs(string ipAddress, ushort port)
{
IpAddress = ipAddress;
Port = port;
}
public string IpAddress { get; }
public ushort Port { get; }
}
}
@@ -1,11 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Gameloop.Vdf" Version="0.6.1" />
</ItemGroup>
</Project>
@@ -1,48 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace Impostor.Patcher.Shared.Innersloth
{
public class RegionInfo
{
public RegionInfo(string name, string ping, IReadOnlyList<ServerInfo> servers)
{
Name = name;
Ping = ping;
Servers = servers;
}
public string Name { get; }
public string Ping { get; }
public IReadOnlyList<ServerInfo> Servers { get; }
public void Serialize(BinaryWriter writer)
{
writer.Write(0);
writer.Write(Name);
writer.Write(Ping);
writer.Write(Servers.Count);
foreach (var server in Servers)
{
server.Serialize(writer);
}
}
public static RegionInfo Deserialize(BinaryReader reader)
{
var unknown = reader.ReadInt32();
var name = reader.ReadString();
var ping = reader.ReadString();
var servers = new List<ServerInfo>();
var serverCount = reader.ReadInt32();
for (var i = 0; i < serverCount; i++)
{
servers.Add(ServerInfo.Deserialize(reader));
}
return new RegionInfo(name, ping, servers);
}
}
}
Loaded 100 of 156 files, more files were not shown because too many files have changed in this diff. Show more