Compare commits

..
131 Commits
Author SHA1 Message Date
miniduikboot 5ee0fdb61f Bump version 2026-08-30 18:48:37 +02:00
miniduikboot 44ebc0c6b0 Rerun Dumpostor on 18.0 2026-08-30 18:48:37 +02:00
miniduikboot 681126dfb5 Address my own comments 2026-08-30 18:48:37 +02:00
linmeideli 9f627b7b01 clean up warns 2026-08-30 18:48:37 +02:00
linmeideli 510ed07389 Basically support AMCI 2026-08-30 18:48:37 +02:00
linmeideli 8f41030faf fix 17.4 support i thk 2026-08-30 18:48:37 +02:00
linmeideli f23dd8aa25 Support Judge and new vote 2026-08-30 18:48:37 +02:00
linmeideli 857e04dbc3 Support New HideNSeekGameOptions 2026-08-30 18:48:37 +02:00
miniduikboot b78f86f0f3 Fix compile warnings 2026-08-30 18:48:37 +02:00
miniduikboot db9c6ab66c Add special game version for mobile
Thanks Niko
2026-08-30 18:48:37 +02:00
miniduikboot a161aa7045 Add basic implementation of judge overruling 2026-08-30 18:48:37 +02:00
miniduikboot caab877c80 Add support for RoleOptions v11
This is bare minimum bringup for AU v18
2026-08-30 18:48:37 +02:00
miniduikboot 202b955db1 Deserialize gamecode as packed in PackedGameDataTo (#738)
* Deserialize gamecode as packed in PackedGameDataTo

Most packets use a normal 32 bit int but not PackedGameDataTo. Instead,
Innersloth decided to waste a byte by using WritePacked. As v2 game
codes are always negative, this is less efficient, but alas.

It was documented in the pseudocode and I didn't spot the difference
when reviewing the implementation, so I made this mistake when
deduplicating this code.

Test plan:
- Start EHR on at least 2 clients
- Use the game mode "Natural Disasters"
- Observe that pixelated blobs are visible for all players, not just the host

* uopt: stop parsing code to string

* Address review comments
2026-08-14 19:47:39 +02:00
NikoCat233andminiduikboot 1cd298608a Support new http matchmaking (#685)
* Add GameFilters interface

* Add Newtonsoft.Json

Used by InnerSloth

* Port 2025.3.25 Data

* Generate OptionNames

* Fix missing CategorizedOptionNames

* Remove Newtonsoft.Json

* Add missing GameFilterList

* Basic support for new filters

* Add FiltersController

Server need to tell clients its filters

* Add Tag filter in filtergames

* Add GameTags in legacy game options

* Correctly deserialize Filters

* Minor text fixes

---------

Co-authored-by: miniduikboot <mini@duikbo.at>
2026-08-13 22:03:04 +02:00
miniduikboot f0689ac2e2 Bump Hazel to 1.0.1 (#735)
This pulls in https://github.com/Impostor/Impostor.Hazel/pull/4
2026-07-01 19:02:24 +02:00
2cad4ffa14 Added an RPC packet size anti-cheat check with config (#731)
* Added an RPC packet size anti-cheat

* Support PackedGameDataTo

* Move packet size check to HazelConnection

* Update PacketSizeLimit to 1153 and add anti-cheat docs

* Change default to value determined on officials

Thanks Niko for doing the research
---------

Co-authored-by: miniduikboot <mini@duikbo.at>
Co-authored-by: NikoCat233 <139348239+NikoCat233@users.noreply.github.com>
2026-06-21 11:17:05 +02:00
miniduikboot e9437e781b Add compat versions for 17.4
Mobile has a different version than PC for reasons unknown at the
moment, probably related to ads.
2026-06-16 06:05:34 +02:00
miniduikboot 06f4f8bf00 Gate PackedGameDataTo behind AC check 2026-05-24 20:36:02 +02:00
NikoCat233 2cb9c648f3 Add support for PackedGameDataTo 2026-05-24 20:36:02 +02:00
miniduikboot b30775c2fc Add compat version for 17.3 (#724)
* Add compat version for 17.3

* Add compat version for 17.3.1
2026-04-20 22:00:32 +02:00
miniduikboot 764c42eae3 Merge pull request #723 from miniduikboot/fix/aof-concdict
Fix race conditions in Game.Data
2026-03-30 22:30:37 +02:00
miniduikboot 4244f39640 Rename _allObjectsFast to _allObjects
Done because there is no second datastructure anymore
2026-03-27 23:32:57 +01:00
miniduikboot 069ec446cd Switch to concurrent dictionary
PR proposed in #722 doesn't solve all issues, now Nullrefs show up.

Also drop _allObjects as it's never read.
2026-03-26 23:58:45 +01:00
miniduikboot 98783e5b42 Merge pull request #717 from Impostor/feature/17.2.2
Add 17.2.2 version
2026-03-22 23:06:45 +01:00
miniduikboot fd12cd1a39 Add 17.2.2 version
Only changes are ad related, and officials allow crossplay
2026-03-17 20:18:25 +01:00
miniduikboot 139f2334a2 Merge pull request #716 from Impostor/feature/17.2-hotfix
Add 17.2 hotfix version
2026-03-04 20:31:58 +01:00
miniduikboot 60371d538c Add 17.2 hotfix version 2026-02-19 21:34:54 +01:00
miniduikboot 6e68747fe0 Merge pull request #715 from Impostor/feature/17.2
Add 17.2 version
2026-02-18 07:24:47 +01:00
miniduikboot 3736b37831 Add 17.2 version
https://www.innersloth.com/announcing-among-us-x-ace-attorney-again/
2026-02-18 07:19:36 +01:00
miniduikboot eeb85e4abc Merge pull request #712 from miniduikboot/feature/aumi-host-chat-cmds
Add support for host chat cmds
2026-02-18 07:17:58 +01:00
miniduikboot fd33a892cd Reduce warnings about +25
As Innersloth documented it in their mod info document, assume it's here
to stay, so warn about it less heavily
2026-01-27 22:41:42 +01:00
miniduikboot fbdb708941 Implement host chat commands
On January 21 Innersloth released a document that details a few new
features. One of these are host chat commands: any chat message that
starts with "/cmd" is forwarded to the host and to the host only.

Implement this feature in Impostor too for implementation parity.

For more info, see their GitHub page:
https://github.com/Innersloth-LLC/AmongUsModdingInformation
2026-01-27 22:17:03 +01:00
miniduikboot 9a60ce3d56 Merge pull request #708 from Impostor/feature/17.1
Bump compatversion to support 17.1
2026-01-27 20:38:36 +01:00
miniduikboot d7601e8057 Add another mobile-only hotfix
From their Discord:

Admin has pushed hotfix v17.1.2 for mobile players! minor changes to the ad rewards to the daily cap (subject to future change) and updated the screen to clarify when the daily cap is reset.
2025-12-11 22:20:17 +01:00
miniduikbootandNiko233 0e9454ecdf Bump compatversion to support 17.1.1
https://www.innersloth.com/ad-rewards-v17-1-1/

Thanks to Niko for reminding me about this

Co-Authored-By: Niko233 <139348239+NikoCat233@users.noreply.github.com>
2025-12-07 10:33:23 +01:00
miniduikboot ce5a491cdb Bump compatversion to support 17.1 2025-11-18 20:50:57 +01:00
miniduikboot 16c8acc849 Merge pull request #706 from NikoCat233/patch-1
Add missing comment for game version 16.1.0
2025-11-18 20:50:46 +01:00
NikoCat233 195d31cad1 Add missing comment for game version 16.1.0 2025-11-05 01:35:52 +08:00
miniduikboot 18846d3dd4 Merge pull request #705 from Impostor/feature/17.0.1
Bump to 17.0.1
2025-10-15 20:42:47 +02:00
miniduikboot eb231dc393 Bump to 17.0.1 2025-10-14 20:29:33 +02:00
miniduikboot a00b9e63d8 Merge pull request #702 from Impostor/feature/17.0.0
Add support for 17.0.0
2025-09-26 20:22:43 +02:00
miniduikboot d7e5594cf6 Run Dumposter on 17.0.0 2025-09-22 21:06:05 +02:00
miniduikboot 8ba89d9844 Add GameOptions V10
Manually added roles to RoleTypes, will clean up later
2025-09-09 20:40:07 +02:00
miniduikboot ebd7f99438 Bump to 1.10.4
No breaking changes expected
2025-09-09 20:39:31 +02:00
miniduikboot 2197434bdc Add version block for 17.0.0 2025-09-09 20:16:31 +02:00
miniduikboot 8f54566245 Add trailing comma
Fixes compile warning
2025-06-05 22:39:11 +02:00
miniduikboot a70e875a4d Update System.Text.Json 2025-06-05 22:39:11 +02:00
miniduikboot e473e4cdb4 Merge pull request #692 from miniduikboot/doc/https-now-mandatory
Update docs to add that HTTPS is now mandatory
2025-06-05 22:31:25 +02:00
miniduikboot 9169fab67d Update FAQ for AU3D 2025-06-02 22:52:45 +02:00
miniduikboot 565bf26ff8 Clarify startup warning 2025-06-02 22:52:24 +02:00
miniduikboot 8fb996b314 Update documentation for mandatory HTTPS
The new Unity version used by Among Us 16.0.5 disables the
"InsecureHttpOption", which blocks UnityWebRequests via HTTP. Therefore
all Impostor servers should now switch to HTTPS.

https://docs.unity3d.com/2022.3/Documentation/ScriptReference/InsecureHttpOption.html

Thanks to TianMengLucky for reporting this and NikoCat233 for
identifying the cause
2025-06-02 22:49:45 +02:00
miniduikboot 46a6005ba5 Don't clear RoleOptions on update
Among Us always adds a constant amount of roles to this dict, so it
doesn't make sense to clear this dictionary every time.

Furthermore, previously it was possible to trigger a
ConcurrentModificationException when running Deserialize in parallel
with Serialize. This should no longer happen, as new keys are no longer added
2025-05-28 22:34:57 +02:00
miniduikboot 0ac0b1d403 Address review comments 2025-05-22 23:50:28 +02:00
miniduikboot e1bd85313a Remove AllowNull from PlayerControl.PlayerInfo
It is possible to spawn invalid network objects without an attached
PlayerInfo, and Impostor will throw exceptions everywhere if you happen
to spawn one. Officials do allow spawning objects like this, so allow it
in Impostor for compatibility reasons
2025-05-22 23:50:28 +02:00
miniduikboot fff9713697 Add new cheat category for InvalidObject checks
For some network objects, like PlayerControl, we expect that there is an
PlayerInfo object associated to them. If there isn't, throw an exception
in this AC category
2025-05-22 23:50:28 +02:00
miniduikboot be0a0b917c Add Murder/Protect/Exile API without checks
Force(.*)Async will perform the same function as \1Async but without
throwing exceptions. The old API is maintained for backwards
compatibility reasons.

Murder checks are now shared with the main function that handles
CheckMurder, as ProtectPlayerAsync and ExileAsync aren't used in the
code I didn't make a similar change there. Checks were also synced
between both methods
2025-05-22 23:50:28 +02:00
miniduikboot c672e19e99 Always store previous color
Because it's only restored in standard SA networking, the stored value
will be ignored in HA. This is preferred to logging when in HA mode
2025-05-22 23:48:50 +02:00
miniduikboot 104376b43f Record and restore player colors
This prevents situations where a player that requested red has a
different color when rejoining because red is the default color of a
PlayerInfo object. This is most noticeable if the host is late with
rejoining, and a lot of players are alreading in WaitingForHost limbo.

When the host finally rejoins, they will not assign Red immediately to
players that requested Red as it is "in use", and instead assign the
next color.

By recording and restoring this is avoided, and players tend to get
their own colors back in most cases.

This is not something that officials do at the moment, but I don't think
this has an impact on mods, as a normal checkcolor/setcolor cycle will
take place as usual.
2025-05-22 23:48:50 +02:00
miniduikboot 92ca5aa9d7 Merge pull request #690 from Impostor/feature/16.0.5
Add 16.0.5 support
2025-05-22 23:13:49 +02:00
miniduikboot 67e94e8efc Add 16.0.5
Crossplay with the previous version is not supported, so it gets its own
compatibility group.

Further analysis on the changes is still needed
2025-05-20 19:48:57 +02:00
miniduikboot da8353655d Merge pull request #683 from Impostor/feature/16.0.0
Add support for 16.0.0
2025-04-03 21:31:35 +02:00
miniduikboot dab8751f1d Add note to old game methods
These are no longer used, so warn people who do want to edit them.

I don't foresee these getting removed soon though, as we do want to kick
players on old versions for having an old game version
2025-04-01 23:50:17 +02:00
miniduikboot 723495e390 Add GameTags enum
Can't be generated from game files at the moment unfortunately
2025-04-01 23:49:46 +02:00
miniduikboot cd220530ac Remove GameListingV2
The game ignores unknown fields when deserializing, and as only fields
were added it's acceptable to merge the structs together
2025-04-01 23:48:32 +02:00
miniduikbootandNiko233 fc2679a8d2 Add stub for Find Lobby endpoint
Co-Authored-By: Niko233 <139348239+NikoCat233@users.noreply.github.com>
2025-03-30 20:35:19 +02:00
miniduikbootandNiko233 28e933092a Stop hardcoding the GameOptions in GameListingV2
To prevent needing to pull in Impostor.Hazel, use an extension method
instead.

Thanks Niko for researching this part

Co-Authored-By: Niko233 <139348239+NikoCat233@users.noreply.github.com>
2025-03-30 20:35:03 +02:00
miniduikboot 06289d46c0 Bump minor version
At this point it doesn't seem necessary to drop support for old
Amoguses, so just bump the minor version for support purposes
2025-03-30 20:34:18 +02:00
miniduikboot 6bd13c0399 Forbid crossplay between 2024.10.29 and 16.0
Innersloth also forbids it, and it's likely not possible due to the new
GameOption version
2025-03-30 20:34:18 +02:00
miniduikboot 7e85419b82 Add endpoint to find games by code
This is used in the new "Enter Code" flow

3 extra fields are added to GameListing, including a Base 64
serialization method for game options
2025-03-30 20:34:18 +02:00
miniduikboot 3e2c3f1525 Update Game Options
Creating a game is now possible
2025-03-30 20:34:18 +02:00
miniduikboot 61cbd5c4d8 Add new network version 2025-03-30 20:34:18 +02:00
miniduikbootandjs6pak 495dc01948 Add diagnostic http message (#663)
* Add diagnostic message to test HTTP server

This makes it possible to diagnose if the reverse proxy setup is working
correctly or if the game configuration is wrong

* Print console log as well

Only print it once to prevent console spam

* Update src/Impostor.Server/Http/HelloController.cs

Co-authored-by: js6pak <me@6pak.dev>

---------

Co-authored-by: js6pak <me@6pak.dev>
2025-01-26 18:00:03 +01:00
Chipmunk a448ece73e Add missing cosmetic methods to IInnerPlayerControl 2025-01-26 17:58:42 +01:00
miniduikboot e7b98f1842 Push down AC checks in PlayerPhysics as well
I've checked Impostor for issues that are similar to #664, and found
this one: if mods add a custom rpc on PlayerPhysics, Impostor will check
ownership of it, which isn't intentional.
2025-01-26 17:42:18 +01:00
miniduikboot d65e99fe45 Move down target AC check in InnerShipStatus
Mods may send RPC's on a ShipStatus that aren't broadcasts. Previously
Impostor would forbid that, which doesn't make sense to me. By moving
down the check it'll allow mods to broadcast RPC's on ShipStatus.

Reported-By: xtracube
2025-01-26 17:41:13 +01:00
miniduikboot bb980096ff Bump version to 1.10.2 2024-12-22 17:02:56 +01:00
miniduikbootandjs6pak 58a8bda8e7 Check for disconnection between handling messages
Without this, it is possible for clients to get the remainder of their
messages handled in a packet.

Co-Authored-By: js6pak <me@6pak.dev>
2024-12-22 17:02:56 +01:00
miniduikbootandjs6pak a7c37b6bcf Clean up abandoned games on disconnect
When a client disconnects without ever having joined a game, it leaves
behind an empty game on the server. Remove these when a player
disconnects.

Co-Authored-By: js6pak <me@6pak.dev>
2024-12-22 17:02:56 +01:00
miniduikbootandjs6pak 792c9626e4 Prevent clients from creating a second game
Legitimate clients will never create a second game using the same
connection.

Co-Authored-By: js6pak <me@6pak.dev>
2024-12-22 17:02:56 +01:00
miniduikboot e4f81b4a30 Bump to Among Us 2024.10.29 (#656)
Inspected mono, no networking changes found.

Ran dumpostor as well

Bumped Impostor version
2024-10-24 19:47:01 +02:00
danbudworthmead 2603f34b89 Bugfix/fix outfit RPCs (#653)
* implemented the sequence ID

* implemented the sequence ID for the player outfits

the sequence ID in the set call must be greater than that stored on the client otherwise the client disregards the set call.

* make sequence IDs internal

* added space after closing brace

* make the sequence ID addition +5
2024-10-05 12:44:08 +02:00
Niko233 9b27edbb11 Remove unused HandleSetInfected 2024-10-05 12:28:50 +02:00
danbudworthmead 2f9406cb6a updated the api call to use the setnamerpc serialise
the client now expects a byte which is the netid otherwise the setname call wont work
2024-10-05 12:28:16 +02:00
Niko233 d5f48aa798 Fix vanilla venting as Phantom cause a ban 2024-09-07 08:53:45 +04:00
js6pak ff5501aab7 Add support for 2024.9.4 2024-09-05 05:09:34 +02:00
js6pak acc4da1210 Fix RemovePlayer reason on internal disconnects 2024-08-24 16:52:50 +02:00
js6pak a0f0a65cbf Send DisconnectReason.Hacking to other players when disconnecting for cheating
Base game doesn't have a specific message for it so it results in a generic "left the game due to error" but it's still better than the default "left the game".
This also allows client side mods to fix the message.
2024-08-24 16:51:51 +02:00
js6pak 6c91280125 Fix cheating disconnect message
DisconnectReason.Hacking has been broken in base game for a long time so replace it with a custom message.
Also add a random support code for easier debugging in case of false positives.
2024-08-24 16:51:51 +02:00
miniduikboot 2ddbb8891c Refactor SetColor anticheat
One issue with the ColorLimits checks is that it didn't account for mods
that add extra colors, this is fixed by checking if the route followed
is plausible instead of mimicking client logic and only accepting a
single answer that it expected. A false negative is possible where the
host decides to wrap back to the front but there are still custom colors
available at the end of the list. This is acceptable because Impostor
doesn't currently know the amount of colors available and this issue
isn't major enough to ask all mod authors to declare their custom color
amount to the server.

As extra colors should now be handled by this code, it makes sense to
change the category of the unknown colors check to ProtocolExtensions.
For these mods an exemption of the ColorLimits category is no longer
necessary.

Note that mods that allow duplicate colors that do not use a custom rpc
to set colors can only be supported by disabling the ColorLimits
category, this was already the case and I don't think it can be unified
with the existing check.
2024-08-23 18:04:52 +02:00
Niko233 8c52b13af4 更新 Game.State.cs 2024-08-22 08:12:47 +02:00
miniduikboot f6318c7040 Merge pull request #609 from Impostor/feature/2024.6.18-prepare
Prepare for 2024.6.18 update
2024-08-20 21:50:45 +02:00
Niko233 4d6c060c7c Add handler for phantom rpcs 2024-08-20 21:41:38 +02:00
Niko233 83bf1c4368 Add Phantom Rpcs support 2024-08-20 21:41:38 +02:00
miniduikboot ed9f6e3134 Add Phantom to the list of Impostor roles 2024-08-20 21:41:38 +02:00
miniduikboot 219b72a3d1 Clean up owned playerinfo's when disconnecting
This commit does what official servers do:
- despawn the PlayerInfo if someone disconnects while in the lobby
- despawn all PlayerInfos at the end of a game when the EndGame screen
is shown. At this point a lot of other netobjects are destroyed as well,
with the PlayerInfos generally done first.
2024-08-20 21:41:38 +02:00
miniduikboot 01e9cccdf5 Set PlayerLevel in PlayerInfo on RPC SetLevel
Previously this used to be done by the host, but this is now the
server's responsibility.
2024-08-20 21:41:38 +02:00
miniduikbootandjs6pak fc79bd1536 Add Message24EndGameHostMigration serialization
Looks like Innersloth added this but hasn't actually used this. Weird..

When sent it makes the new host perform a full rejoin, which gums up
Impostor's spawning logic. Fixing that causes the client to gum itself
up. Sigh.

After some testing it seems like host migration still works fine, so
this investigation wasn't needed. Committing for posterity.

Co-Authored-By: js6pak <me@6pak.dev>
2024-08-20 21:41:38 +02:00
miniduikboot 9e1abe07ef Switch PlayerName writes to Outfit
In 2024.6.18 Innersloth changed the storage location of the PlayerName
from PlayerInfo to PlayerOutfit. If we don't set the PlayerName, we'll
send an incomplete PlayerInfo to new players, so new players are unable
to stay connected as they will not receive complete PlayerInfo objects
before a timeout in the client activates.

Because we publish PlayerName in the API, we can't completely remove
the PlayerName field from PlayerInfo. We also use it internally in a few
places, so I don't this is worth removing.
2024-08-20 21:41:38 +02:00
miniduikboot ccd36b6571 Sync over tasks
As tasks used to be part of the GameData, 2024.6.18 has moved them to
the PlayerInfo. Because they are now called on the PlayerInfo of the
correct player, the playerId field in the RPC is no longer necessary.
2024-08-20 21:41:38 +02:00
miniduikboot 44e9dfe28d Bump to 2024.8.13
Network side seems unchanged and officials allow crossplay
2024-08-20 21:41:38 +02:00
miniduikboot 09cadcb63b Spawn PlayerInfo's if host is not authoritive
This PlayerInfo also needs to be synced over if it is server owned
2024-08-20 21:41:38 +02:00
miniduikboot b118af12be Add NetId to SetName and SetColor
2024.6.18 adds a NetId field *in front* of the existing fields to help link back to the PlayerInfo. We don't actually need this NetId and assume it's called on the correct PlayerControl, so we ignore it.
2024-08-20 21:41:38 +02:00
miniduikboot 372ed8511f Set RoleWhenAlive when set to a Ghost role 2024-08-20 21:41:38 +02:00
miniduikboot 85296d2d46 Serialize Color -1 as 0 in PlayerOutfit
Official servers send 0 instead of -1 when the outfit is still
incomplete to prevent some client issues.
2024-08-20 21:41:38 +02:00
miniduikboot c43fdd2a69 Demote InnerGameData, promote InnerPlayerInfo
In 2024.6.18 Innersloth promoted PlayerInfo to its own network object, and gave it to the server. InnerGameData is now a container that holds these players. This commit implements the changes necessary to handle these separate PlayerInfo network objects.
2024-08-20 21:41:38 +02:00
miniduikboot 615469c6c8 Add dirty tracking to PlayerOutfit
To prevent excessive modifications in the codebase, which is prone to
issues if a callsite is missed, automatically do this using properties.

Keep the access modifiers intact for now.
2024-08-20 21:41:38 +02:00
miniduikboot 482b8c2b72 Add sequence id's to playeroutfit 2024-08-20 21:41:38 +02:00
miniduikboot 2eca9b4e9f Add canOverrideRole param to SetRole
This is currently unused by Innersloth
2024-08-20 21:41:38 +02:00
miniduikboot 079f58a7b7 Implement GameOptions V8
This adds a field for RulesPresets and SpecialGameModes, as well as
RoleOptions for the new roles.
2024-08-20 21:41:38 +02:00
miniduikboot c315ef9c28 VoteBanSystem now contains itself as a component
This makes it consistent with the other spawnables.
2024-08-20 21:41:38 +02:00
miniduikboot 0e7372d9d9 Update Dumpostor to v1.0.1 and run for 6.18
Add the RulesPresets and SpecialGamemodes enums
2024-08-20 21:41:38 +02:00
miniduikboot bf5a2c2be5 Prepare for 2024.6.18 update
Major changes are incoming, this doesn't yet work.
2024-08-20 21:41:38 +02:00
miniduikboot 066eca09ee Add scene param to forbidden scene log statement
I'm seeing multiple warnings in my server log about forbidden scene
changes. Unfortunately this warning does not include the actual scene
that was triggering the issue, which makes diagnosis/remediation difficult.

Additionally change this logging call to use names suitable for
structured logging.
2024-08-20 17:18:00 +02:00
miniduikboot 35c66cda5f Change default ListenIp to match with config
The default ListenIp for the base game server has been set to 0.0.0.0 in
config.json for as long as I remember, and a different value is only
sporadically used. I remember a case where someone was fancy with Split
DNS. 0 is better as a default than localhost, so change the code
accordingly to aligh with config.json
2024-08-10 18:50:57 +02:00
Bilibili_MengChu 5d4b3d34f5 Modify config.json GameRecorderEnabled Default Value 2024-08-10 18:50:57 +02:00
Bilibili_MengChu d94cf78a98 Update docs/Server-configuration.md File Description 2024-08-10 18:50:57 +02:00
Bilibili_MengChu cfa66fa5cb Delete full-config-example.json and merge it into config.json 2024-08-10 18:50:57 +02:00
miniduikboot 2d82e1af19 Bump System.Text.Json to 8.0.4
Fixes CVE-2024-30105 (High 7.5/10)
https://github.com/advisories/GHSA-hh2w-p6rv-4g7w

Impostor doesn't use the affected
JsonSerializer.DeserializeAsyncEnumerable method as it doesn't accept
JSON formatted payloads, but the NU1903 warning is shown on all builds,
leading to warning fatigue.
2024-07-29 19:59:24 +02:00
miniduikboot c6bad15346 Let hosts handle exiles
Previously Impostor liked to kill exiled players server side to raise an
event. This is quite early, as the host will SetRole them to a ghost
role later.

However as PlayerInfo is server-owned by default in 2024.6.18, it starts
killing players client side. This caused meetings to immediately
conclude if the final impostor was voted out, ruining the cutscene.

So this commit makes Impostor wait for hosts to actually kill the player
using SetRole. Do set the LastDeathReason property though for players
using the API.
2024-07-29 19:58:33 +02:00
miniduikboot b26f451a07 Make Start/Finish GameData API public 2024-07-29 19:57:52 +02:00
miniduikboot 4f7ef58b00 Refactor Start/FinishRpc to add GameData methods
Rpc's are a specific sort of GameData packets, so it makes sense to
split and extract a method for sending GameData packets from these functions.
2024-07-29 19:57:52 +02:00
miniduikbootandjs6pak fe3ea67510 Don't DC the client if they send an unknown flag
This item is only used in a LogTrace call, so it seems excessive to kick
a player on servers just for a log statement

Co-Authored-By: js6pak <me@6pak.dev>
2024-07-27 19:13:42 +02:00
miniduikboot c522dd9f88 Add compatibility option to allow host authority
There are various bugs in the 2024.6.18 implementation of host-authoritive
multiplayer such that I can't recommend players to use it. Fixing these
issues on the server side is impossible, and I can't smell remotely if
clients have applied the necessary fixes. The currently known bugs
include at least:

- Unable to join more than 8 players in a room
- Unable for new players to join if host switched from host authoritive
  to server authoritive
- Host is unable to kill if it is in a host authoritive game while it
  thinks it is in server authoritive mode

If Innersloth fixes this mode in future game patches, this option may be
reconsidered.
2024-07-27 18:49:04 +02:00
miniduikboot 46caeb76c2 Add new option to full config example
Some people were confused by config-full.json, thinking that this file
should be edited too. This is not the case and the values in this file
don't apply as it was only meant as an example, so rename the file to
make that more clear.

Also update this file to add the options from the Modular Anticheat
2024-07-27 17:05:34 +02:00
miniduikboot b218079f9e Merge pull request #599 from miniduikboot/fix/compatman-no-invalid-clients
Improve CompatMgr behaviour for unknown versions
2024-07-19 20:11:44 +02:00
miniduikboot a61524842f Improve CompatMgr behaviour for unknown versions
Currently, Impostor will show an "Client is in an invalid state."
message if a player tries to join a match where the host or the joining
player is using a version unknown to Impostor.

This message confused a *lot* of people, so this PR changes the API such
that if the version is not registered, the version itself is put in a
compatibility group and compared, assuming no other game versions are
compatible with it. This works fine in most cases, as Among Us network
versions generally increase over time.

Failure on 2222.2.2 (which is ancient at this point, so likely no longer
used) and future cases where Innersloth messes up (which is unlikely) is
an acceptable consequence to me.
2024-06-14 21:49:12 +02:00
miniduikboot 34c14f69a4 Merge pull request #598 from miniduikboot/fix/custom-rpc-gamelogic
Accept RPC's called on GameManager
2024-06-14 07:13:20 +02:00
miniduikboot da0ee81b5d Accept RPC's called on GameManager
Previously Impostor would just throw when a method was called on a
GameManager, but this caused issues with client mods that did just this,
even through vanilla never calls an RPC on GameManager.

This commit changes that throw statement to pass the exception to the
custom message handler/anticheat instead, which is also how it works for
the other NetObjects
2024-06-13 20:52:25 +02:00
miniduikboot 468a71d01e Merge pull request #597 from Impostor/feature/2024.6.4
Add 2024.6.4 game version
2024-06-05 19:43:03 +02:00
miniduikboot 6f05946689 Bump Impostor version 2024-06-05 19:16:59 +02:00
miniduikboot b2a9bab125 Add 2024.6.4 game version
Officials allow crossplay and after inspecting the Mono I don't see any
major changes, so I'm following officials here
2024-06-04 20:42:18 +02:00
149 changed files with 64692 additions and 797 deletions

No files matched your search

+8
View File
@@ -8,6 +8,14 @@ The Impostor project is a reverse engineered and open source server for the game
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)
## Can this be used with the console version of the game?
Sadly not, as these don't allow adding custom servers.
## Can this be used with Among Us 3D/VR?
No, it's an entirely different game with a completely different network stack. So we'd need to develop another server completely from scratch. We're currently not interested in doing this.
## 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!
+25 -10
View File
@@ -1,19 +1,13 @@
# HTTP Server
Since Impostor 1.9.0 a HTTP service is included for matchmaking. Recent versions of Among Us require this service in order to connect correctly and not kick players after a round.
Since Impostor 1.9.0 a HTTP service is included for matchmaking, which is required for clients to connect to the Impostor server.
Depending on whether you want to support mobile players, you can set up the HTTP server in one of two ways:
- Directly expose the HTTP server. Simpler, but only works if you don't want to support mobile players.
- Use a reverse proxy to expose the HTTP server. More complex, but allows mobile players to connect.
## Directly expose the HTTP server.
In config.json, set "ListenIp" to "0.0.0.0" in the "HttpServer" section. If you don't have this section, look in the example [config.json](https://github.com/Impostor/Impostor/blob/master/src/Impostor.Server/config.json)
Starting from Among Us 16.0.5, you need to set up HTTPS for players to connect to your server.
Unfortunately Impostor doesn't handle SSL certificates, so you need to set up a reverse proxy.
## Use a reverse proxy
A reverse proxy allows you to forward HTTP requests from users to multiple services. If you already have one, you should configure it to add Impostor. This page contains an Nginx configuration you can use for reference.
A reverse proxy allows you to forward HTTP requests from users to multiple services. If you already have one, you should configure it to add Impostor.
If you have never set up a reverse proxy before, we recommend you to set up [Caddy](https://caddyserver.com/). It is easy to set up and comes with support for requesting SSL certificates out of the box.
@@ -65,3 +59,24 @@ server {
```
</details>
## Stop exposing Impostor's HTTP server to the internet directly
To only allow connections to Impostor's HTTP servers via the reverse proxy, we recommend to set the ListenIp of the HTTP server to 127.0.0.1:
```json
{
// NOTE: merge this snippet into the rest of your configuration
"HttpServer": {
"ListenIp": "127.0.0.1"
}
}
```
Or if you're configuring Impostor with environment variables:
```sh
IMPOSTOR_HttpServer__ListenIp=127.0.0.1
```
For more information on server configuration, see [Server-configuration.md](Server Configuration).
+12 -9
View File
@@ -10,7 +10,9 @@ To connect to the server, you need to configure and install a region file on htt
Among Us connects to the server using two network services: the (TCP) HTTP service points Among Us to the UDP service, then the UDP service hosts the actual game traffic. Because of this, Impostor uses port 22023 using **both** the TCP and UDP protocols.
As the phone version of Among Us requires this HTTP connection to be secure, we recommend using a HTTP reverse proxy to terminate SSL on the HTTP service. Setup instructions for this are in the [Http Server documentation](Http-server.md).
To connect to your Among Us version from another PC, you need to set up a HTTP reverse proxy to support HTTPS. If you're just running a version of Impostor for testing Setup instructions for this are in the [Http Server documentation](Http-server.md).
If you want to test if your Among Us HTTP server is working, open `http://localhost:22023` (assuming you're using the default settings, change the IP and port respectively) in a browser
Depending on your host you may also need to port forward Impostor to the internet or pass Impostor traffic by your firewall. Port 22023 UDP needs to be accessible for everyone that wants to play on the server, then you also need to portforward your HTTP reverse proxy or port 22023 TCP if you don't use a reverse proxy. As port forwarding changes per host or router configuration, port forwarding is not covered by this guide.
@@ -21,8 +23,9 @@ Depending on your host you may also need to port forward Impostor to the interne
3. Extract the zip.
4. Modify `config.json` to your liking. Documentation can be found [here](Server-configuration.md). You need to at least change `PublicIp` to the address people will connect to your server to.
5. Run `Impostor.Server` (Linux/macOS) or `Impostor.Server.exe` (Windows)
6. (OPTIONAL - Linux) Configure a systemd definition file and enable the service to start on boot, see [systemd configuration](Server-configuration.md#systemd)
7. (OPTIONAL) Set up a reverse proxy to support HTTPS, so mobile phones can connect, see [reverse proxy configuration](Http-server.md)
6. Set up a reverse proxy to support HTTPS, so you can connect to your server from another device. See [reverse proxy configuration](Http-server.md)
7. (OPTIONAL - Linux) Configure a systemd definition file and enable the service to start on boot, see [systemd configuration](Server-configuration.md#systemd)
## Using Docker
@@ -34,7 +37,7 @@ Docker is a program that allows you to run programs like Impostor in a container
After installing Docker, you can just start a Docker container with `docker run`:
```
docker run -p 22023:22023/tcp -p 22023:22023/udp -e IMPOSTOR_Server__PublicIp=your.public.ip.here aeonlucid/impostor:nightly
docker run -p 127.0.0.1:22023:22023/tcp -p 22023:22023/udp -e IMPOSTOR_Server__PublicIp=your.public.ip.here aeonlucid/impostor:nightly
```
Please replace `your.public.ip.here` with the public IP address of your server. This is the address Among Us will try to reach your server at.
@@ -43,7 +46,7 @@ To configure the docker container, either use environment variables or mount con
## Using Docker Compose
Docker Compose allows you to start a Docker container using a prefined configuration. This is an example configuration you can continue on:
Docker Compose allows you to start a Docker container using a predefined configuration. This is an example configuration you can continue on:
```
version: '3.4'
@@ -53,12 +56,12 @@ services:
image: aeonlucid/impostor:nightly
container_name: impostor
ports:
- 22023:22023/tcp # Add "127.0.0.1:" if you're using a reverse proxy to terminate HTTPS
- 127.0.0.1:22023:22023/tcp # Set up your own reverse proxy to terminate HTTPS
- 22023:22023/udp
environment: # Either configure Impostor using environment variables or mount a copy of config.json
environment: # Either configure Impostor using environment variables or mount config.json in your container
- IMPOSTOR_Server__PublicIp=your.public.ip.here
volumes:
- /path/to/local/config.json:/app/config.json # For easy editing of the config
- /path/to/local/config.json:/app/config.json # Mount config.json
- /path/to/local/plugins:/app/plugins # Only needed if using plugins
- /path/to/local/libraries:/app/libraries # Only needed if using external libraries
- /path/to/local/libraries:/app/libraries # Only needed if using external libraries (some plugins may need this)
```
+25 -23
View File
@@ -1,7 +1,5 @@
# Server configuration
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
@@ -27,29 +25,33 @@ Impostor has an Http Server that is used by recent versions of Among Us to conne
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. |
| **AllowCheatingHosts** | `"Never"` | Configure whether hosts are allowed to cheat. "Never" forbids it, "Always" allows it. "IfRequested" allows hosts to cheat if they connect with the DisableServerAuthorityFlag set. |
| **EnableGameFlowChecks** | `true` | Enable checks that check if certain actions are done in the appropriate order or at the appropriate moment in the game. This includes changing cosmetics while in game or murdering too fast. |
| **EnableMustBeHostChecks** | `true` | Enables checks that check if players are the host before they can do actions that require them to be host of the game. This includes starting the game and spawning objects. |
| **EnableColorLimitChecks** | `true` | Enables checks that checks if players request colors that are already in use. |
| **EnableNameLimitChecks** | `true` | Enables checks that checks if player names have a length that is possible to set using the user interface. |
| **EnableOwnershipChecks** | `true` | Enables checks that check if players are allowed to perform a certain action on themself or another player. |
| **EnableRoleChecks** | `true` | Enables checks that check if players have the correct role when performing certain role abilities like venting or murdering. |
| **EnableTargetChecks** | `true` | Enables checks that check if certain packets to everyone that should only have been sent to certain players or vice versa. This includes sending votes or network objects. |
| **ForbidProtocolExtensions** | `true` | If disabled allows players to send network packets that go beyond the network packets sent by the vanilla game. This is necessary for most mods that need all players to install it. |
| 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. |
| **AllowCheatingHosts** | `"Never"` | Configure whether hosts are allowed to cheat. "Never" forbids it, "Always" allows it. "IfRequested" allows hosts to cheat if they connect with the DisableServerAuthorityFlag set. |
| **AllowHostOnlyExtensions** | "IfRequested" | Configure whether [Host-Only extensions](https://github.com/Innersloth-LLC/AmongUsModdingInformation#host-only-mods) are allowed in all games ("Always"), +25 games only ("IfRequested") or by nobody ("Never") |
| **EnableGameFlowChecks** | `true` | Enable checks that check if certain actions are done in the appropriate order or at the appropriate moment in the game. This includes changing cosmetics while in game or murdering too fast. |
| **EnableInvalidObjectChecks** | `true` | Enables checks that check if network objects are spawned properly. Disabling this option also implies disabling EnableRoleChecks. |
| **EnableMustBeHostChecks** | `true` | Enables checks that check if players are the host before they can do actions that require them to be host of the game. This includes starting the game and spawning objects. |
| **EnableColorLimitChecks** | `true` | Enables checks that checks if players request colors that are already in use. |
| **EnableNameLimitChecks** | `true` | Enables checks that checks if player names have a length that is possible to set using the user interface. |
| **EnableOwnershipChecks** | `true` | Enables checks that check if players are allowed to perform a certain action on themself or another player. |
| **EnableRoleChecks** | `true` | Enables checks that check if players have the correct role when performing certain role abilities like venting or murdering. |
| **EnableTargetChecks** | `true` | Enables checks that check if certain packets to everyone that should only have been sent to certain players or vice versa. This includes sending votes or network objects. |
| **ForbidProtocolExtensions** | `true` | If disabled allows players to send network packets that go beyond the network packets sent by the vanilla game. This is necessary for most mods that need all players to install it. |
| **EnablePacketSizeChecks** | `true` | Enables checks that verify if network messages exceed the maximum allowed size. |
| **PacketSizeLimit** | `1203` | The maximum allowed size (in bytes) for a network message. The official servers of Among Us requires that a Hazel packet is < 1204 bytes, excluding headers. |
### Compatibility
Impostor has two compatibility options which allow some extra flexibility but may not work properly. Enabling either of these options is not recommended.
Impostor has some compatibility options which allow some extra flexibility but may not work properly. Enabling any of these options is not recommended. When contacting support, please mention which of these options are enabled.
| Key | Default | Value |
| ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **AllowNewGameVersions** | `false` | Warning: Setting this option to `true` is unsupported and may cause issues when large updates to Among Us are released. Allow future versions of Among Us to join your server. |
| **AllowVersionMixing** | `false` | Allow players using different game versions to play in one lobby. |
| Key | Default | Value |
|-----------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **AllowFutureGameVersions** | `false` | Warning: Setting this option to `true` is unsupported and may cause issues when large updates to Among Us are released, but can be useful when a small patch is released. Allows future versions of Among Us to join your server. |
| **AllowHostAuthority** | `false` | Certain Among Us mods allow disabling some server-authoritative features, which also changes some code paths in the client. These code paths have not undergone as much testing and contain bugs, which can't be fixed from the Impostor side. Use with caution. |
| **AllowVersionMixing** | `false` | Allows players using different game versions to play in one lobby that have not been marked by the Impostor developers as compatible. |
### Debug
@@ -99,11 +101,11 @@ Other Serilog sinks are also supported, but are out of scope for this documentat
### File
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.
The `config.json` file contains all the configurable options.
### Environment variables
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:
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
+6
View File
@@ -47,6 +47,12 @@ Also check if the port Impostor (ListenPort) is listening on is correctly port-f
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.
## Disconnected with `You disconnected from the server. If this happens often, check your network strength. This may also be a server issue`
Usually this means that Among Us couldn't connect to Impostor's HTTP server.
Open the address of your server in a web browser, it should show you a small page confirming that Impostor is available.
## 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:
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>11</LangVersion>
<VersionPrefix>1.9.2</VersionPrefix>
<VersionPrefix>1.10.8</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
@@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" Version="8.0.0" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="System.Text.Json" Version="8.0.5" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="System.Text.Encodings.Web" Version="8.0.0" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" PrivateAssets="all" GeneratePathProperty="true" />
@@ -79,10 +79,20 @@ public sealed class SourceGenerator : IIncrementalGenerator
enumGenerator.Generate("GameOverReason", underlyingType: CSharpEnumUnderlyingType.Byte);
enumGenerator.Generate("Platforms");
enumGenerator.Generate("RoleTypes", underlyingType: CSharpEnumUnderlyingType.UnsignedShort);
enumGenerator.Generate("RulesPresets", underlyingType: CSharpEnumUnderlyingType.Byte);
enumGenerator.Generate("SpecialGameModes", underlyingType: CSharpEnumUnderlyingType.Byte);
enumGenerator.Generate("StringNames");
enumGenerator.Generate("SystemTypes", underlyingType: CSharpEnumUnderlyingType.Byte);
enumGenerator.Generate("Language", sourceName: "SupportedLangs");
enumGenerator.Generate("TaskTypes");
enumGenerator.Generate("BoolOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("ByteOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("CategorizedOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("FloatArrayOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("FloatOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("Int32ArrayOptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("Int32OptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("UInt32OptionNames", "Impostor.Api.Innersloth.GameFilters");
enumGenerator.Generate("RpcCalls", "Impostor.Api.Net.Inner", underlyingType: CSharpEnumUnderlyingType.Byte);
@@ -60,9 +60,9 @@
},
"System.Text.Json": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==",
"requested": "[8.0.5, )",
"resolved": "8.0.5",
"contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.Buffers": "4.5.1",
@@ -151,6 +151,7 @@
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
}
}
}
},
".NETStandard,Version=v2.0/win-x64": {}
}
}
+9
View File
@@ -5,6 +5,9 @@ public enum CheatCategory
/// <summary>A packet used a part of the network protocol that is unknown to Impostor, like a custom RPC.</summary>
ProtocolExtension,
/// <summary>A host-only mod extension is used outside of host-authoritive mode.</summary>
HostOnlyExtension,
/// <summary>A packet was sent at an inappropriate moment.</summary>
GameFlow,
@@ -26,6 +29,12 @@ public enum CheatCategory
/// <summary>A packet was sent to a player that should be broadcasted, or vice versa.</summary>
Target,
/// <summary>A packet was sent on an invalid network object, like a PlayerControl without PlayerInfo.</summary>
InvalidObject,
/// <summary>A packet was sent that exceeded the maximum allowed RPC size.</summary>
PacketSize,
/// <summary>Legacy category for unsorted anticheat checks.</summary>
Other,
}
+9 -1
View File
@@ -1,4 +1,4 @@
namespace Impostor.Api.Config
namespace Impostor.Api.Config
{
public class AntiCheatConfig
{
@@ -10,10 +10,14 @@
public CheatingHostMode AllowCheatingHosts { get; set; } = CheatingHostMode.Never;
public CheatingHostMode AllowHostOnlyExtensions { get; set; } = CheatingHostMode.IfRequested;
public bool EnableGameFlowChecks { get; set; } = true;
public bool EnableMustBeHostChecks { get; set; } = true;
public bool EnableInvalidObjectChecks { get; set; } = true;
public bool EnableColorLimitChecks { get; set; } = true;
public bool EnableNameLimitChecks { get; set; } = true;
@@ -25,5 +29,9 @@
public bool EnableTargetChecks { get; set; } = true;
public bool ForbidProtocolExtensions { get; set; } = true;
public bool EnablePacketSizeChecks { get; set; } = true;
public int PacketSizeLimit { get; set; } = 1203;
}
}
@@ -6,6 +6,8 @@ namespace Impostor.Api.Config
public bool AllowFutureGameVersions { get; set; } = false;
public bool AllowHostAuthority { get; set; } = false;
public bool AllowVersionMixing { get; set; } = false;
}
}
@@ -30,5 +30,7 @@
Sorry, UDP Matchmaking is no longer supported.
See <link={UpgradingDocsLink}#impostor-190>Impostor documentation</link> on how to migrate to HTTP Matchmaking
""";
public const string HostAuthorityUnsupported = "Your client is requesting host authority, which is not enabled on this Impostor server.";
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ namespace Impostor.Api.Config
public ushort PublicPort { get; set; } = 22023;
public string ListenIp { get; set; } = "127.0.0.1";
public string ListenIp { get; set; } = "0.0.0.0";
public ushort ListenPort { get; set; } = 22023;
@@ -1,4 +1,4 @@
using Impostor.Api.Net.Inner.Objects;
using Impostor.Api.Net.Inner.Objects;
namespace Impostor.Api.Events.Meeting
{
@@ -13,5 +13,15 @@ namespace Impostor.Api.Events.Meeting
/// Gets a value indicating whether meeting result is a tie.
/// </summary>
bool IsTie { get; }
/// <summary>
/// Gets a value indicating whether a Judge overruled the vote result.
/// </summary>
bool WasOverruled { get; }
/// <summary>
/// Gets the nonce of the winning Judge overrule, or 0 if the meeting wasn't overruled.
/// </summary>
ushort OverrideId { get; }
}
}
@@ -1,4 +1,4 @@
namespace Impostor.Api.Events.Player
namespace Impostor.Api.Events.Player
{
public interface IPlayerChatEvent : IPlayerEvent, IEventCancelable
{
@@ -6,5 +6,12 @@
/// Gets the message sent by the player.
/// </summary>
string Message { get; }
/// <summary>
/// Gets or sets a value indicating whether the message is sent to
/// all players (true) or only to the host (false).
/// To not send this message at all, cancel this event instead.
/// </summary>
bool SendToAllPlayers { get; set; }
}
}
+30 -5
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
@@ -47,6 +48,11 @@ namespace Impostor.Api.Games
/// </remarks>
bool IsHostAuthoritive { get; }
/// <summary>
/// Gets the mod GUID that the game was registered with through the AMCI.
/// </summary>
Guid? ModGuid { get; }
IClientPlayer? GetClientPlayer(int clientId);
T? FindObjectByNetId<T>(uint netId)
@@ -101,22 +107,41 @@ namespace Impostor.Api.Games
/// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>
ValueTask SendToAsync(IMessageWriter writer, int id);
/// <summary>
/// Start a GameData(To) message.
/// </summary>
/// <remarks>Use with caution, recommended only for advanced developers.</remarks>
/// <param name="targetClientId">The client to target if needed, `null` if the message should be sent to all players.</param>
/// <param name="type">Message type of the message.</param>
/// <returns>A <see cref="IMessageWriter" /> that you can fill and send using <see cref="FinishGameDataAsync"/>.</returns>
IMessageWriter StartGameData(int? targetClientId = null, MessageType type = MessageType.Reliable);
/// <summary>
/// Finishes GameData message and sends it to either the target or all players.
/// </summary>
/// <remarks>Use with caution, recommended only for advanced developers.</remarks>
/// <param name="writer">MessageWriter received from <see cref="StartGameData"/>.</param>
/// <param name="targetClientId">Same target ClientId passed to StartGameData.</param>
/// <returns>Task that sends the packet.</returns>
ValueTask FinishGameDataAsync(IMessageWriter writer, int? targetClientId = null);
/// <summary>
/// Creates a MessageWriter with GameData header.
/// </summary>
/// <remarks>Use with caution, recommended only for advanced developers.</remarks>
/// <param name="targetNetId">Net id of the InnerNetObject.</param>
/// <param name="callId">Rpc id of the message.</param>
/// <param name="targetClientId">Client id of GameDataTo's target.</param>
/// <param name="targetClientId">The client to target if needed, `null` if the message should be sent to all players.</param>
/// <param name="type">Message type of the message.</param>
/// <returns>A <see cref="IMessageWriter" /> that you can fill with rpc data and send using <see cref="FinishRpcAsync"/>.</returns>
IMessageWriter StartRpc(uint targetNetId, RpcCalls callId, int? targetClientId, MessageType type = MessageType.Reliable);
IMessageWriter StartRpc(uint targetNetId, RpcCalls callId, int? targetClientId = null, MessageType type = MessageType.Reliable);
/// <summary>
/// Finishes rpc message and sends it to the target.
/// Finishes rpc message and sends it to either the target or all players.
/// </summary>
/// <remarks>Use with caution, recommended only for advanced developers.</remarks>
/// <param name="writer">Message writer of the rpc.</param>
/// <param name="targetClientId">Id of the receiving client.</param>
/// <param name="targetClientId">Same target client id passed to <see cref="StartRpc"/>.</param>
/// <returns>A <see cref="ValueTask" /> representing the asynchronous operation.</returns>
ValueTask FinishRpcAsync(IMessageWriter writer, int? targetClientId = null);
}
+1 -1
View File
@@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" PrivateAssets="all" />
<PackageReference Include="Impostor.Hazel.Abstractions" Version="1.0.0" />
<PackageReference Include="Impostor.Hazel.Abstractions" Version="1.0.1" />
<ProjectReference Include="..\Impostor.Api.Innersloth.Generator\Impostor.Api.Innersloth.Generator.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
<AdditionalFiles Include="Innersloth/Data/**/*.json" />
@@ -2,42 +2,102 @@ namespace Impostor.Api.Innersloth.Customization
{
public class PlayerOutfit
{
public ColorType Color { get; internal set; } = (ColorType)(-1);
// Magic value that is assigned by default when the outfit is initially created. This is not
// `null` because existing API already exposed this magic value. It's also not 0 (which is
// the value exposed when serializing) because SetColor anticheat checks exist.
private const ColorType UnknownColor = (ColorType)(-1);
public string HatId { get; internal set; } = "missing";
private string _playerName = string.Empty;
private ColorType _color = UnknownColor;
private string _hatId = "missing";
private string _petId = "missing";
private string _skinId = "missing";
private string _visorId = "missing";
private string _namePlateId = "missing";
public string PetId { get; internal set; } = "missing";
public bool IsDirty { get; internal set; }
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
public string PlayerName
{
get
{
if (!string.IsNullOrEmpty(PlayerName) && Color != (ColorType)(-1) && HatId != "missing" && PetId != "missing" && SkinId != "missing" && VisorId != "missing")
{
return NamePlateId == "missing";
}
return true;
}
get => _playerName;
internal set => SetField(ref _playerName, value);
}
public ColorType Color
{
get => _color;
internal set => SetField(ref _color, value);
}
public string HatId
{
get => _hatId;
internal set => SetField(ref _hatId, value);
}
public string PetId
{
get => _petId;
internal set => SetField(ref _petId, value);
}
public string SkinId
{
get => _skinId;
internal set => SetField(ref _skinId, value);
}
public string VisorId
{
get => _visorId;
internal set => SetField(ref _visorId, value);
}
public string NamePlateId
{
get => _namePlateId;
internal set => SetField(ref _namePlateId, value);
}
internal byte HatSequenceId { get; set; } = 0;
internal byte PetSequenceId { get; set; } = 0;
internal byte SkinSequenceId { get; set; } = 0;
internal byte VisorSequenceId { get; set; } = 0;
internal byte NamePlateSequenceId { get; set; } = 0;
/// <summary>
/// Gets a value indicating whether a player outfit is complete or whether it needs to receive additional cosmetics.
/// </summary>
/// <returns>true if the outfit is incomplete, false if the outfit is complete.</returns>
public bool IsIncomplete => string.IsNullOrEmpty(PlayerName)
|| Color == UnknownColor
|| HatId == "missing"
|| PetId == "missing"
|| SkinId == "missing"
|| VisorId == "missing"
|| NamePlateId == "missing";
public void Serialize(IMessageWriter writer)
{
writer.Write(PlayerName);
writer.WritePacked((int)Color);
// Follow officials by not sending a color value that is out of range. Sending -1 can cause exceptions on the client side.
writer.WritePacked(Color == UnknownColor ? 0 : (int)Color);
writer.Write(HatId);
writer.Write(PetId);
writer.Write(SkinId);
writer.Write(VisorId);
writer.Write(NamePlateId);
writer.Write(HatSequenceId);
writer.Write(PetSequenceId);
writer.Write(SkinSequenceId);
writer.Write(VisorSequenceId);
writer.Write(NamePlateSequenceId);
}
public void Deserialize(IMessageReader reader)
@@ -49,6 +109,17 @@ namespace Impostor.Api.Innersloth.Customization
SkinId = reader.ReadString();
VisorId = reader.ReadString();
NamePlateId = reader.ReadString();
HatSequenceId = reader.ReadByte();
PetSequenceId = reader.ReadByte();
SkinSequenceId = reader.ReadByte();
VisorSequenceId = reader.ReadByte();
NamePlateSequenceId = reader.ReadByte();
}
private void SetField<T>(ref T field, T value)
{
field = value;
IsDirty = true;
}
}
}
File diff suppressed because it is too large. Load diff
@@ -2,13 +2,14 @@
"0": "SkeldShipStatus",
"1": "MeetingHud",
"2": "LobbyBehaviour",
"3": "GameData",
"4": "PlayerControl",
"5": "MiraShipStatus",
"6": "PolusShipStatus",
"7": "ShipStatus",
"7": "SkeldShipStatus",
"8": "AirshipStatus",
"9": "HideAndSeekManager",
"10": "NormalGameManager",
"11": "NetworkedPlayerInfo",
"12": "VoteBanSystem",
"13": "FungleShipStatus"
}
@@ -0,0 +1,17 @@
{
"Invalid": 0,
"VisualTasks": 1,
"GhostsDoTasks": 2,
"ConfirmImpostor": 3,
"AnonymousVotes": 4,
"IsDefaults": 5,
"UseFlashlight": 6,
"SeekerFinalVents": 7,
"SeekerFinalMap": 8,
"SeekerPings": 9,
"ShowCrewmateNames": 10,
"Roles": 11,
"ShapeshifterLeaveSkin": 1000,
"ImpostorsCanSeeProtect": 1100,
"NoisemakerImpostorAlert": 1300
}
@@ -0,0 +1,4 @@
{
"Invalid": 0,
"MapId": 1
}
@@ -0,0 +1,14 @@
{
"KillCooldown": 0,
"PlayerSpeed": 1,
"EmergencyMeetingCount": 2,
"EmergencyMeetingCooldown": 3,
"DiscussionTime": 4,
"VotingTime": 5,
"CrewmateVision": 6,
"HidingTime": 7,
"MaxTimeInVent": 8,
"ImposterVision": 9,
"FinalHideTime": 10,
"FinalHideImposterSpeed": 11
}
@@ -0,0 +1,4 @@
{
"Invalid": 0,
"KillDistances": 1
}
@@ -0,0 +1,31 @@
{
"Invalid": 0,
"KillCooldown": 1,
"PlayerSpeedMod": 2,
"ImpostorLightMod": 3,
"CrewLightMod": 4,
"CrewmateTimeInVent": 100,
"FinalEscapeTime": 101,
"EscapeTime": 102,
"SeekerFinalSpeed": 103,
"MaxPingTime": 104,
"CrewmateFlashlightSize": 105,
"ImpostorFlashlightSize": 106,
"ShapeshifterCooldown": 1000,
"ShapeshifterDuration": 1001,
"ProtectionDurationSeconds": 1100,
"GuardianAngelCooldown": 1101,
"ScientistCooldown": 1200,
"ScientistBatteryCharge": 1201,
"EngineerCooldown": 1300,
"EngineerInVentMaxTime": 1301,
"PhantomCooldown": 1500,
"PhantomDuration": 1501,
"TrackerCooldown": 1550,
"TrackerDuration": 1551,
"TrackerDelay": 1552,
"NoisemakerAlertDuration": 1600,
"ViperDissolveTime": 1601,
"DetectiveSuspectLimit": 1602,
"JudgeTaskRequirementPercentage": 1620
}
@@ -1,11 +1,11 @@
{
"HumansByVote": 0,
"HumansByTask": 1,
"ImpostorByVote": 2,
"ImpostorByKill": 3,
"ImpostorBySabotage": 4,
"CrewmatesByVote": 0,
"CrewmatesByTask": 1,
"ImpostorsByVote": 2,
"ImpostorsByKill": 3,
"ImpostorsBySabotage": 4,
"ImpostorDisconnect": 5,
"HumansDisconnect": 6,
"HideAndSeek_ByTimer": 7,
"HideAndSeek_ByKills": 8
"CrewmateDisconnect": 6,
"HideAndSeek_CrewmatesByTimer": 7,
"HideAndSeek_ImpostorsByKills": 8
}
@@ -0,0 +1,5 @@
{
"Invalid": 0,
"MaxImpostors": 1,
"MinPlayers": 2
}
@@ -0,0 +1,21 @@
{
"Invalid": 0,
"NumImpostors": 1,
"KillDistance": 2,
"NumEmergencyMeetings": 3,
"EmergencyCooldown": 4,
"DiscussionTime": 5,
"VotingTime": 6,
"MaxImpostors": 7,
"MinPlayers": 8,
"MaxPlayers": 9,
"NumCommonTasks": 10,
"NumShortTasks": 11,
"NumLongTasks": 12,
"TaskBarMode": 13,
"Tag": 14,
"CrewmatesRemainingForVitals": 100,
"CrewmateVentUses": 101,
"ImpostorPlayerID": 102,
"RulePreset": 200
}
@@ -6,5 +6,11 @@
"GuardianAngel": 4,
"Shapeshifter": 5,
"CrewmateGhost": 6,
"ImpostorGhost": 7
"ImpostorGhost": 7,
"Noisemaker": 8,
"Phantom": 9,
"Tracker": 10,
"Detective": 12,
"Viper": 18,
"Judge": 19
}
@@ -8,15 +8,15 @@
"SetName": 6,
"CheckColor": 7,
"SetColor": 8,
"SetHat": 9,
"SetSkin": 10,
"SetHat_Deprecated": 9,
"SetSkin_Deprecated": 10,
"ReportDeadBody": 11,
"MurderPlayer": 12,
"SendChat": 13,
"StartMeeting": 14,
"SetScanner": 15,
"SendChatNote": 16,
"SetPet": 17,
"SetPet_Deprecated": 17,
"SetStartCounter": 18,
"EnterVent": 19,
"ExitVent": 20,
@@ -33,8 +33,8 @@
"SendQuickChat": 33,
"BootFromVent": 34,
"UpdateSystem": 35,
"SetVisor": 36,
"SetNamePlate": 37,
"SetVisor_Deprecated": 36,
"SetNamePlate_Deprecated": 37,
"SetLevel": 38,
"SetHatStr": 39,
"SetSkinStr": 40,
@@ -55,5 +55,10 @@
"CheckShapeshift": 55,
"RejectShapeshift": 56,
"LobbyTimeExpiring": 60,
"ExtendLobbyTimer": 61
"ExtendLobbyTimer": 61,
"CheckVanish": 62,
"StartVanish": 63,
"CheckAppear": 64,
"StartAppear": 65,
"QueueOverruleVotes": 66
}
@@ -0,0 +1,6 @@
{
"Standard": 0,
"StandardRoles": 1,
"Flashlight": 2,
"Custom": 100
}
@@ -0,0 +1,23 @@
{
"None": 0,
"ImpersonationCeleb": 1,
"Spamming": 2,
"InappropriateNameUnsportsmanlike": 3,
"UnsportsmanlikeConduct": 4,
"ImpersonationDevelopers": 5,
"InappropriateChatPersonalInfo": 6,
"InappropriateNameDerogatory": 7,
"InappropriateNameNsfw": 8,
"Bullying": 9,
"CheatingHacking": 10,
"InappropriateChatDating": 11,
"WeaponizingRules": 12,
"RepeatOffender3": 13,
"SexualMisconduct": 14,
"Doxing": 15,
"IllegalActivity": 16,
"Harassment": 17,
"SelfHarmPromotion": 18,
"RepeatOffender10": 19,
"InvalidUser": 20
}
@@ -0,0 +1,4 @@
{
"None": 0,
"AprilFools": 1
}
@@ -570,10 +570,39 @@
"RolesHelp_Engineer_02": 1563,
"RolesHelp_GuardianAngel_01": 1564,
"RolesHelp_GuardianAngel_02": 1565,
"RolesHelp_Noisemaker_01": 1566,
"RolesHelp_Noisemaker_02": 1567,
"RolesHelp_Tracker_01": 1568,
"RolesHelp_Tracker_02": 1569,
"RolesHelp_Shapeshifter_01": 1600,
"RolesHelp_Shapeshifter_02": 1601,
"RolesHelp_Phantom_01": 1602,
"RolesHelp_Phantom_02": 1603,
"RolesHelpOutro_01": 1650,
"RolesHelpOutro_02": 1651,
"NoisemakerRole": 1660,
"NoisemakerBlurb": 1661,
"NoisemakerBlurbMed": 1662,
"NoisemakerBlurbLong": 1663,
"NoisemakerImpostorAlert": 1664,
"NoisemakerAlertDuration": 1665,
"PhantomAbility": 1670,
"PhantomRole": 1671,
"PhantomBlurb": 1672,
"PhantomBlurbMed": 1673,
"PhantomBlurbLong": 1674,
"PhantomCooldown": 1675,
"PhantomDuration": 1676,
"PhantomAbilityUndo": 1677,
"TrackerAbility": 1680,
"TrackerRole": 1681,
"TrackerBlurb": 1682,
"TrackerBlurbMed": 1683,
"TrackerBlurbLong": 1684,
"TrackerCooldown": 1685,
"TrackerDuration": 1686,
"TrackerAbilityUndo": 1687,
"TrackerDelay": 1688,
"SanctionDuration": 1700,
"SanctionPermanent": 1701,
"SanctionConduct": 1702,
@@ -597,6 +626,14 @@
"SanctionSelfHarmPromotion": 1720,
"SanctionRepeatOffender10": 1721,
"SanctionUnknown": 1722,
"ReplicatorAbility": 1800,
"ReplicatorRole": 1801,
"ReplicatorBlurb": 1802,
"ReplicatorBlurbMed": 1803,
"ReplicatorBlurbLong": 1804,
"ReplicatorCooldown": 1805,
"ReplicatorDuration": 1806,
"ReplicatorQuantity": 1807,
"ScreenShakeOption": 1900,
"FeaturedItems": 1901,
"FeaturedBundles": 1902,
@@ -1304,6 +1341,24 @@
"ConfirmDeleteAccountsEmpty": 3209,
"AccountRequestDelete": 3210,
"HasBeenKilled": 3211,
"RoomSettingsLabel": 3280,
"ViewLabel": 3281,
"RoomCodeLabel": 3282,
"ModeLabel": 3283,
"PrivacyLabel": 3284,
"CapacityLabel": 3285,
"GameSettingsLabel": 3286,
"RoleSettingsLabel": 3287,
"GamePresetsLabel": 3288,
"GamePresetsDescription": 3289,
"StandardRulesPresetDescription": 3290,
"GameModeStandard": 3291,
"GameModeStandardRoles": 3292,
"GameModeCustom": 3293,
"ReturnToLobby": 3294,
"GameModeFlashlight": 3295,
"HostNounLabel": 3296,
"RoleSettingsAll": 3297,
"ScrollList": 3300,
"ScrollNews": 3301,
"AmongUsAnnouncements": 3302,
@@ -1331,6 +1386,40 @@
"ReportNotificationBody": 3324,
"MissingGuardianEmailText": 3325,
"AprilFoolsModeToggle": 3326,
"LobbyChangeSettingNotification": 3327,
"LoadingBarLoading": 3328,
"LoadingBarGameStart": 3329,
"LoadingBarGameStartWaitingPlayers": 3330,
"ImpostorsCategory": 3350,
"RolesCategory": 3351,
"MeetingsCategory": 3352,
"PlayersCategory": 3353,
"TasksCategory": 3354,
"GameSettingsDescription": 3355,
"RoleSettingsDescription": 3356,
"InfoLabel": 3357,
"RoleQuotaLabel": 3358,
"OverviewCategory": 3359,
"CrewmateRolesHeader": 3360,
"ImpostorRolesHeader": 3361,
"ViewingLabel": 3362,
"LocalLobbyLabel": 3363,
"NumberRoleHeader": 3364,
"ChanceRoleHeader": 3365,
"WaitingForPlayers": 3366,
"HideAndSeekModeDescription": 3367,
"RolesRulesPresetDescription": 3368,
"FlashlightRulesPresetDescription": 3369,
"RoomCodeCopied": 3370,
"FinalHideCategory": 3371,
"LobbyChangeSettingNotificationRole": 3372,
"StandardRulesPresetDescriptionHideAndSeek": 3373,
"GameModeStandardSeek": 3374,
"HostYouLabel": 3375,
"GamePresetsPopupTitle": 3376,
"GamePresetsPopupText": 3377,
"GamePresetsPopupContinue": 3378,
"GamePresetsPopupBack": 3379,
"MiningPit": 3400,
"Jungle": 3401,
"BuildSandcastle": 3402,
@@ -1365,6 +1454,9 @@
"CookFish": 3431,
"PrepVegetables": 3432,
"MushroomMixupName": 3433,
"EosAccountSilentMergeFail": 3434,
"AskForEosAccount": 3435,
"KwsCreateFail": 3436,
"GameType": 3500,
"GameTypeError": 3501,
"GameTypeClassic": 3502,
@@ -1399,5 +1491,176 @@
"HttpErrorBadGateway": 3531,
"HttpErrorServiceUnavailable": 3532,
"HttpErrorGatewayTimeout": 3533,
"UserVersionError": 3534
"UserVersionError": 3534,
"QCLobbyRoleNoisemakers": 3535,
"QCLobbyRoleTrackers": 3536,
"QCLobbyRolePhantoms": 3537,
"QCSelfWasTracking": 3538,
"QCSelfGoingToNoisemaker": 3539,
"QCSelfSawRoleInLocation": 3540,
"QCCoreSettings": 3541,
"QCRolesGalore": 3542,
"QCPitchDark": 3543,
"QCFlashlight": 3544,
"QCPleaseWait": 3545,
"QCWaitMinute": 3546,
"QCLobbyClosing": 3547,
"QCChangeCosmetics": 3548,
"QCWaitingPlayers": 3549,
"QCLetsWaitPlayers": 3550,
"ServerLabel": 3551,
"JoinLobby": 3552,
"ChatLabel": 3553,
"ToolTipDefault": 3554,
"LevelLabel": 3555,
"NoneLowercaseLabel": 3556,
"ToolTipClassic": 3557,
"ToolTipHide": 3558,
"ToolTipMap": 3559,
"ToolTipCapacity": 3560,
"ToolTipLevel": 3561,
"ToolTipServer": 3562,
"ToolTipCrossplay": 3563,
"ToolTipNoCrossplay": 3564,
"ToolTipLanguage": 3565,
"ConfirmSettings": 3566,
"ConfirmSettingsTooltip": 3567,
"EnterCodeElipsis": 3568,
"ToolTipChatTab": 3569,
"DropshipsLabel": 3570,
"FiltersActive": 3571,
"ClearFilters": 3572,
"MoreLabel": 3573,
"HostNounEmpty": 3574,
"ToolTipSkeld": 3575,
"ToolTipMIRA": 3576,
"ToolTipPolus": 3577,
"ToolTipAirship": 3578,
"ToolTipFungle": 3579,
"FilterLow": 3580,
"FilterMed": 3581,
"FilterHigh": 3582,
"FilterVeryHigh": 3583,
"FilterLong": 3584,
"FilterNone": 3585,
"FilterPlayerLevel": 3586,
"RefreshLabel": 3587,
"JoinGameLabel": 3588,
"FilterClearText": 3589,
"MatchesFoundLabel": 3590,
"FilterGameTooltip": 3591,
"FilterMapTooltip": 3592,
"FilterImpostorTooltip": 3593,
"FilterRoleTooltip": 3594,
"FilterSpeedTooltip": 3595,
"FilterKillCDTooltip": 3596,
"FilterVotingTooltip": 3597,
"FilterVisualTaskTooltip": 3598,
"FilterAnonVotesTooltip": 3599,
"FilterConfirmEjectTooltip": 3600,
"FilterLevelTooltip": 3601,
"FilterCrossplayTooltip": 3602,
"FilterDefaultTooltip": 3603,
"FilterFlashlightTooltip": 3604,
"FilterHidingTimeTooltip": 3605,
"FilterFinalTimeTooltip": 3606,
"FilterMaxVentTooltip": 3607,
"FilterBeginner": 3608,
"FilterIntermediate": 3609,
"FilterExpert": 3610,
"FilterTag": 3611,
"FilterTagTooltip": 3612,
"TooltipTag": 3613,
"SelectedLabel": 3614,
"TooltipSpeedInfo": 3615,
"TooltipKillCDInfo": 3616,
"TooltipVotingInfo": 3617,
"TooptipHidingTimeInfo": 3618,
"TooltipFinalTimeInfo": 3619,
"FilterNoMaps": 3620,
"MaxFilters": 3621,
"XboxCommsMultiplayerError": 3700,
"XboxCommsUGError": 3701,
"XboxCommsCreateGameError": 3702,
"XboxCommsSearchError": 3703,
"XboxCommsJoinError": 3704,
"WatchAdBody": 3800,
"WatchAdTitle": 3801,
"WatchAd": 3802,
"RewardAdTitle": 3803,
"RewardAdBody1": 3804,
"RewardAdBody2": 3805,
"WatchAdErrorBody": 3806,
"WatchAdErrorTitle": 3807,
"DetectiveRole": 4000,
"DetectiveAbility": 4001,
"DetectiveAbilityNotes": 4002,
"DetectiveBlurb": 4003,
"DetectiveBlurbMed": 4004,
"DetectiveBlurbLong": 4005,
"DetectiveNotesEmpty": 4006,
"DetectiveNotesFoundDead": 4007,
"DetectiveNotesSuspectInfo": 4008,
"DetectiveNearLocation": 4009,
"DetectiveNotesKiller": 4010,
"StatsDetectiveQuestioned": 4011,
"StatsDetectiveCasesFilled": 4012,
"DetectiveSuspectLimit": 4013,
"RolesHelp_Detective_01": 4014,
"RolesHelp_Detective_02": 4015,
"ViperRole": 4037,
"ViperBlurb": 4038,
"ViperBlurbMed": 4039,
"ViperBlurbLong": 4040,
"ViperDissolveTime": 4041,
"ViperAbility": 4042,
"StatsViperBodiesDissolved": 4043,
"RolesHelp_Viper_01": 4044,
"RolesHelp_Viper_02": 4045,
"QCWhereWasDead": 4060,
"QCInterrogateCrew": 4061,
"QCInterrogated": 4062,
"QCBodyDissolved": 4063,
"QCBodyVeryDissolved": 4064,
"QCBodyAlmostDissolved": 4065,
"QCDetectives": 4066,
"QCVipers": 4067,
"DetectiveNotesTitleVictim": 4068,
"DetectiveNotesTitleFoundDead": 4069,
"DetectiveNotesTitleKiller": 4070,
"DetectiveNotesTitleSuspects": 4071,
"DetectiveNotesTitleLocationsAtTimeOfDeath": 4072,
"DetectiveNotesPromptPreposition": 4073,
"DetectiveNotesPromptLocation": 4074,
"DetectiveNotesPromptImpostor": 4075,
"DetectiveNotesPromptSuspect": 4076,
"DetectiveNotesPrepositionIn": 4077,
"DetectiveNotesPrepositionBy": 4078,
"DetectiveNotesPrepositionMaybe": 4079,
"DetectiveNotesTitlePostIt": 4080,
"DetectiveNotesPostIt": 4081,
"DetectiveNotesSuspectNumber": 4082,
"AprilFoolsMode": 4083,
"MatchInfoGuideTitle": 4084,
"MatchInfoGuidePlayersLabel": 4085,
"MatchInfoGuideNoRolesMessage": 4086,
"JudgeRole": 4090,
"JudgeAbility": 4091,
"JudgeBlurb": 4092,
"JudgeBlurbMed": 4093,
"JudgeBlurbLong": 4094,
"JudgeTaskRequirement": 4095,
"RolesHelp_Judge_01": 4096,
"RolesHelp_Judge_02": 4097,
"JudgeAbilityUnlocked": 4098,
"JudgeAbilityProgress": 4099,
"JudgeCourtAdjourned": 4100,
"JudgeHasSpoken": 4101,
"StatsJudgeSuccessfulOverrules": 4102,
"StatsJudgeFailedOverrules": 4103,
"QCJudges": 4104,
"QCJudgeOverrule": 4105,
"QCJudgePlease": 4106,
"QCJudgeWillOverrule": 4107,
"JudgeBeatYouToIt": 4108
}
@@ -0,0 +1,4 @@
{
"Invalid": 0,
"Keywords": 1
}
+3 -3
View File
@@ -1,5 +1,5 @@
{
"dumpostorVersion": "1.0.0",
"gameVersion": "2024.3.5",
"dumpostorVersion": "1.0.1-dev",
"gameVersion": "2026.8.18",
"platformType": "StandaloneItch"
}
}
@@ -1,93 +1,93 @@
{
"0": {
"room": "Cafeteria",
"position": {
"x": 6.3936,
"y": 1.33848
}
},
"1": {
"room": "Storage",
"position": {
"x": 5.268,
"y": -14.2884
}
},
"2": {
"room": "UpperEngine",
"position": {
"x": 16.930801,
"y": -2.1276002
}
},
"3": {
"room": "Cafeteria",
"position": {
"x": 0.708,
"y": -4.992
}
},
"4": {
"room": "LowerEngine",
"position": {
"x": 14.6808,
"y": -11.472
}
},
"5": {
"room": "UpperEngine",
"position": {
"x": 14.676002,
"y": 1.3406401
}
},
"6": {
"room": "Security",
"position": {
"x": 14.734801,
"y": -5.1417603
}
},
"7": {
"room": "Storage",
"position": {
"x": -1.1316001,
"y": -11.98128
}
},
"8": {
"room": "Cafeteria",
"position": {
"x": -5.1432004,
"y": 1.3355999
}
},
"9": {
"1": {
"room": "Cafeteria",
"position": {
"x": 0.708,
"y": -4.992
}
},
"2": {
"room": "Cafeteria",
"position": {
"x": 6.3936,
"y": 1.33848
}
},
"3": {
"room": "Electrical",
"position": {
"x": 9.53616,
"y": -13.416001
}
},
"10": {
"room": "MedBay",
"4": {
"room": "Storage",
"position": {
"x": 9.1452,
"y": -0.4439999
"x": -1.1316001,
"y": -11.98128
}
},
"11": {
"5": {
"room": "Storage",
"position": {
"x": 0.70919997,
"y": -8.560801
}
},
"6": {
"room": "Storage",
"position": {
"x": 5.268,
"y": -14.2884
}
},
"7": {
"room": "LowerEngine",
"position": {
"x": 14.6808,
"y": -11.472
}
},
"8": {
"room": "LowerEngine",
"position": {
"x": 16.930801,
"y": -8.9664
}
},
"12": {
"room": "Storage",
"9": {
"room": "UpperEngine",
"position": {
"x": 0.70919997,
"y": -8.560801
"x": 14.676002,
"y": 1.3406401
}
},
"10": {
"room": "UpperEngine",
"position": {
"x": 16.930801,
"y": -2.1276002
}
},
"11": {
"room": "Security",
"position": {
"x": 14.734801,
"y": -5.1417603
}
},
"12": {
"room": "MedBay",
"position": {
"x": 9.1452,
"y": -0.4439999
}
}
}
@@ -1,11 +1,11 @@
{
"initialSpawnCenter": {
"x": -0.72,
"y": 0.62
"x": 0.87,
"y": 0.69
},
"meetingSpawnCenter": {
"x": -0.72,
"y": 0.62
"x": 0.87,
"y": 0.69
},
"meetingSpawnCenter2": {
"x": 0,
@@ -476,5 +476,138 @@
"usableDistance": 1
}
]
},
"29": {
"type": "NormalPlayerTask",
"taskType": "VentCleaning",
"length": "Short",
"consoles": [
{
"id": 0,
"room": "Admin",
"position": {
"x": -2.544,
"y": -9.955201
},
"usableDistance": 1
},
{
"id": 2,
"room": "Cafeteria",
"position": {
"x": -4.2588,
"y": -0.27600002
},
"usableDistance": 1
},
{
"id": 13,
"room": "Nav",
"position": {
"x": -16.008001,
"y": -6.3840003
},
"usableDistance": 1
},
{
"id": 12,
"room": "Nav",
"position": {
"x": -16.008001,
"y": -3.1680002
},
"usableDistance": 1
},
{
"id": 7,
"room": "Weapons",
"position": {
"x": -8.820001,
"y": 3.3240001
},
"usableDistance": 1
},
{
"id": 10,
"room": "Shields",
"position": {
"x": -9.5232,
"y": -14.337601
},
"usableDistance": 1
},
{
"id": 1,
"room": "Hallway",
"position": {
"x": -9.384,
"y": -6.438
},
"usableDistance": 1
},
{
"id": 3,
"room": "Electrical",
"position": {
"x": 9.7764,
"y": -8.034
},
"usableDistance": 1
},
{
"id": 8,
"room": "Reactor",
"position": {
"x": 20.796001,
"y": -6.9528003
},
"usableDistance": 1
},
{
"id": 11,
"room": "Reactor",
"position": {
"x": 21.876,
"y": -3.0516002
},
"usableDistance": 1
},
{
"id": 9,
"room": "LowerEngine",
"position": {
"x": 15.2508,
"y": -13.656001
},
"usableDistance": 1
},
{
"id": 4,
"room": "UpperEngine",
"position": {
"x": 15.288,
"y": 2.52
},
"usableDistance": 1
},
{
"id": 5,
"room": "Security",
"position": {
"x": 12.534,
"y": -6.9492
},
"usableDistance": 1
},
{
"id": 6,
"room": "MedBay",
"position": {
"x": 10.608001,
"y": -4.176
},
"usableDistance": 1
}
]
}
}
@@ -1,6 +1,6 @@
{
"0": {
"name": "AdminVent",
"name": "AdminVent (1)",
"position": {
"x": -2.544,
"y": -9.955201
@@ -10,7 +10,7 @@
"right": 1
},
"1": {
"name": "BigYVent",
"name": "BigYVent (1)",
"position": {
"x": -9.384,
"y": -6.438
@@ -20,7 +20,7 @@
"right": 2
},
"2": {
"name": "CafeVent",
"name": "CafeVent (1)",
"position": {
"x": -4.2588,
"y": -0.27600002
@@ -30,7 +30,7 @@
"right": 1
},
"3": {
"name": "ElecVent",
"name": "ElecVent (1)",
"position": {
"x": 9.7764,
"y": -8.034
@@ -40,7 +40,7 @@
"right": 6
},
"4": {
"name": "LEngineVent",
"name": "LEngineVent (1)",
"position": {
"x": 15.288,
"y": 2.52
@@ -50,7 +50,7 @@
"right": null
},
"5": {
"name": "LifeSuppVent",
"name": "SecurityVent",
"position": {
"x": 12.534,
"y": -6.9492
@@ -60,7 +60,7 @@
"right": 3
},
"6": {
"name": "MedVent",
"name": "MedVent (1)",
"position": {
"x": 10.608001,
"y": -4.176
@@ -70,7 +70,7 @@
"right": 3
},
"7": {
"name": "WeaponsVent",
"name": "WeaponsVent (1)",
"position": {
"x": -8.820001,
"y": 3.3240001
@@ -80,7 +80,7 @@
"right": 12
},
"8": {
"name": "ReactorVent",
"name": "ReactorVent (1)",
"position": {
"x": 20.796001,
"y": -6.9528003
@@ -90,7 +90,7 @@
"right": null
},
"9": {
"name": "REngineVent",
"name": "REngineVent (1)",
"position": {
"x": 15.2508,
"y": -13.656001
@@ -100,7 +100,7 @@
"right": null
},
"10": {
"name": "ShieldsVent",
"name": "ShieldsVent (1)",
"position": {
"x": -9.5232,
"y": -14.337601
@@ -110,7 +110,7 @@
"right": null
},
"11": {
"name": "UpperReactorVent",
"name": "UpperReactorVent (1)",
"position": {
"x": 21.876,
"y": -3.0516002
@@ -120,7 +120,7 @@
"right": null
},
"12": {
"name": "NavVentNorth",
"name": "NavVentNorth (1)",
"position": {
"x": -16.008001,
"y": -3.1680002
@@ -130,7 +130,7 @@
"right": 7
},
"13": {
"name": "NavVentSouth",
"name": "NavVentSouth (1)",
"position": {
"x": -16.008001,
"y": -6.3840003
@@ -0,0 +1,37 @@
{
"hatIds": [
"hat_pk04_beanie",
"hat_pk05_Cheese",
"hat_pk05_Cherry",
"hat_Chocolate",
"hat_Zipper",
"hat_pk02_Crown",
"hat_babybean",
"hat_Sorry",
"hat_mareLwyd",
"hat_fishingHat",
"hat_schnapp",
"hat_pk03_Traffic",
"hat_Pot",
"hat_AbominalHat",
"hat_tombstone",
"hat_pk04_MiniCrewmate",
"hat_pk05_Wizardhat",
"hat_pk05_Ellryhat",
"hat_pk05_EllieToppat"
],
"visorIds": [
"visor_pk01_AngeryVisor",
"visor_pk01_DumStickerVisor",
"visor_vr_Vr-Black",
"visor_pk01_PaperMaskVisor",
"visor_wash"
],
"petIds": [
"pet_YuleGoatPet",
"pet_HamPet",
"pet_Lava",
"pet_Snow",
"pet_Bush"
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class BoolGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "bool";
[JsonPropertyName("AcceptedValues")]
public required List<bool> AcceptedValues { get; set; }
[JsonPropertyName("OptionEnum")]
public required BoolOptionNames OptionEnum { get; set; }
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class CategorizedGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "cat";
[JsonPropertyName("AcceptedValues")]
public required List<int> AcceptedValues { get; set; }
[JsonPropertyName("OptionEnum")]
public required CategorizedOptionNames OptionEnum { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class ChatModeGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "chat";
[JsonPropertyName("AcceptedValues")]
public required byte AcceptedValues { get; set; }
}
}
@@ -0,0 +1,87 @@
using System;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class GameFilter
{
[JsonConstructor]
public GameFilter(string optionType, string key, string subFilterString)
{
this.OptionType = optionType;
this.Key = key;
this.SubFilterString = subFilterString;
this.SubFilter = ResolveSubFilter(OptionType, SubFilterString);
}
[JsonIgnore]
public ISubFilter SubFilter { get; set; }
[JsonPropertyName("OptionType")]
public required string OptionType { get; set; }
[JsonPropertyName("Key")]
public required string Key { get; set; }
[JsonPropertyName("SubFilterString")]
public required string SubFilterString { get; set; }
public void ModifySubFilter(ISubFilter subFilter)
{
this.SubFilter = subFilter;
}
[OnSerializing]
internal void OnSerializing(StreamingContext context)
{
this.SubFilterString = JsonSerializer.Serialize(this.SubFilter);
}
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
this.SubFilter = this.ResolveSubFilter(this.OptionType, this.SubFilterString);
}
private ISubFilter ResolveSubFilter(string type, string filterString)
{
if (type != null)
{
switch (type.ToLower())
{
case "int":
return JsonSerializer.Deserialize<IntGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for IntGameFilter");
case "platform":
return JsonSerializer.Deserialize<PlatformGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for PlatformGameFilter");
case "cat":
return JsonSerializer.Deserialize<CategorizedGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for CategorizedGameFilter");
case "languages":
return JsonSerializer.Deserialize<LanguageFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for LanguageFilter");
case "chat":
return JsonSerializer.Deserialize<ChatModeGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for ChatModeGameFilter");
case "map":
return JsonSerializer.Deserialize<MapGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for MapGameFilter");
case "bool":
return JsonSerializer.Deserialize<BoolGameFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for BoolGameFilter");
case "mod":
return JsonSerializer.Deserialize<ModFilter>(filterString)
?? throw new InvalidOperationException("Deserialization returned null for ModFilter");
default:
throw new InvalidOperationException("No type matches subfilter");
}
}
throw new InvalidOperationException("Must provide type for sub filter");
}
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class GameFilterSet
{
[JsonConstructor]
public GameFilterSet(GameModes gameMode, List<GameFilter> filters)
{
GameMode = gameMode;
Filters = filters;
}
[JsonPropertyName("GameMode")]
public required GameModes GameMode { get; set; }
[JsonPropertyName("Filters")]
public required List<GameFilter> Filters { get; set; }
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class GameFiltersList
{
[JsonPropertyName("FilterSets")]
public required List<GameFilterSet> FilterSets { get; set; }
}
}
@@ -0,0 +1,7 @@
namespace Impostor.Api.Innersloth.GameFilters
{
public interface ISubFilter
{
string FilterType { get; }
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class IntGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "int";
[JsonPropertyName("AcceptedValues")]
public required List<int> AcceptedValues { get; set; }
[JsonPropertyName("OptionEnum")]
public required Int32OptionNames OptionEnum { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class LanguageFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "languages";
[JsonPropertyName("AcceptedValues")]
public required uint AcceptedValues { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class MapGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "map";
[JsonPropertyName("AcceptedValues")]
public required byte AcceptedValues { get; set; }
}
}
@@ -0,0 +1,16 @@
using System;
using System.Text.Json.Serialization;
// https://github.com/Innersloth-LLC/AmongUsModdingInformation
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class ModFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "mod";
[JsonPropertyName("AcceptedValues")]
public required Guid AcceptedValues { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Text.Json.Serialization;
namespace Impostor.Api.Innersloth.GameFilters
{
[Serializable]
public class PlatformGameFilter : ISubFilter
{
[JsonPropertyName("FilterType")]
public string FilterType { get; } = "platform";
[JsonPropertyName("AcceptedValues")]
public required uint AcceptedValues { get; set; }
}
}
@@ -0,0 +1,14 @@
namespace Impostor.Api.Innersloth.GameOptions;
// TODO: generate this enum based on game data
/// <summary>
/// Tag that can be added to indicate the expected skill level of players in this lobby.
/// </summary>
public enum GameTags
{
None = 0,
Beginner = 1,
Intermediate = 2,
Expert = 3,
}
@@ -2,7 +2,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
public class HideNSeekGameOptions : IGameOptions
{
public const int LatestVersion = 7;
public const int LatestVersion = 11;
public HideNSeekGameOptions(byte version = LatestVersion)
{
@@ -16,6 +16,12 @@ public class HideNSeekGameOptions : IGameOptions
/// <inheritdoc />
public GameModes GameMode => GameModes.HideNSeek;
/// <inheritdoc />
public SpecialGameModes SpecialMode { get; set; } = SpecialGameModes.None;
/// <inheritdoc />
public RulesPresets RulesPreset { get; set; } = RulesPresets.Custom;
/// <inheritdoc />
public byte MaxPlayers { get; set; } = 15;
@@ -87,6 +93,11 @@ public class HideNSeekGameOptions : IGameOptions
public float MaxPingTime { get; set; } = 6f;
/// <summary>
/// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert.
/// </summary>
public GameTags Tag { get; set; } = 0;
public static HideNSeekGameOptions Deserialize(IMessageReader reader, byte version)
{
var options = new HideNSeekGameOptions(version);
@@ -96,6 +107,17 @@ public class HideNSeekGameOptions : IGameOptions
public void Deserialize(IMessageReader reader)
{
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
}
if (Version >= 8)
{
SpecialMode = (SpecialGameModes)reader.ReadByte();
RulesPreset = (RulesPresets)reader.ReadByte();
}
MaxPlayers = reader.ReadByte();
Keywords = (GameKeywords)reader.ReadInt32();
Map = (MapTypes)reader.ReadByte();
@@ -121,14 +143,20 @@ public class HideNSeekGameOptions : IGameOptions
MaxPingTime = reader.ReadSingle();
CrewmateTimeInVent = reader.ReadSingle();
if (Version > 7)
if (Version >= 9)
{
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
Tag = (GameTags)reader.ReadByte();
}
}
public void Serialize(IMessageWriter writer)
{
if (Version >= 8)
{
writer.Write((byte)SpecialMode);
writer.Write((byte)RulesPreset);
}
writer.Write(MaxPlayers);
writer.Write((uint)Keywords);
writer.Write((byte)Map);
@@ -154,7 +182,12 @@ public class HideNSeekGameOptions : IGameOptions
writer.Write(MaxPingTime);
writer.Write(CrewmateTimeInVent);
if (Version > 7)
if (Version >= 9)
{
writer.Write((byte)Tag);
}
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
}
@@ -10,10 +10,20 @@ public interface IGameOptions
public byte Version { get; }
/// <summary>
/// Gets the currently active gamemode.
/// Gets the currently active gamemode. This is currently used for the Normal and HideAndSeek gamemodes.
/// </summary>
public GameModes GameMode { get; }
/// <summary>
/// Gets the currently active special gamemode. This is currently used for the AprilFools gamemode.
/// </summary>
public SpecialGameModes SpecialMode { get; }
/// <summary>
/// Gets the rule preset.
/// </summary>
public RulesPresets RulesPreset { get; }
/// <summary>
/// Gets or sets the maximum amount of players for this lobby.
/// </summary>
@@ -39,6 +49,8 @@ public interface IGameOptions
/// </summary>
public bool IsDefaults { get; set; }
public GameTags Tag { get; set; }
public void Serialize(IMessageWriter writer);
public static void EnsureVersionIsModular<TCaller>(byte version)
@@ -21,6 +21,12 @@ public class LegacyGameOptionsData : IGameOptions
public GameModes GameMode => GameModes.Normal;
/// <inheritdoc />
public SpecialGameModes SpecialMode { get; } = SpecialGameModes.None;
/// <inheritdoc />
public RulesPresets RulesPreset { get; } = RulesPresets.Custom;
/// <summary>
/// Gets or sets the maximum amount of players for this lobby.
/// </summary>
@@ -139,6 +145,8 @@ public class LegacyGameOptionsData : IGameOptions
/// </summary>
public bool IsDefaults { get; set; } = true;
public GameTags Tag { get; set; } = 0;
public static LegacyGameOptionsData Deserialize(IMessageReader reader, byte version)
{
var options = new LegacyGameOptionsData(version);
@@ -4,7 +4,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
public class NormalGameOptions : IGameOptions
{
public const int LatestVersion = 7;
public const int LatestVersion = 11;
public NormalGameOptions(byte version = LatestVersion)
{
@@ -19,6 +19,12 @@ public class NormalGameOptions : IGameOptions
/// <inheritdoc />
public GameModes GameMode => GameModes.Normal;
/// <inheritdoc />
public SpecialGameModes SpecialMode { get; set; } = SpecialGameModes.None;
/// <inheritdoc />
public RulesPresets RulesPreset { get; set; } = RulesPresets.Custom;
/// <inheritdoc />
public byte MaxPlayers { get; set; } = 10;
@@ -122,6 +128,11 @@ public class NormalGameOptions : IGameOptions
/// </summary>
public TaskBarUpdate TaskBarUpdate { get; set; } = TaskBarUpdate.Always;
/// <summary>
/// Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert.
/// </summary>
public GameTags Tag { get; set; } = 0;
public RoleOptionsCollection RoleOptions { get; set; }
public static NormalGameOptions Deserialize(IMessageReader reader, byte version)
@@ -133,6 +144,17 @@ public class NormalGameOptions : IGameOptions
public void Deserialize(IMessageReader reader)
{
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
}
if (Version >= 8)
{
SpecialMode = (SpecialGameModes)reader.ReadByte();
RulesPreset = (RulesPresets)reader.ReadByte();
}
MaxPlayers = reader.ReadByte();
Keywords = (GameKeywords)reader.ReadUInt32();
Map = (MapTypes)reader.ReadByte();
@@ -161,16 +183,22 @@ public class NormalGameOptions : IGameOptions
AnonymousVotes = reader.ReadBoolean();
TaskBarUpdate = (TaskBarUpdate)reader.ReadByte();
RoleOptions.Deserialize(reader);
if (Version > 7)
if (Version >= 9)
{
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
Tag = (GameTags)reader.ReadByte();
}
RoleOptions.Deserialize(reader);
}
public void Serialize(IMessageWriter writer)
{
if (Version >= 8)
{
writer.Write((byte)SpecialMode);
writer.Write((byte)RulesPreset);
}
writer.Write(MaxPlayers);
writer.Write((uint)Keywords);
writer.Write((byte)Map);
@@ -199,9 +227,14 @@ public class NormalGameOptions : IGameOptions
writer.Write(AnonymousVotes);
writer.Write((byte)TaskBarUpdate);
if (Version >= 9)
{
writer.Write((byte)Tag);
}
RoleOptions.Serialize(writer);
if (Version > 7)
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
}
@@ -0,0 +1,29 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class DetectiveRoleOptions : IRoleOptions
{
public DetectiveRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Detective;
public byte DetectiveSuspectLimit { get; set; } = 3;
public static DetectiveRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new DetectiveRoleOptions(version);
options.DetectiveSuspectLimit = reader.ReadByte();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write((byte)DetectiveSuspectLimit);
}
}
@@ -0,0 +1,29 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class JudgeRoleOptions : IRoleOptions
{
public JudgeRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Judge;
public float JudgeTaskRequirementPercentage { get; set; } = 50f;
public static JudgeRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new JudgeRoleOptions(version);
options.JudgeTaskRequirementPercentage = (int)reader.ReadByte();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write((byte)JudgeTaskRequirementPercentage);
}
}
@@ -0,0 +1,33 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class NoisemakerRoleOptions : IRoleOptions
{
public NoisemakerRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Noisemaker;
public bool ImpostorAlert { get; set; } = true;
public byte AlertDuration { get; set; } = 10;
public static NoisemakerRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new NoisemakerRoleOptions(version);
options.AlertDuration = reader.ReadByte();
options.ImpostorAlert = reader.ReadBoolean();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write(AlertDuration);
writer.Write(ImpostorAlert);
}
}
@@ -0,0 +1,33 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class PhantomRoleOptions : IRoleOptions
{
public PhantomRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Phantom;
public byte Cooldown { get; set; } = 15;
public byte Duration { get; set; } = 30;
public static PhantomRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new PhantomRoleOptions(version);
options.Cooldown = reader.ReadByte();
options.Duration = reader.ReadByte();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write(Cooldown);
writer.Write(Duration);
}
}
@@ -16,8 +16,6 @@ public class RoleOptionsCollection
public void Deserialize(IMessageReader reader)
{
Roles.Clear();
var count = reader.ReadPackedInt32();
Roles.EnsureCapacity(count);
for (var i = 0; i < count; i++)
@@ -31,10 +29,16 @@ public class RoleOptionsCollection
RoleTypes.Engineer => EngineerRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.GuardianAngel => GuardianAngelRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Shapeshifter => ShapeshifterRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Noisemaker => NoisemakerRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Phantom => PhantomRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Tracker => TrackerRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Detective => DetectiveRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Viper => ViperRoleOptions.Deserialize(roleOptionsReader, Version),
RoleTypes.Judge => JudgeRoleOptions.Deserialize(roleOptionsReader, Version),
_ => throw new ArgumentOutOfRangeException(nameof(roleType), roleType, null),
};
Roles.Add(roleType, new RoleData(roleType, roleOptions, roleRate));
Roles[roleType] = new RoleData(roleType, roleOptions, roleRate);
}
}
@@ -0,0 +1,37 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class TrackerRoleOptions : IRoleOptions
{
public TrackerRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Tracker;
public byte Cooldown { get; set; } = 15;
public byte Duration { get; set; } = 30;
public byte Delay { get; set; } = 1;
public static TrackerRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new TrackerRoleOptions(version);
options.Cooldown = reader.ReadByte();
options.Duration = reader.ReadByte();
options.Delay = reader.ReadByte();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write(Cooldown);
writer.Write(Duration);
writer.Write(Delay);
}
}
@@ -0,0 +1,29 @@
namespace Impostor.Api.Innersloth.GameOptions.RoleOptions;
public class ViperRoleOptions : IRoleOptions
{
public ViperRoleOptions(byte version)
{
Version = version;
}
public byte Version { get; }
public RoleTypes Type => RoleTypes.Viper;
public byte ViperDissolveTime { get; set; } = 15;
public static ViperRoleOptions Deserialize(IMessageReader reader, byte version)
{
var options = new ViperRoleOptions(version);
options.ViperDissolveTime = reader.ReadByte();
return options;
}
public void Serialize(IMessageWriter writer)
{
writer.Write((byte)ViperDissolveTime);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Impostor.Api.Net
/// <summary>
/// Represents a connected game client.
/// </summary>
public interface IClient
public interface IClient : IEquatable<IClient>
{
/// <summary>
/// Gets or sets the unique ID of the client.
@@ -1,6 +1,6 @@
namespace Impostor.Api.Net.Inner.Objects
{
public interface IInnerGameData : IInnerNetObject
public interface IInnerGameData
{
}
}
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Impostor.Api.Events.Player;
@@ -6,6 +6,24 @@ namespace Impostor.Api.Net.Inner.Objects
{
public interface IInnerMeetingHud : IInnerNetObject
{
public interface IJudgeOverrule
{
/// <summary>
/// Gets the player id of the Judge that queued this overrule.
/// </summary>
byte JudgePlayerId { get; }
/// <summary>
/// Gets the player id of the player the Judge wants to overrule.
/// </summary>
byte OverruledPlayerId { get; }
/// <summary>
/// Gets the overrule nonce used to match this overrule to the voting result.
/// </summary>
ushort OverruleNonce { get; }
}
public interface IPlayerVoteArea
{
/// <summary>
@@ -55,5 +73,10 @@ namespace Impostor.Api.Net.Inner.Objects
/// Gets the player that started the meeting.
/// </summary>
IInnerPlayerInfo? Reporter { get; }
/// <summary>
/// Gets the Judge overrules queued in this meeting.
/// </summary>
IReadOnlyCollection<IJudgeOverrule> JudgeOverrules { get; }
}
}
@@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.Customization;
@@ -28,13 +29,14 @@ namespace Impostor.Api.Net.Inner.Objects
/// Gets the <see cref="IInnerPlayerInfo" /> of the <see cref="IInnerPlayerControl" />.
/// Contains metadata about the player.
/// </summary>
IInnerPlayerInfo PlayerInfo { get; }
IInnerPlayerInfo? PlayerInfo { get; }
/// <summary>
/// Sets the name of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="name">A name for the player.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask SetNameAsync(string name);
@@ -43,6 +45,7 @@ namespace Impostor.Api.Net.Inner.Objects
/// Visible to all players.
/// </summary>
/// <param name="colorType">A color for the player.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask SetColorAsync(ColorType colorType);
@@ -51,6 +54,7 @@ namespace Impostor.Api.Net.Inner.Objects
/// Visible to all players.
/// </summary>
/// <param name="hatId">An hat for the player.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask SetHatAsync(string hatId);
@@ -59,6 +63,7 @@ namespace Impostor.Api.Net.Inner.Objects
/// Visible to all players.
/// </summary>
/// <param name="petId">A pet for the player.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask SetPetAsync(string petId);
@@ -67,9 +72,26 @@ namespace Impostor.Api.Net.Inner.Objects
/// Visible to all players.
/// </summary>
/// <param name="skinId">A skin for the player.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask SetSkinAsync(string skinId);
/// <summary>
/// Sets the visor of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="visorId">A visor for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetVisorAsync(string visorId);
/// <summary>
/// Sets the nameplate of the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
/// </summary>
/// <param name="nameplateId">A nameplate for the player.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask SetNamePlateAsync(string nameplateId);
/// <summary>
/// Send a chat message as the current <see cref="IInnerPlayerControl" />.
/// Visible to all players.
@@ -98,32 +120,60 @@ namespace Impostor.Api.Net.Inner.Objects
/// <exception cref="ImpostorProtocolException">Thrown when player is not the impostor.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player is dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when target is dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player or target player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask MurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result);
/// <summary>
/// Murder <paramref name="target" /> player successfully.
/// Murder <paramref name="target" /> player.
/// </summary>
/// <param name="target">Target player to murder.</param>
/// <param name="result">The result of the murder operation.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask ForceMurderPlayerAsync(IInnerPlayerControl target, MurderResultFlags result);
/// <summary>
/// Murder <paramref name="target" /> player after validating if this is a valid kill.
/// </summary>
/// <param name="target">Target player to murder.</param>
/// <exception cref="ImpostorProtocolException">Thrown when player is not the impostor.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player is dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when target is dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player or target player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
[Obsolete("Please switch to version with the MurderResultFlags argument")]
ValueTask MurderPlayerAsync(IInnerPlayerControl target);
/// <summary>
/// Protect <paramref name="target" /> player.
/// </summary>
/// <param name="target">Target player to protect.</param>
/// <exception cref="ImpostorProtocolException">Thrown when target is a guardian angel.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when target is dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player or target player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask ProtectPlayerAsync(IInnerPlayerControl target);
/// <summary>
/// Protect <paramref name="target" /> player.
/// </summary>
/// <param name="target">Target player to protect.</param>
/// <returns>Task that must be awaited.</returns>
ValueTask ForceProtectPlayerAsync(IInnerPlayerControl target);
/// <summary>
/// Exile the current player. This doesn't produce a body to be reported.
/// Visible to all players.
/// </summary>
/// <exception cref="ImpostorProtocolException">Thrown if player to be exiled is already dead.</exception>
/// <exception cref="ImpostorProtocolException">Thrown when player or target player doesn't have a PlayerInfo.</exception>
/// <returns>Task that must be awaited.</returns>
ValueTask ExileAsync();
/// <summary>
/// Exile the current player. This doesn't produce a body to be reported.
/// Visible to all players.
/// </summary>
/// <returns>Task that must be awaited.</returns>
ValueTask ExileAsync();
ValueTask ForceExileAsync();
}
}
@@ -1,4 +1,4 @@
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.GameOptions;
namespace Impostor.Api.Net.Messages.C2S
@@ -0,0 +1,25 @@
using System;
using Impostor.Api.Innersloth;
using Impostor.Api.Innersloth.GameOptions;
namespace Impostor.Api.Net.Messages.C2S
{
/// <summary>
/// Special serialization rules for AU MCI HostGame messages.
/// <see href="https://github.com/Innersloth-LLC/AmongUsModdingInformation?tab=readme-ov-file#all-client-mod-client-identification"/>
/// for more information.
/// </summary>
public static class Message25HostModdedGameC2S
{
public static void Serialize(IMessageWriter writer, IGameOptions gameOptions, CrossplayFlags crossplayFlags, GameFilterOptions gameFilterOptions)
{
Message00HostGameC2S.Serialize(writer, gameOptions, crossplayFlags, gameFilterOptions);
}
public static void Deserialize(IMessageReader reader, out IGameOptions gameOptions, out CrossplayFlags crossplayFlags, out GameFilterOptions gameFilterOptions, out Guid modGuid)
{
Message00HostGameC2S.Deserialize(reader, out gameOptions, out crossplayFlags, out gameFilterOptions);
modGuid = new Guid(reader.ReadBytes(16).Span);
}
}
}
+10 -2
View File
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -28,6 +28,9 @@ namespace Impostor.Api.Net.Messages
public const byte SetActivePodType = 21;
public const byte QueryPlatformIds = 22;
public const byte QueryLobbyInfo = 23;
public const byte EndGameHostMigration = 24;
public const byte HostModdedGame = 25;
public const byte PackedGameDataTo = 26;
private static readonly Dictionary<byte, string> FlagCache;
@@ -39,9 +42,14 @@ namespace Impostor.Api.Net.Messages
.ToDictionary(x => (byte)x.GetValue(null)!, y => y.Name);
}
/// <summary>
/// Convert a MessageFlag to the corresponding string.
/// </summary>
/// <param name="flag">The MessageFlag to convert.</param>
/// <returns>A string corresponding with the value of flag.</returns>
public static string FlagToString(byte flag)
{
return FlagCache[flag];
return FlagCache.TryGetValue(flag, out var res) ? res : $"Unknown Flag {flag}";
}
}
}
@@ -2,13 +2,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc06SetName
{
public static void Serialize(IMessageWriter writer, string name)
public static void Serialize(IMessageWriter writer, uint netId, string name)
{
writer.Write(netId);
writer.Write(name);
}
public static void Deserialize(IMessageReader reader, out string name)
public static void Deserialize(IMessageReader reader, out uint netId, out string name)
{
netId = reader.ReadUInt32();
name = reader.ReadString();
}
}
@@ -4,13 +4,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc08SetColor
{
public static void Serialize(IMessageWriter writer, ColorType color)
public static void Serialize(IMessageWriter writer, uint netId, ColorType color)
{
writer.Write(netId);
writer.Write((byte)color);
}
public static void Deserialize(IMessageReader reader, out ColorType color)
public static void Deserialize(IMessageReader reader, out uint netId, out ColorType color)
{
netId = reader.ReadUInt32();
color = (ColorType)reader.ReadByte();
}
}
@@ -2,6 +2,8 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc23VotingComplete
{
// Pre 18.0 version
// TODO clean up when 17.4 and below are unsupported
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie)
{
writer.WriteBytesAndSize(states);
@@ -9,6 +11,15 @@ namespace Impostor.Api.Net.Messages.Rpcs
writer.Write(tie);
}
public static void Serialize(IMessageWriter writer, byte[] states, byte playerId, bool tie, bool wasOverruled, ushort overrideId)
{
writer.WriteBytesAndSize(states);
writer.Write(playerId);
writer.Write(tie);
writer.Write(wasOverruled);
writer.Write(overrideId);
}
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie)
{
var length = reader.ReadPackedInt32();
@@ -21,5 +32,21 @@ namespace Impostor.Api.Net.Messages.Rpcs
playerId = reader.ReadByte();
tie = reader.ReadBoolean();
}
public static void Deserialize(IMessageReader reader, out IMessageReader[] states, out byte playerId, out bool tie, out bool wasOverruled, out ushort overrideId)
{
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();
wasOverruled = reader.ReadBoolean();
overrideId = reader.ReadUInt16();
}
}
}
@@ -4,15 +4,13 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc29SetTasks
{
public static void Serialize(IMessageWriter writer, byte playerId, ReadOnlyMemory<byte> taskTypeIds)
public static void Serialize(IMessageWriter writer, ReadOnlyMemory<byte> taskTypeIds)
{
writer.Write(playerId);
writer.Write(taskTypeIds);
}
public static void Deserialize(IMessageReader reader, out byte playerId, out ReadOnlyMemory<byte> taskTypeIds)
public static void Deserialize(IMessageReader reader, out ReadOnlyMemory<byte> taskTypeIds)
{
playerId = reader.ReadByte();
taskTypeIds = reader.ReadBytesAndSize();
}
}
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc39SetHatStr
{
public static void Serialize(IMessageWriter writer, string hat)
public static void Serialize(IMessageWriter writer, string hat, byte nextRpcSequenceId)
{
writer.Write(hat);
writer.Write(nextRpcSequenceId);
}
public static void Deserialize(IMessageReader reader, out string hat)
public static void Deserialize(IMessageReader reader, out string hat, out byte nextRpcSequenceId)
{
hat = reader.ReadString();
nextRpcSequenceId = reader.ReadByte();
}
}
}
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc40SetSkinStr
{
public static void Serialize(IMessageWriter writer, string skin)
public static void Serialize(IMessageWriter writer, string skin, byte nextRpcSequenceId)
{
writer.Write(skin);
writer.Write(nextRpcSequenceId);
}
public static void Deserialize(IMessageReader reader, out string skin)
public static void Deserialize(IMessageReader reader, out string skin, out byte nextRpcSequenceId)
{
skin = reader.ReadString();
nextRpcSequenceId = reader.ReadByte();
}
}
}
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc41SetPetStr
{
public static void Serialize(IMessageWriter writer, string pet)
public static void Serialize(IMessageWriter writer, string pet, byte nextRpcSequenceId)
{
writer.Write(pet);
writer.Write(nextRpcSequenceId);
}
public static void Deserialize(IMessageReader reader, out string pet)
public static void Deserialize(IMessageReader reader, out string pet, out byte nextRpcSequenceId)
{
pet = reader.ReadString();
nextRpcSequenceId = reader.ReadByte();
}
}
}
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc42SetVisorStr
{
public static void Serialize(IMessageWriter writer, string visor)
public static void Serialize(IMessageWriter writer, string visor, byte nextRpcSequenceId)
{
writer.Write(visor);
writer.Write(nextRpcSequenceId);
}
public static void Deserialize(IMessageReader reader, out string visor)
public static void Deserialize(IMessageReader reader, out string visor, out byte nextRpcSequenceId)
{
visor = reader.ReadString();
nextRpcSequenceId = reader.ReadByte();
}
}
}
@@ -2,14 +2,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc43SetNamePlateStr
{
public static void Serialize(IMessageWriter writer, string namePlate)
public static void Serialize(IMessageWriter writer, string namePlate, byte nextRpcSequenceId)
{
writer.Write(namePlate);
writer.Write(nextRpcSequenceId);
}
public static void Deserialize(IMessageReader reader, out string namePlate)
public static void Deserialize(IMessageReader reader, out string namePlate, out byte nextRpcSequenceId)
{
namePlate = reader.ReadString();
nextRpcSequenceId = reader.ReadByte();
}
}
}
@@ -4,14 +4,16 @@ namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc44SetRole
{
public static void Serialize(IMessageWriter writer, RoleTypes role)
public static void Serialize(IMessageWriter writer, RoleTypes role, bool canOverrideRole)
{
writer.Write((ushort)role);
writer.Write(canOverrideRole);
}
public static void Deserialize(IMessageReader reader, out RoleTypes role)
public static void Deserialize(IMessageReader reader, out RoleTypes role, out bool canOverrideRole)
{
role = (RoleTypes)reader.ReadUInt16();
canOverrideRole = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc62CheckVanish
{
public static void Serialize(IMessageWriter writer, float maxDuration)
{
writer.Write(maxDuration);
}
public static void Deserialize(IMessageReader reader, out float maxDuration)
{
maxDuration = reader.ReadSingle();
}
}
}
@@ -0,0 +1,13 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc63StartVanish
{
public static void Serialize(IMessageWriter writer)
{
}
public static void Deserialize(IMessageReader reader)
{
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc64CheckAppear
{
public static void Serialize(IMessageWriter writer, bool shouldAnimate)
{
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, out bool shouldAnimate)
{
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,15 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc65StartAppear
{
public static void Serialize(IMessageWriter writer, bool shouldAnimate)
{
writer.Write(shouldAnimate);
}
public static void Deserialize(IMessageReader reader, out bool shouldAnimate)
{
shouldAnimate = reader.ReadBoolean();
}
}
}
@@ -0,0 +1,19 @@
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc66QueueOverruleVotes
{
public static void Serialize(IMessageWriter writer, byte judgePlayerId, byte targetPlayerId, ushort overruleNonce)
{
writer.Write(judgePlayerId);
writer.Write(targetPlayerId);
writer.Write(overruleNonce);
}
public static void Deserialize(IMessageReader reader, out byte judgePlayerId, out byte targetPlayerId, out ushort overruleNonce)
{
judgePlayerId = reader.ReadByte();
targetPlayerId = reader.ReadByte();
overruleNonce = reader.ReadUInt16();
}
}
}
@@ -18,7 +18,7 @@ namespace Impostor.Api.Net.Messages.S2C
writer.Write(hostId);
writer.Write(player.Client.Name);
player.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(player.Character?.PlayerInfo.PlayerLevel ?? 1);
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);
@@ -22,7 +22,7 @@ namespace Impostor.Api.Net.Messages.S2C
writer.WritePacked(ply.Client.Id);
writer.Write(ply.Client.Name);
ply.Client.PlatformSpecificData.Serialize(writer);
writer.WritePacked(ply.Character?.PlayerInfo.PlayerLevel ?? 1);
writer.WritePacked(ply.Character?.PlayerInfo?.PlayerLevel ?? 1);
// ProductUserId and FriendCode are not yet known, so set them to an empty string
writer.Write(string.Empty);
@@ -0,0 +1,21 @@
using Impostor.Api.Games;
namespace Impostor.Api.Net.Messages.S2C
{
public class Message24EndGameHostMigrationS2C
{
public static void Serialize(IMessageWriter writer, GameCode gameCode, int hostClientId)
{
writer.StartMessage(MessageFlags.EndGameHostMigration);
gameCode.Serialize(writer);
writer.Write(hostClientId);
writer.EndMessage();
}
public static void Deserialize(IMessageReader reader, out GameCode gameCode, out int hostClientId)
{
gameCode = reader.ReadInt32();
hostClientId = reader.ReadInt32();
}
}
}
+11 -4
View File
@@ -4,9 +4,9 @@
"net8.0": {
"Impostor.Hazel.Abstractions": {
"type": "Direct",
"requested": "[1.0.0, )",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"requested": "[1.0.1, )",
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Direct",
@@ -40,6 +40,12 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.30, )",
"resolved": "8.0.30",
"contentHash": "SfmwuZjUPBTEoBMAsXZ/hWkaEK9oL+Y/UtAx7N6RyWHfcMt9J0ExrNPuOgTyPbCXIyiTBFsxFNrPLEZNPmJcvA=="
},
"StyleCop.Analyzers": {
"type": "Direct",
"requested": "[1.2.0-beta.556, )",
@@ -117,6 +123,7 @@
"resolved": "4.5.0",
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
}
}
},
"net8.0/win-x64": {}
}
}
+10 -10
View File
@@ -42,19 +42,19 @@
},
"Impostor.Hazel": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
"resolved": "1.0.1",
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
"dependencies": {
"Impostor.Hazel.Abstractions": "1.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.ObjectPool": "7.0.1",
"Impostor.Hazel.Abstractions": "1.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.ObjectPool": "8.0.0",
"Serilog": "2.12.0"
}
},
"Impostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
@@ -588,7 +588,7 @@
"impostor.api": {
"type": "Project",
"dependencies": {
"Impostor.Hazel.Abstractions": "[1.0.0, )",
"Impostor.Hazel.Abstractions": "[1.0.1, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
@@ -597,8 +597,8 @@
"impostor.server": {
"type": "Project",
"dependencies": {
"Impostor.Api": "[1.9.0-dev, )",
"Impostor.Hazel": "[1.0.0, )",
"Impostor.Api": "[1.10.7-dev, )",
"Impostor.Hazel": "[1.0.1, )",
"Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )",
"Microsoft.Extensions.Hosting": "[8.0.0, )",
"Microsoft.Extensions.ObjectPool": "[8.0.1, )",
+12 -12
View File
@@ -13,19 +13,19 @@
},
"Impostor.Hazel": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
"resolved": "1.0.1",
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
"dependencies": {
"Impostor.Hazel.Abstractions": "1.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.ObjectPool": "7.0.1",
"Impostor.Hazel.Abstractions": "1.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.ObjectPool": "8.0.0",
"Serilog": "2.12.0"
}
},
"Impostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
@@ -97,8 +97,8 @@
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "7.0.1",
"contentHash": "dDFItid/TGJljAnqbUQ0PkAl6ShIG0htwRR8J0cG/5Il6lIZPfkIZMwrZTWiF23AiQcqt6suPaUy2ih6dsCB9Q=="
"resolved": "8.0.0",
"contentHash": "4pm+XgxSukskwjzDDfSjG4KNUIOdFF2VaqZZDtTzoyQMOVSnlV6ZM8a9aVu5dg9LVZTB//utzSc8fOi0b0Mb2Q=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
@@ -132,7 +132,7 @@
"impostor.api": {
"type": "Project",
"dependencies": {
"Impostor.Hazel.Abstractions": "[1.0.0, )",
"Impostor.Hazel.Abstractions": "[1.0.1, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
@@ -141,8 +141,8 @@
"impostor.client": {
"type": "Project",
"dependencies": {
"Impostor.Api": "[1.9.0-dev, )",
"Impostor.Hazel": "[1.0.0, )"
"Impostor.Api": "[1.10.7-dev, )",
"Impostor.Hazel": "[1.0.1, )"
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<ItemGroup>
<ProjectReference Include="..\Impostor.Api\Impostor.Api.csproj" />
<PackageReference Include="Impostor.Hazel" Version="1.0.0" />
<PackageReference Include="Impostor.Hazel" Version="1.0.1" />
</ItemGroup>
</Project>
+11 -11
View File
@@ -4,20 +4,20 @@
"net8.0": {
"Impostor.Hazel": {
"type": "Direct",
"requested": "[1.0.0, )",
"resolved": "1.0.0",
"contentHash": "FgVKuIWK+o6Q+wOPFoZZTeoSuVh4giAhrS8Zvs6O0++Bg8RxH4Lw51uSwnexLiwosXveUCIXICzgGHmRyguVSA==",
"requested": "[1.0.1, )",
"resolved": "1.0.1",
"contentHash": "DFsZcOa9ptFEj8FCfK63L/bcJ9RUVRZUhUS0qkrmdSqkgeJRdvUOTWR1gKlqbaOJcSEvexAITC0QJEPnJUcI7Q==",
"dependencies": {
"Impostor.Hazel.Abstractions": "1.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
"Microsoft.Extensions.ObjectPool": "7.0.1",
"Impostor.Hazel.Abstractions": "1.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.ObjectPool": "8.0.0",
"Serilog": "2.12.0"
}
},
"Impostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
@@ -89,8 +89,8 @@
},
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "7.0.1",
"contentHash": "dDFItid/TGJljAnqbUQ0PkAl6ShIG0htwRR8J0cG/5Il6lIZPfkIZMwrZTWiF23AiQcqt6suPaUy2ih6dsCB9Q=="
"resolved": "8.0.0",
"contentHash": "4pm+XgxSukskwjzDDfSjG4KNUIOdFF2VaqZZDtTzoyQMOVSnlV6ZM8a9aVu5dg9LVZTB//utzSc8fOi0b0Mb2Q=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
@@ -124,7 +124,7 @@
"impostor.api": {
"type": "Project",
"dependencies": {
"Impostor.Hazel.Abstractions": "[1.0.0, )",
"Impostor.Hazel.Abstractions": "[1.0.1, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
@@ -4,8 +4,8 @@
"net8.0": {
"Impostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
@@ -102,7 +102,7 @@
"impostor.api": {
"type": "Project",
"dependencies": {
"Impostor.Hazel.Abstractions": "[1.0.0, )",
"Impostor.Hazel.Abstractions": "[1.0.1, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
@@ -4,8 +4,8 @@
"net8.0": {
"Impostor.Hazel.Abstractions": {
"type": "Transitive",
"resolved": "1.0.0",
"contentHash": "x56AEsY5fKU2Z5O7ZrzEqKfr62kq/C05Aevcciwz5yMfOQlpwuFI1awgNGJsoe+bcx+XnFc3+l+eadtY72aZsg=="
"resolved": "1.0.1",
"contentHash": "de2bMj78xDXOWwyqQVk8o4vbZKGfAgOjnWnkKB1IGGovNtxLeBF8jy3wH5moTZB6juTnLf0ptkCDkRi8c2OcjA=="
},
"Microsoft.AspNetCore.Http.Abstractions": {
"type": "Transitive",
@@ -102,7 +102,7 @@
"impostor.api": {
"type": "Project",
"dependencies": {
"Impostor.Hazel.Abstractions": "[1.0.0, )",
"Impostor.Hazel.Abstractions": "[1.0.1, )",
"Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )",
"Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )",
"Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
@@ -1,4 +1,4 @@
using Impostor.Api.Events.Meeting;
using Impostor.Api.Events.Meeting;
using Impostor.Api.Games;
using Impostor.Api.Net.Inner.Objects;
@@ -6,12 +6,14 @@ namespace Impostor.Server.Events.Meeting
{
public class MeetingEndedEvent : IMeetingEndedEvent
{
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie)
public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud, IInnerPlayerControl? exiled, bool isTie, bool wasOverruled = false, ushort overrideId = 0)
{
Game = game;
MeetingHud = meetingHud;
Exiled = exiled;
IsTie = isTie;
WasOverruled = wasOverruled;
OverrideId = overrideId;
}
public IGame Game { get; }
@@ -21,5 +23,9 @@ namespace Impostor.Server.Events.Meeting
public IInnerPlayerControl? Exiled { get; }
public bool IsTie { get; }
public bool WasOverruled { get; }
public ushort OverrideId { get; }
}
}
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff. Show more