Compare commits

..
Author SHA1 Message Date
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
miniduikboot 9f55fe205d Prepare release 1.9.2 2024-06-01 16:12:10 +02:00
miniduikboot 734643c8c9 Merge pull request #596 from miniduikboot/fix/cleanup-disposed-clients
Clean up clients that refuse to quit properly
2024-05-30 21:47:31 +02:00
miniduikboot 661421da5a Clean up clients that refuse to quit properly
When a player is kicked or banned from a game, the client is expected to
disconnect in response. When they decide not to *or* the disconnect
packet is lost, Impostor just silently disposes of the connection.
This does not remove them from ClientManager, which is how I tracked
this bug down.

By sending them a proper disconnect message, clients should have
received *a* disconnection message before dropping their connection.

Future work should split this entire method in removing players from a
game and processing kick/bans because the latter feature is not relevant
for other callers of this method.
2024-05-26 22:05:21 +02:00
miniduikboot ff2e80d939 Merge pull request #594 from miniduikboot/feature/refine-modular-ac
Change some anticheat checks categories
2024-05-11 13:54:49 +02:00
miniduikboot ddd701320d Add similar AC check to HandleSetName 2024-04-28 20:55:50 +02:00
miniduikboot be39c9facf Move Name checks to NameLimits 2024-04-28 20:55:50 +02:00
miniduikboot 6f56e1acaf Move remaining Color checks to ColorLimits
Some mods add custom colors and still use the standard vanilla SetColor
RPC, which leads to issues with this check.
2024-04-28 20:50:41 +02:00
miniduikboot f1f5185f10 Merge pull request #593 from Pietrodjaowjao/master
Patch game-breaking exploits
2024-04-28 20:44:40 +02:00
miniduikboot 414303b2c1 Documentation: Update .NET 7 to .NET 8 2024-04-28 18:31:34 +02:00
Pietrodjaowjao 0ba700ef68 Forgot to actually return 2024-04-28 05:12:29 -03:00
Pietrodjaowjao c2b5a3e9df gamedata 2024-04-28 04:28:09 -03:00
miniduikboot ac12bacb00 Merge pull request #591 from NikoCat233/add-broadcast-check-to-most-of-calls
[AntiCheat] Add ValidateBroadCast check to most of the rpc calls
2024-04-28 08:22:55 +02:00
miniduikboot 2f3e1a620b Fix embarrassing bugs 2024-04-27 20:20:04 +02:00
miniduikboot d391b24aad Remove use of SetInfected RPC
Client no longer sends the RPC nor does anything with it when receiving it
2024-04-27 16:55:50 +02:00
miniduikboot 5854000220 Refactor if statements to be more vertically dense
Multiple guard clauses make the file harder to navigate, so put them in
a single guard clause with one validation rule per line. Some statements
in the file were already set up like this, so this commit makes it consistent
2024-04-27 16:45:15 +02:00
miniduikboot ef022b04e4 Add Broadcast checks to remaining RPC's 2024-04-27 16:43:50 +02:00
miniduikboot 155c3e69d9 Merge pull request #589 from miniduikboot/feature/game-created-event-add-host
Add Host property to IGameCreatedEvent
2024-04-21 21:44:24 +02:00
Niko233 3c9b0c305c Add broadcast 2024-04-21 14:48:54 +08:00
miniduikboot 2c0f45c5d1 Add Host property to IGameCreatedEvent
When a game has just been created, it does not yet have any players in
them, and as a result you can't get the host or its connection id when
this event is called. This makes it annoying to link between this event
and the IGameCreationEvent that came just before it.

Having this link is useful for the usecase of a game code allocation
plugin that allocates codes from a pool, and needs to return codes to
a pool if these codes weren't picked.
2024-04-12 22:53:01 +02:00
miniduikboot 9b7b1e8b26 Merge pull request #571 from Impostor/bugfix/misc-ac
Miscellaneous Anticheat fixes
2024-04-04 20:17:44 +02:00
miniduikboot 1a11b5f43e Rework Color calculation loop to always terminate 2024-04-04 20:12:44 +02:00
miniduikboot bdd65e9e9f Don't dequeue a color when none are available 2024-04-04 20:10:23 +02:00
miniduikboot ba9eeb5bb4 Check AC config in SetName/Color checks
Previously this was not done causing these checks to be unconditionally enabled
2024-04-04 20:10:23 +02:00
miniduikboot 5dafd9fe5b Allow host-only mods to desync kill timeouts 2024-04-04 20:10:23 +02:00
miniduikboot 7f943e6b2e Allow host-only mods to desync murder roles 2024-04-04 20:10:23 +02:00
miniduikboot 28f3cb3b2e Make Impostor's AC modular (#559)
* Add extra configuration options for the anticheat

* Implement option to exempt hosts from AC checks

* Add AntiCheatConfig to InnerNetObject and friends

* Categorize calls in InnerNetObject.Anticheat

* Categorize other calls to ReportCheatAsync

* Update cosmetics AC handling

When Innersloth changed SetHat etc to SetHatStr as part of the cosmicube
update we accidentally created a few dead methods. Restore and update
the code.

* Push up cheat category to ReportCheatAsync

This makes it possible to log failed checks for diagnosis. Also much
cleaner now that we don't have to push AntiCheatConfig everywhere.
Should've done this immediately q.q

* Add back old version of ReportCheatAsync

some plugins used this method. affected plugins should rebuild after the
next impostor release

* Add documentation for modular anticheat

* Address comments

* Sort AntiCheatConfig keys

* Rework the option to allow cheating hosts

This option should actually be tristate: to not break host-only mods we
should disable the anticheat for hosts running host-only mods.

* Document CheatingHostMode

* Split Limits category in Color and Name Limits
2024-04-04 20:09:59 +02:00
miniduikboot 7a49d2d917 Prepare release 1.9.1 2024-04-03 19:59:30 +02:00
miniduikboot 092504f2f1 Merge pull request #573 from ykundesu/fix/AprilFools2024
fixed AprilFools2024 Deserialize error
2024-04-01 13:30:35 +02:00
miniduikboot 453d12171e Fix compile warning 2024-04-01 13:23:18 +02:00
ykundesu b2e220a738 fixed AprilFools2024 Deserialize error 2024-04-01 17:33:38 +09:00
miniduikboot 2be1d68eeb Merge pull request #569 from Impostor/feature/2024.3.5
Feature/2024.3.5
2024-03-30 16:25:41 +01:00
miniduikboot c7ea59fed9 Run Dumpostor on 2024.3.5 2024-03-06 21:07:09 +01:00
miniduikboot 6cc2185915 Add new game version for 2024.3.5
Officials don't allow crosplay either, so neither should we
2024-03-06 21:06:16 +01:00
miniduikboot 7b23d80978 Merge pull request #563 from Impostor/feature/net8
Upgrade to net8.0
2024-01-25 12:31:07 +01:00
miniduikboot 1582b2c65a Merge pull request #564 from Impostor/hotfix/docker-actions
Migrate to environment files correctly
2024-01-25 12:26:35 +01:00
miniduikboot 81753a53e0 Migrate to environment files correctly
See https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2024-01-25 12:21:56 +01:00
miniduikboot a9fdd82345 Merge pull request #562 from Impostor/fix/http2-warning
Don't set HTTP protocols explicitly
2024-01-25 12:15:43 +01:00
miniduikboot d06b21b066 Merge pull request #561 from miniduikboot/chore/update-gh-actions
Update Github actions
2024-01-25 12:15:26 +01:00
miniduikboot c1fb4336df Merge pull request #560 from Impostor/fix/dleks
Allow spawning in the Dleks map
2024-01-25 12:14:59 +01:00
miniduikboot 9c02a912b4 Merge pull request #557 from miniduikboot/fix/http-default-listenip
Change default ListenIp of Http server to 0.0.0.0
2024-01-25 12:14:31 +01:00
miniduikboot 3143c67eb4 Merge pull request #552 from Impostor/feature/options-changed-event
Implement event to detect game option changes
2024-01-25 12:14:02 +01:00
js6pak 9f93e0955b Update ReadFrom.Configuration call 2024-01-25 12:13:12 +01:00
js6pak d74d7fcecc Drop BinaryFormatter override in ImpostorException 2024-01-25 12:10:36 +01:00
miniduikboot 012e45c6a9 Remove unnecessary using directive 2024-01-25 12:05:09 +01:00
js6pak 2cbfe159be Upgrade to net8.0 2024-01-25 00:51:06 +01:00
js6pak e142fc7c73 Don't set HTTP protocols explicitly 2024-01-25 00:44:41 +01:00
miniduikboot 165621a8cc Update Github actions
- Update versions to the latest one
- Get rid of .gitmodules as we dumped that years ago
- Change ::set-output to output files as the former is slated for removal
2024-01-24 21:37:37 +01:00
miniduikboot 83fe5814ab Only throw warning outside of docker containers 2024-01-24 21:21:07 +01:00
miniduikboot d640f90d88 Allow spawning in the Dleks map
Now with 100% less crash cascades on spawn attempts and it even includes
working map data!
2024-01-24 20:45:21 +01:00
miniduikboot f4f10269b2 Show both warnings 2024-01-24 19:55:35 +01:00
miniduikboot 880951c5b8 Add warning message for direct configuration 2024-01-24 18:13:02 +01:00
miniduikboot 3b514f82f2 Change default ListenIp of Http server to 0.0.0.0
We see that a lot of support tickets are caused by this default setting,
as it is one of the things that blocks connections to the server with no
obvious indication that this is happening. To make it easier for first
time users to set up Impostor, it is important that this setting is
changed.

Previously we also recommended against direct exposing due to security
reasons, but I believe this fear to be unfounded: Microsoft claims that
the Kestrel server is hardened against web server vulnerabilities and
supports its use both with and without a reverse proxy.[1]

We still recommend using reverse proxies if HTTPS support is needed, so
that section of documentation still remains. Setting the ListenIp to
127.0.0.1 is still recommended when using a reverse proxy to remove the
ability for people to connect directly, so we add it there as a recommendation.

[1]: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/when-to-use-a-reverse-proxy?view=aspnetcore-8.0
2024-01-10 19:17:38 +01:00
js6pak 16d3f1c651 Merge branch 'master' into feature/options-changed-event 2023-12-17 20:23:01 +01:00
miniduikbootandjs6pak b5ea1d8864 Update docs to reflect http plugin merge (#546)
Co-authored-by: js6pak <me@6pak.dev>
2023-12-17 20:22:36 +01:00
js6pak 5325a81cb6 Merge Impostor.Http into core (#533) 2023-12-17 20:19:27 +01:00
js6pak c6121eed49 Implement MushroomMixupSabotage 2023-12-16 23:55:58 +01:00
js6pak 389b7dfc0a Drop the RpcSnapTo anticheat 2023-12-16 23:55:58 +01:00
js6pak 5b14c7b64e Implement fungle rpcs 2023-12-16 23:55:58 +01:00
js6pak 9b58f2b36d Source generate map data and enums (#548) 2023-12-16 23:36:02 +01:00
miniduikboot 6e03e90bd0 Merge pull request #553 from Impostor/feature/2023.11.28
Add new network version for 2023.11.28
2023-12-03 23:27:05 +01:00
js6pak f281639763 Make GameLogicComponent methods async 2023-12-03 22:59:05 +01:00
miniduikboot 87fd177e39 more comments sent over discord pm's 2023-12-03 22:55:05 +01:00
miniduikboot fa024b70c5 Process comments 2023-12-03 22:49:13 +01:00
miniduikboot 2c12623a65 Get rid of Rpc02 SyncSettings
This RPC is still sent by clients, but no longer used by clients. As the
server can get the info it wants from LogicOptions instead, remove this
RPC to prevent people from using it accidentally
2023-11-29 22:14:59 +01:00
miniduikboot e303065688 Process comments 2023-11-28 23:09:39 +01:00
miniduikboot 92920357f8 Add new network version for 2023.11.28
Did a quick run through, didn't seem too broken
2023-11-28 20:25:38 +01:00
miniduikboot 5edb409388 Implement event to detect game option changes
This can be triggered by both RPC 02 and a(nother) plugin calling
SyncSettings to force certain settings on the host. Users of this API
could cause this event to recurse by calling SyncSettingsAsync inside an
event handler, so we prevent such reasonable foreseeable misuse.
2023-11-26 23:16:11 +01:00
186 changed files with 4488 additions and 3008 deletions

No files matched your search

+3 -7
View File
@@ -5,13 +5,11 @@ on:
paths:
- '.github/workflows/ci.yml'
- 'src/**/**'
- '.gitmodules'
- 'build.cake'
pull_request:
paths:
- '.github/workflows/ci.yml'
- 'src/**/**'
- '.gitmodules'
- 'build.cake'
jobs:
@@ -20,9 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
uses: actions/checkout@v4
- uses: actions/cache@v3
with:
@@ -34,7 +30,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v2
with:
dotnet-version: 7.x
dotnet-version: 8.x
- name: Run the Cake script
uses: cake-build/cake-action@v1
@@ -54,4 +50,4 @@ jobs:
- name: Push NuGet package
if: github.ref_type == 'tag'
run: |
dotnet nuget push ./build/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_IMPOSTOR }}
dotnet nuget push ./build/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_IMPOSTOR }}
+8 -12
View File
@@ -6,7 +6,6 @@ on:
paths:
- 'src/Impostor.Server/**'
- 'src/Impostor.Shared/**'
- '.gitmodules'
- '.github/workflows/docker.yml'
- 'Dockerfile'
tags:
@@ -15,7 +14,6 @@ on:
paths:
- 'src/Impostor.Server/**'
- 'src/Impostor.Shared/**'
- '.gitmodules'
- '.github/workflows/docker.yml'
- 'Dockerfile'
@@ -25,9 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
submodules: true
uses: actions/checkout@v4
- name: Prepare
id: prep
run: |
@@ -48,14 +44,14 @@ jobs:
if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=version::${VERSION}
echo ::set-output name=tags::${TAGS}
echo ::set-output name=versionsuffix::${VERSIONSUFFIX}
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
echo version=${VERSION} >> $GITHUB_OUTPUT
echo tags=${TAGS} >> $GITHUB_OUTPUT
echo versionsuffix=${VERSIONSUFFIX} >> $GITHUB_OUTPUT
echo created=$(date -u +'%Y-%m-%dT%H:%M:%SZ') >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
@@ -63,7 +59,7 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
View File
Whitespace-only changes.
+2 -8
View File
@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
# See for all possible platforms
# https://github.com/containerd/containerd/blob/master/platforms/platforms.go#L17
@@ -36,18 +36,12 @@ RUN case "$TARGETARCH" in \
dotnet publish -c release -o /app -r "$NETCORE_PLATFORM" -p:VersionSuffix="$VERSIONSUFFIX" --no-restore ./src/Impostor.Server/Impostor.Server.csproj
# Final image.
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:7.0
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app ./
# Add Impostor.Http as a default built-in plugin
ADD https://github.com/Impostor/Impostor.Http/releases/download/v0.5.0/Impostor.Http.dll /app/builtin-plugins/
# Make it listen to 0.0.0.0 to expose it to the outside world.
ENV IMPOSTOR_HTTP_HttpServer__ListenIp=0.0.0.0
# Override ASPNETCORE_URLS to stop warning.
ENV ASPNETCORE_URLS=
# Enable the built-in plugin folder. Use a high number to prevent conflicts with existing configurations
ENV IMPOSTOR_PluginLoader__Paths__76=/app/builtin-plugins
EXPOSE 22023/tcp 22023/udp
ENTRYPOINT ["./Impostor.Server"]
+2 -2
View File
@@ -1,6 +1,6 @@
# Building from source
The solution contains the Impostor server and its dependencies, like Hazel and the plugin API. The server is built using [.NET 7](https://dotnet.microsoft.com/download/dotnet/7.0).
The solution contains the Impostor server and its dependencies, like Hazel and the plugin API. The server is built using [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0).
## Cloning Impostor
@@ -14,7 +14,7 @@ git clone https://github.com/Impostor/Impostor.git
### Dependencies
- [.NET 7 SDK](https://dotnet.microsoft.com/download/dotnet/7.0)
- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
- [Rider](https://www.jetbrains.com/rider/) or [Visual Studio](https://visualstudio.microsoft.com/vs/) (Optional, only if you want the full IDE experience)
### Build using the CLI
+67
View File
@@ -0,0 +1,67 @@
# 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.
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)
## 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.
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.
To prevent people from connecting directly to Impostor, we recommend changing the "ListenIp" in the "HttpServer" section to "127.0.0.1". This makes sure people can't connect to your HTTP server other than via your reverse proxy. Keep the "ListenIp" in the "Server" section at "0.0.0.0" though, running the normal game traffic through a proxy is not supported.
### Caddy
To install Caddy, follow the [official installation guide](https://caddyserver.com/docs/install). Then use the following lines as your `Caddyfile` configuration file:
```
example.com # replace example.com with your domain name
reverse_proxy :22023
```
Now run `caddy run` in the folder with this Caddyfile and it should set up a server for you with a free SSL certificate.
If this works, you should set up [Caddy to run in the background](https://caddyserver.com/docs/running)
### Nginx
Nginx is an alternative to Caddy that is a bit harder to set up. If you already use Nginx, you can use our snippet to add Impostor:
<details><summary>Nginx configuration</summary>
```nginx
server {
listen 443 ssl http2;
server_name example.com; # replace example.com with your domain name
# Assuming you're using Certbot, replace example.com with your domain name
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
location / {
proxy_pass http://localhost:22023; # Change the port to your HttpServer's ListenPort
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Redirect all traffic to HTTPS
server {
listen 80 default_server;
location / {
return 307 https://$host$request_uri;
}
}
```
</details>
+10 -10
View File
@@ -10,19 +10,19 @@ 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 [Impostor.Http readme](https://github.com/Impostor/Impostor.Http#readme).
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).
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.
## Normal installation
1. Install [.NET 7.0](https://dotnet.microsoft.com/download/dotnet/7.0). We recommend either the ASP.NET Core Runtime or the SDK. The SDK is necessary in case you want to develop Impostor or Impostor plugins.
2. Download the [latest release](https://github.com/Impostor/Impostor/releases) or the [latest CI build](https://nightly.link/Impostor/Impostor/workflows/ci/master). Note that Impostor is built for multiple CPU-architectures and operating systems, you most likely want the x64 version, unless you are running on a Raspberry Pi or another device with an Arm processor.
1. Install [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0). We recommend either the ASP.NET Core Runtime or the SDK. The SDK is necessary in case you want to develop Impostor or Impostor plugins.
2. Download the [latest release](https://github.com/Impostor/Impostor/releases) or the [latest CI build](https://nightly.link/Impostor/Impostor/workflows/ci/master). Note that Impostor is built for multiple CPU-architectures and operating systems, you most likely want the x64 version, unless you are running on a Raspberry Pi or another device/VPS with an Arm processor.
3. Extract the zip.
4. Download the [Impostor.Http](https://github.com/Impostor/Impostor.Http) plugin and put it in your `plugins` folder. Please note the Reverse Proxy configuration: you either need to configure a reverse proxy to terminate SSL or expose the service to the world by changing ListenIp.
5. 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.
6. Run `Impostor.Server` (Linux/macOS) or `Impostor.Server.exe` (Windows)
7. (OPTIONAL - Linux) Configure a systemd definition file and enable the service to start on boot - [systemd configuration](Server-configuration.md#systemd)
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)
## Using Docker
@@ -34,10 +34,10 @@ 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 127.0.0.1:22023:22023/tcp -p 22023:22023/udp -e IMPOSTOR_Server__PublicIp=your.public.ip.here aeonlucid/impostor:nightly
docker run -p 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 client at.
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.
To configure the docker container, either use environment variables or mount config.json in your container.
@@ -53,7 +53,7 @@ services:
image: aeonlucid/impostor:nightly
container_name: impostor
ports:
- 127.0.0.1:22023:22023/tcp
- 22023:22023/tcp # Add "127.0.0.1:" if you're using a reverse proxy to terminate HTTPS
- 22023:22023/udp
environment: # Either configure Impostor using environment variables or mount a copy of config.json
- IMPOSTOR_Server__PublicIp=your.public.ip.here
+33 -15
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
@@ -11,25 +9,45 @@ Some information about all the possible configurations. Click [here](https://git
| **PublicIp** | `127.0.0.1` | This needs to the public IPv4 address of the server which you give to others to connect. You can find your IPv4 address [on this website](http://whatismyip.host/). Unless you are only planning to use Impostor privately, on your local network, you should change this to your public ip. It is also possible to use hostnames instead of IPv4 addresses, which will be resolved to IPv4 addresses. |
| **PublicPort** | `22023` | The public port of the server which you give to others to connect. (**This is the external port you configure on your router when port forwarding.**) Usually `22023`. |
| **ListenIp** | `0.0.0.0` | The network interface to listen on. If you do not know what to put here, use `0.0.0.0`. Since 1.2.2 it is also possible to use hostnames instead of IPv4 addresses, these must resolve to a valid IPv4 address. |
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. |
| **ListenPort** | `22023` | The listen port of the server, usually `22023`. For port forwarding purposes: this is an UDP port |
### HttpServer
Impostor has an Http Server that is used by recent versions of Among Us to connect to. See [the Http Server page](Http-server.md) for more details on how to set this up.
| Key | Default | Description |
|----------------|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Enabled** | `true` | Whether the http server should be enabled. |
| **ListenIp** | `0.0.0.0` | The network interface to listen on. Use `127.0.0.1` if you use a reverse proxy or just run locally. Use `0.0.0.0` if you are directly exposing this server to the internet |
| **ListenPort** | `22023` | The listen port of this server. For port forwarding purposes, this is an TCP port. |
### AntiCheat
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. |
| 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. |
### 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
@@ -79,11 +97,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
@@ -158,4 +176,4 @@ If everything has started correctly, ensure the service is set to start on boot.
```
sudo systemctl enable impostor.service
```
```
+12
View File
@@ -0,0 +1,12 @@
# Upgrading Impostor
Sometimes we make incompatible changes to existing code. This document lists these changes and which changes you should make as a server administrator.
## Impostor 1.9.0
Previously we recommended using the Impostor.Http plugin for HTTP matchmaking. Because Among Us now relies on HTTP matchmaking, this plugin is now part of the default installation. As a result, you should change your server as follows:
- If you have Impostor.Http installed, you should remove that plugin. If you have changed the default settings, you need to move these changes to the [HttpServer section in config.json](Server-configuration.md#HttpServer).
- If you have plugins that required Impostor.Http's API (like Reactor.Impostor.Http), you should check that plugin for updates
- It is no longer necessary to add the ASP.NET Core folder to the PluginLoader's LibraryPaths
+4 -12
View File
@@ -27,24 +27,16 @@ https://dotnet.microsoft.com/download
## 2. Create a C# project
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 7, which includes:
- .NET Standard 2.0
- .NET Standard 2.1
- .NET 5
- .NET 6
- .NET 7
The first step is creating a new C# project, it must be a **Class Library (.NET Standard)**. The target framework can be any of those compatible with .NET 8, but we recommend sticking with **.NET 8.0**.
For more information about compatibility, see https://docs.microsoft.com/en-us/dotnet/standard/net-standard.
> At the moment of writing this document, I recommend you to use **.NET 7.0**.
When the project has been created, you should have `Class.cs` and `Project.csproj` files. Your `Project.csproj` should look something like this.
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>
```
@@ -270,7 +262,7 @@ Now your plugin is ready to be tested.
1. Right click your project and press `Build`.
2. Right click your project and press `Open Folder in File Explorer`.
3. Go to `bin/Debug/net7.0/`.
3. Go to `bin/Debug/net8.0/`.
4. In this directory, you should find your plugin named `Project.dll`.
5. Copy the `Project.dll` to the `plugins` directory in your Impostor server directory.
6. (Re)start your Impostor server.
@@ -309,7 +301,7 @@ Constantly copying the plugin dll to your server directory can be pretty annoyin
},
"PluginLoader": {
"Paths": [
"D:\\Projects\\Impostor\\src\\Impostor.Plugins.Example\\bin\\Debug\\net7.0"
"D:\\Projects\\Impostor\\src\\Impostor.Plugins.Example\\bin\\Debug\\net8.0"
],
"LibraryPaths": []
}
+2 -2
View File
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>11</LangVersion>
<VersionPrefix>1.9.0</VersionPrefix>
<VersionPrefix>1.10.2</VersionPrefix>
<VersionSuffix>dev</VersionSuffix>
<EnforceCodeStyleInBuild Condition="$(MSBuildProjectName)!='Hazel'">true</EnforceCodeStyleInBuild>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
@@ -1,11 +1,30 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
namespace Impostor.Api.Innersloth.Generator;
internal static class Extensions
{
public static string NormalizePath(this string path)
{
return path.Replace("\\", "/");
}
public static bool TryTrimStart(this string text, string value, [NotNullWhen(true)] out string? result)
{
if (text.StartsWith(value))
{
result = text[value.Length..];
return true;
}
result = null;
return false;
}
public static string ToCSharpString(this Vector2 value)
{
return $"new Vector2({value.X}f, {value.Y}f)";
return $"new Vector2({value.X.ToString(CultureInfo.InvariantCulture)}f, {value.Y.ToString(CultureInfo.InvariantCulture)}f)";
}
}
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Numerics;
using System.Text.Json;
using CSharpPoet;
@@ -102,7 +103,7 @@ public sealed class MapDataGenerator : BaseGenerator
new CSharpBlankLine(),
CreateSimpleProperty("float", "SpawnRadius", spawnInfo.SpawnRadius + "f"),
CreateSimpleProperty("float", "SpawnRadius", spawnInfo.SpawnRadius.ToString(CultureInfo.InvariantCulture) + "f"),
CreateSimpleProperty("Vector2", "InitialSpawnCenter", spawnInfo.InitialSpawnCenter.ToCSharpString()),
CreateSimpleProperty("Vector2", "MeetingSpawnCenter", spawnInfo.MeetingSpawnCenter.ToCSharpString()),
CreateSimpleProperty("Vector2", "MeetingSpawnCenter2", spawnInfo.MeetingSpawnCenter2.ToCSharpString()),
@@ -180,7 +181,6 @@ public sealed class MapDataGenerator : BaseGenerator
{
public required string Type { get; init; }
public required string TaskType { get; init; }
public required string HudText { get; init; }
public required string Length { get; init; }
public required TaskConsole[] Consoles { get; init; }
}
@@ -6,15 +6,19 @@
<DebugType>embedded</DebugType>
<Nullable>enable</Nullable>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="PolySharp" Version="1.13.2" PrivateAssets="all" />
<PackageReference Include="PolySharp" Version="1.14.1" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" Version="7.0.3" Private="true" GeneratePathProperty="true" />
<PackageReference Include="CSharpPoet" Version="0.3.0" Private="true" GeneratePathProperty="true" />
<PackageReference Include="System.Text.Json" Version="8.0.4" 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" />
<PackageReference Include="CSharpPoet" Version="0.3.0" PrivateAssets="all" GeneratePathProperty="true" />
</ItemGroup>
<PropertyGroup>
@@ -23,8 +27,11 @@
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PkgSystem_Text_Json)\lib\netstandard2.0\System.Text.Json.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PkgCSharpPoet)\lib\netstandard2.0\CSharpPoet.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PkgSystem_Text_Json)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PkgSystem_Text_Encodings_Web)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Bcl_AsyncInterfaces)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PkgCSharpPoet)\lib\netstandard2.0\*.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using CSharpPoet;
using Impostor.Api.Innersloth.Generator.Generators;
using Microsoft.CodeAnalysis;
@@ -8,7 +9,24 @@ namespace Impostor.Api.Innersloth.Generator;
[Generator(LanguageNames.CSharp)]
public sealed class SourceGenerator : IIncrementalGenerator
{
private record struct Options(string ProjectDirectory);
private const string DataPath = "Innersloth/Data/";
private readonly record struct Options(string ProjectDirectory)
{
public bool TryGetRelativePath(string path, [NotNullWhen(true)] out string? relativePath)
{
if (
path.NormalizePath().TryTrimStart(ProjectDirectory, out relativePath) &&
relativePath.TryTrimStart(DataPath, out relativePath)
)
{
return true;
}
relativePath = null;
return false;
}
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@@ -20,22 +38,26 @@ public sealed class SourceGenerator : IIncrementalGenerator
throw new Exception("Couldn't get project directory");
}
return new Options(projectDirectory);
return new Options(projectDirectory.NormalizePath());
});
var filesProvider = context.AdditionalTextsProvider.Combine(optionsProvider)
.Where(static pair =>
{
var (file, options) = pair;
return file.Path.StartsWith(options.ProjectDirectory) &&
file.Path[options.ProjectDirectory.Length..].StartsWith("Innersloth/Data/") &&
file.Path.EndsWith(".json");
return options.TryGetRelativePath(file.Path, out var relativePath) && relativePath.EndsWith(".json");
})
.Select(static (pair, cancellationToken) =>
{
var (file, options) = pair;
if (!options.TryGetRelativePath(file.Path, out var relativePath))
{
throw new InvalidOperationException();
}
return (
RelativePath: file.Path[(options.ProjectDirectory.Length + "Innersloth/Data/".Length)..],
RelativePath: relativePath,
Content: file.GetText(cancellationToken)!.ToString()
);
})
@@ -43,6 +65,11 @@ public sealed class SourceGenerator : IIncrementalGenerator
context.RegisterSourceOutput(filesProvider, (spc, files) =>
{
if (files.IsEmpty)
{
throw new InvalidOperationException($"No json files found in Impostor.Api/{DataPath}");
}
var enumGenerator = new EnumGenerator(spc, files);
enumGenerator.Generate("ColorType", "Impostor.Api.Innersloth.Customization");
@@ -52,6 +79,8 @@ 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");
@@ -8,6 +8,15 @@
"resolved": "0.3.0",
"contentHash": "3QDh9rMuiNhMtNWOLj0FItiZruON7lj3zD0CXC9IHq4JWpF5OjbaZ2s1X0u6gftE73KCoTezXIgKvxacps78Fw=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Direct",
"requested": "[3.3.4, )",
@@ -34,30 +43,32 @@
},
"PolySharp": {
"type": "Direct",
"requested": "[1.13.2, )",
"resolved": "1.13.2",
"contentHash": "XwNhfkr7IeUiH8AE4pzob8YioxfL6nxgAx+fHEeWCObY/NZuBMfWLh39FznXbneKvagiqeeI7quIvZ6P1eVaEA=="
"requested": "[1.14.1, )",
"resolved": "1.14.1",
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
},
"System.Text.Encodings.Web": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Json": {
"type": "Direct",
"requested": "[7.0.3, )",
"resolved": "7.0.3",
"contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==",
"requested": "[8.0.4, )",
"resolved": "8.0.4",
"contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5",
"System.Numerics.Vectors": "4.5.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encodings.Web": "7.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg==",
"dependencies": {
"System.Text.Encodings.Web": "8.0.0",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
@@ -106,8 +117,8 @@
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
"resolved": "4.4.0",
"contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
},
"System.Reflection.Metadata": {
"type": "Transitive",
@@ -132,16 +143,6 @@
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==",
"dependencies": {
"System.Buffers": "4.5.1",
"System.Memory": "4.5.5",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
+31
View File
@@ -0,0 +1,31 @@
namespace Impostor.Api;
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 packet was sent at an inappropriate moment.</summary>
GameFlow,
/// <summary>A packet was sent by a non-host player that should normally only be sent by the host.</summary>
MustBeHost,
/// <summary>A packet was sent that violated limits on the selection of player colors.</summary>
ColorLimits,
/// <summary>A packet was sent that exceeded the limits of possible nicknames to enter ingame.</summary>
NameLimits,
/// <summary>A packet was sent on behalf of another player.</summary>
Ownership,
/// <summary>An ability was used that the current role cannot access.</summary>
Role,
/// <summary>A packet was sent to a player that should be broadcasted, or vice versa.</summary>
Target,
/// <summary>Legacy category for unsorted anticheat checks.</summary>
Other,
}
@@ -7,5 +7,23 @@
public bool Enabled { get; set; } = true;
public bool BanIpFromGame { get; set; } = true;
public CheatingHostMode AllowCheatingHosts { get; set; } = CheatingHostMode.Never;
public bool EnableGameFlowChecks { get; set; } = true;
public bool EnableMustBeHostChecks { get; set; } = true;
public bool EnableColorLimitChecks { get; set; } = true;
public bool EnableNameLimitChecks { get; set; } = true;
public bool EnableOwnershipChecks { get; set; } = true;
public bool EnableRoleChecks { get; set; } = true;
public bool EnableTargetChecks { get; set; } = true;
public bool ForbidProtocolExtensions { get; set; } = true;
}
}
@@ -0,0 +1,29 @@
namespace Impostor.Api.Config
{
/// <summary>
/// Details if exceptions are made for hosts that are cheating.
/// </summary>
public enum CheatingHostMode
{
/// <summary>
/// Hosts follow the same policies as other players.
/// </summary>
Never,
/// <summary>
/// Hosts are allowed to cheat if they request HostAuthority. If they
/// do not request this, the same policies as for other players applies.
/// </summary>
/// <para>
/// HostAuthority can be requested by hosts by adding 25 to their patch
/// version when connecting. This flag is used by a lot of (host-only)
/// mods and also disable server authority over MurderPlayer packets.
/// </para>
IfRequested,
/// <summary>
/// Hosts are always allowed to cheat.
/// </summary>
Always,
}
}
@@ -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;
}
}
@@ -23,5 +23,14 @@
public const string VersionServerTooOld = "Your client is too new, please update your Impostor server to play.";
public const string VersionUnsupported = "Your client version is unsupported, please update your Game and/or Impostor server.";
private const string UpgradingDocsLink = "https://github.com/Impostor/Impostor/blob/master/docs/Upgrading.md";
public const string UdpMatchmakingUnsupported = $"""
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.";
}
}
@@ -0,0 +1,27 @@
namespace Impostor.Api.Config;
/// <summary>
/// Configuration for HttpServer.
/// </summary>
public class HttpServerConfig
{
/// <summary>
/// Gets the name of this config section.
/// </summary>
public const string Section = "HttpServer";
public bool Enabled { get; set; } = true;
/// <summary>
/// Gets or sets the IP address the HTTP Matchmaking server will listen on.
/// </summary>
/// Use "127.0.0.1" if you are running behind a reverse proxy or just testing locally.
/// Use "0.0.0.0" if you are directly exposing this server to the internet.
public string ListenIp { get; set; } = "0.0.0.0";
/// <summary>
/// Gets or sets the port the HTTP Matchmaking server will listen on.
/// </summary>
/// For port forwarding purposes, this is a TCP port.
public ushort ListenPort { get; set; } = 22023;
}
+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,11 +1,24 @@
using Impostor.Api.Games;
using Impostor.Api.Net;
namespace Impostor.Api.Events
{
/// <summary>
/// Called whenever a new <see cref="IGame" /> is created.
/// </summary>
/// <remarks>
/// Note that the game just has been created, so no players have joined
/// it yet. If you want to know the future host of this game, use the
/// <see cref="Host"/> property.
/// </remarks>
public interface IGameCreatedEvent : IGameEvent
{
/// <summary>
/// Gets the client that requested creation of the game.
/// </summary>
/// <remarks>
/// Will be null if game creation was requested by a plugin.
/// </remarks>
IClient? Host { get; }
}
}
@@ -0,0 +1,49 @@
using Impostor.Api.Games;
namespace Impostor.Api.Events
{
/// <summary>
/// This event is triggered when game options are going to be changed.
/// </summary>
/// <remarks>
/// Be careful when calling SyncSettingsAsync while handling this event,
/// as it may call this event again in some cases.
/// </remarks>
public interface IGameOptionsChangedEvent : IGameEvent
{
/// <summary>
/// Lists the possible reasons that the game options may have changed.
/// </summary>
public enum ChangeReason
{
/// <summary>
/// The options were changed by the host using LogicOptions deserialization.
/// </summary>
/// <remarks>
/// This event does not change the message sent to other players,
/// so changes made to Game.Options are not synced until
/// SyncSettingsAsync is called afterwards.
/// </remarks>
Host,
/// <summary>
/// The options were changed by a plugin using the
/// SyncSettingsAsync method in <see cref="IGame"/>.
/// </summary>
/// <remarks>
/// This event is called after serializing the settings, which
/// means that changes made by event handlers will not be synced
/// over.
///
/// We strongly recommend against calling SyncSettingsAsync while
/// handling this event.
/// </remarks>
Api,
}
/// <summary>
/// Gets the reason that the game options have changed.
/// </summary>
public ChangeReason ChangedBy { get; }
}
}
@@ -1,5 +1,4 @@
using System;
using System.Runtime.Serialization;
namespace Impostor.Api
{
@@ -16,9 +15,5 @@ namespace Impostor.Api
public ImpostorConfigException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ImpostorConfigException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Runtime.Serialization;
namespace Impostor.Api
{
@@ -16,9 +15,5 @@ namespace Impostor.Api
public ImpostorException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ImpostorException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Runtime.Serialization;
namespace Impostor.Api
{
@@ -16,9 +15,5 @@ namespace Impostor.Api
public ImpostorProtocolException(string? message, Exception? innerException) : base(message, innerException)
{
}
protected ImpostorProtocolException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
+23 -4
View File
@@ -101,22 +101,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);
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using Impostor.Api.Games;
using Microsoft.AspNetCore.Http;
namespace Impostor.Api.Http;
/// <summary>
/// Register a method to filter listings on.
/// </summary>
public interface IListingFilter
{
/// <summary>
/// Return a filter to filter listings on.
/// </summary>
/// <param name="context">HTTP Context of this request.</param>
/// <returns>A function that looks at a game and returns true iff the connecting player is compatible with this game.</returns>
Func<IGame, bool> GetFilter(HttpContext context);
}
+4 -3
View File
@@ -25,9 +25,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<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" />
<ProjectReference Include="..\Impostor.Api.Innersloth.Generator\Impostor.Api.Innersloth.Generator.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
@@ -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;
}
}
}
@@ -4,5 +4,7 @@ namespace Impostor.Api.Innersloth.Customization
{
Default,
Shapeshifted,
HorseWrangler,
MushroomMixup,
}
}
@@ -2,7 +2,6 @@
"0": "SkeldShipStatus",
"1": "MeetingHud",
"2": "LobbyBehaviour",
"3": "GameData",
"4": "PlayerControl",
"5": "MiraShipStatus",
"6": "PolusShipStatus",
@@ -10,5 +9,7 @@
"8": "AirshipStatus",
"9": "HideAndSeekManager",
"10": "NormalGameManager",
"11": "NetworkedPlayerInfo",
"12": "VoteBanSystem",
"13": "FungleShipStatus"
}
}
@@ -17,4 +17,4 @@
"Gray": 15,
"Tan": 16,
"Coral": 17
}
}
@@ -16,6 +16,7 @@
"IncorrectGame": 18,
"ServerRequest": 19,
"ServerFull": 20,
"MismatchedVersion": 21,
"InternalPlayerMissing": 100,
"InternalNonceFailure": 101,
"InternalConnectionToken": 102,
@@ -42,5 +43,6 @@
"PlatformFailedToGetUserBlock": 213,
"ServerNotFound": 214,
"ClientTimeout": 215,
"ErrorAuthNonceFailure": 216,
"Unknown": 255
}
}
@@ -19,4 +19,4 @@
"SChinese": 65536,
"TChinese": 131072,
"Irish": 262144
}
}
@@ -8,4 +8,4 @@
"HumansDisconnect": 6,
"HideAndSeek_ByTimer": 7,
"HideAndSeek_ByKills": 8
}
}
@@ -10,4 +10,4 @@
"Switch": 8,
"Xbox": 9,
"Playstation": 10
}
}
@@ -6,5 +6,8 @@
"GuardianAngel": 4,
"Shapeshifter": 5,
"CrewmateGhost": 6,
"ImpostorGhost": 7
}
"ImpostorGhost": 7,
"Noisemaker": 8,
"Phantom": 9,
"Tracker": 10
}
@@ -55,5 +55,9 @@
"CheckShapeshift": 55,
"RejectShapeshift": 56,
"LobbyTimeExpiring": 60,
"ExtendLobbyTimer": 61
}
"ExtendLobbyTimer": 61,
"CheckVanish": 62,
"StartVanish": 63,
"CheckAppear": 64,
"StartAppear": 65
}
@@ -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
}
@@ -506,6 +506,7 @@
"ErrorSanction": 728,
"ErrorClientTimeout": 729,
"ErrorClientTimeoutConsole": 730,
"ErrorAuthNonceFailure": 731,
"VentDirection": 1000,
"VentMove": 1001,
"MenuNavigate": 1002,
@@ -569,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,
@@ -1303,6 +1333,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,
@@ -1328,6 +1376,35 @@
"FriendsLabel": 3322,
"ReportNotificationHeader": 3323,
"ReportNotificationBody": 3324,
"MissingGuardianEmailText": 3325,
"AprilFoolsModeToggle": 3326,
"LobbyChangeSettingNotification": 3327,
"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,
"MiningPit": 3400,
"Jungle": 3401,
"BuildSandcastle": 3402,
@@ -1362,6 +1439,9 @@
"CookFish": 3431,
"PrepVegetables": 3432,
"MushroomMixupName": 3433,
"EosAccountSilentMergeFail": 3434,
"AskForEosAccount": 3435,
"KwsCreateFail": 3436,
"GameType": 3500,
"GameTypeError": 3501,
"GameTypeClassic": 3502,
@@ -1395,5 +1475,22 @@
"HttpErrorInternalServerError": 3530,
"HttpErrorBadGateway": 3531,
"HttpErrorServiceUnavailable": 3532,
"HttpErrorGatewayTimeout": 3533
}
"HttpErrorGatewayTimeout": 3533,
"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
}
@@ -15,4 +15,4 @@
"SChinese": 13,
"TChinese": 14,
"Irish": 15
}
}
@@ -58,4 +58,4 @@
"SleepingQuarters": 56,
"MushroomMixupSabotage": 57,
"HeliSabotage": 58
}
}
@@ -81,4 +81,4 @@
"ExtractFuel": 79,
"MonitorMushroom": 80,
"PlayVideogame": 81
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
{
"dumpostorVersion": "1.0.0",
"gameVersion": "2023.10.24",
"platformType": "StandaloneSteamPC"
}
"dumpostorVersion": "1.0.1",
"gameVersion": "2024.10.29",
"platformType": "StandaloneItch"
}
@@ -146,4 +146,4 @@
"y": -4.694899
}
}
}
}
@@ -12,4 +12,4 @@
"y": 9
},
"spawnRadius": 1.55
}
}
@@ -10,4 +10,4 @@
"Security": "SecurityCameraSystemType",
"Ventilation": "VentilationSystem",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "Viewing Deck: Fix Wiring (0/3)",
"length": "Common",
"consoles": [
{
@@ -73,7 +72,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "EnterIdCode",
"hudText": "Meeting Room: Enter Id Code",
"length": "Common",
"consoles": [
{
@@ -90,7 +88,6 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "CalibrateDistributor",
"hudText": "Electrical: Calibrate Distributor",
"length": "Long",
"consoles": [
{
@@ -107,7 +104,6 @@
"3": {
"type": "NormalPlayerTask",
"taskType": "ResetBreakers",
"hudText": "Electrical: Reset Breakers (0/7)",
"length": "Long",
"consoles": [
{
@@ -178,7 +174,6 @@
"4": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Vault: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -204,7 +199,6 @@
"5": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Brig: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -230,7 +224,6 @@
"6": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Cargo Bay: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -256,7 +249,6 @@
"7": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Gap Room: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -282,7 +274,6 @@
"8": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Records: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -308,7 +299,6 @@
"9": {
"type": "NormalPlayerTask",
"taskType": "UnlockSafe",
"hudText": "Cargo Bay: Unlock Safe",
"length": "Long",
"consoles": [
{
@@ -325,35 +315,30 @@
"10": {
"type": "NormalPlayerTask",
"taskType": "StartFans",
"hudText": "Ventilation: Start Fans",
"length": "Long",
"consoles": []
},
"11": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Main Hall: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"12": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Medical: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"13": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Kitchen: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"14": {
"type": "NormalPlayerTask",
"taskType": "DevelopPhotos",
"hudText": "Main Hall: Develop Photos",
"length": "Long",
"consoles": [
{
@@ -370,14 +355,12 @@
"15": {
"type": "NormalPlayerTask",
"taskType": "FuelEngines",
"hudText": "Cargo Bay: Fuel Engines",
"length": "Long",
"consoles": []
},
"16": {
"type": "NormalPlayerTask",
"taskType": "RewindTapes",
"hudText": "Security: Rewind Tapes",
"length": "Long",
"consoles": [
{
@@ -394,21 +377,18 @@
"17": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Lounge: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"18": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Showers: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"19": {
"type": "NormalPlayerTask",
"taskType": "PolishRuby",
"hudText": "Vault: Polish Ruby",
"length": "Short",
"consoles": [
{
@@ -425,7 +405,6 @@
"20": {
"type": "NormalPlayerTask",
"taskType": "StabilizeSteering",
"hudText": "Cockpit: Stabilize Steering",
"length": "Short",
"consoles": [
{
@@ -442,7 +421,6 @@
"21": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Armory: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -468,7 +446,6 @@
"22": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Cockpit: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -494,7 +471,6 @@
"23": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Communications: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -520,7 +496,6 @@
"24": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Medical: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -546,7 +521,6 @@
"25": {
"type": "AirshipUploadTask",
"taskType": "UploadData",
"hudText": "Viewing Deck: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -572,7 +546,6 @@
"26": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Armory (0/2)",
"length": "Short",
"consoles": [
{
@@ -589,7 +562,6 @@
"27": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Cockpit (0/2)",
"length": "Short",
"consoles": [
{
@@ -606,7 +578,6 @@
"28": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Gap Room (0/2)",
"length": "Short",
"consoles": [
{
@@ -623,7 +594,6 @@
"29": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Main Hall (0/2)",
"length": "Short",
"consoles": [
{
@@ -640,7 +610,6 @@
"30": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Meeting Room (0/2)",
"length": "Short",
"consoles": [
{
@@ -657,7 +626,6 @@
"31": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Showers (0/2)",
"length": "Short",
"consoles": [
{
@@ -674,7 +642,6 @@
"32": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Engine Room (0/2)",
"length": "Short",
"consoles": [
{
@@ -691,7 +658,6 @@
"33": {
"type": "TowelTask",
"taskType": "PickUpTowels",
"hudText": "Showers: Pick Up Towels (0/8)",
"length": "Short",
"consoles": [
{
@@ -834,7 +800,6 @@
"34": {
"type": "NormalPlayerTask",
"taskType": "CleanToilet",
"hudText": "Lounge: Clean Toilet",
"length": "Short",
"consoles": [
{
@@ -878,7 +843,6 @@
"35": {
"type": "NormalPlayerTask",
"taskType": "DressMannequin",
"hudText": "Vault: Dress Mannequin",
"length": "Short",
"consoles": [
{
@@ -895,7 +859,6 @@
"36": {
"type": "NormalPlayerTask",
"taskType": "SortRecords",
"hudText": "Records: Sort Records (0/4)",
"length": "Short",
"consoles": [
{
@@ -984,21 +947,18 @@
"37": {
"type": "NormalPlayerTask",
"taskType": "PutAwayPistols",
"hudText": "Armory: Put Away Pistols",
"length": "Short",
"consoles": []
},
"38": {
"type": "NormalPlayerTask",
"taskType": "PutAwayRifles",
"hudText": "Armory: Put Away Rifles",
"length": "Short",
"consoles": []
},
"39": {
"type": "NormalPlayerTask",
"taskType": "Decontaminate",
"hudText": "Main Hall: Decontaminate",
"length": "Short",
"consoles": [
{
@@ -1024,7 +984,6 @@
"40": {
"type": "NormalPlayerTask",
"taskType": "MakeBurger",
"hudText": "Kitchen: Make Burger",
"length": "Short",
"consoles": [
{
@@ -1041,7 +1000,6 @@
"41": {
"type": "NormalPlayerTask",
"taskType": "FixShower",
"hudText": "Showers: Fix Shower",
"length": "Short",
"consoles": [
{
@@ -1058,7 +1016,6 @@
"42": {
"type": "NormalPlayerTask",
"taskType": "VentCleaning",
"hudText": "Gap Room: Clean Vent",
"length": "Short",
"consoles": [
{
@@ -1171,4 +1128,4 @@
}
]
}
}
}
@@ -119,4 +119,4 @@
"center": null,
"right": 10
}
}
}
@@ -90,4 +90,4 @@
"y": -8.560801
}
}
}
}
@@ -12,4 +12,4 @@
"y": 0
},
"spawnRadius": 1.6
}
}
@@ -8,4 +8,4 @@
"LifeSupp": "LifeSuppSystemType",
"Ventilation": "VentilationSystem",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "SwipeCard",
"hudText": "Admin: Swipe Card",
"length": "Common",
"consoles": [
{
@@ -19,7 +18,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "Electrical: Fix Wiring (0/3)",
"length": "Common",
"consoles": [
{
@@ -81,7 +79,6 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "ClearAsteroids",
"hudText": "Weapons: Clear Asteroids (0/20)",
"length": "Long",
"consoles": [
{
@@ -98,7 +95,6 @@
"3": {
"type": "NormalPlayerTask",
"taskType": "AlignEngineOutput",
"hudText": "Upper Engine: Align Engine Output (0/2)",
"length": "Long",
"consoles": [
{
@@ -124,7 +120,6 @@
"4": {
"type": "NormalPlayerTask",
"taskType": "SubmitScan",
"hudText": "MedBay: Submit Scan",
"length": "Long",
"consoles": [
{
@@ -141,7 +136,6 @@
"5": {
"type": "NormalPlayerTask",
"taskType": "InspectSample",
"hudText": "MedBay: Inspect Sample",
"length": "Long",
"consoles": [
{
@@ -158,14 +152,12 @@
"6": {
"type": "NormalPlayerTask",
"taskType": "FuelEngines",
"hudText": "Storage: Fuel Engines (0/2)",
"length": "Long",
"consoles": []
},
"7": {
"type": "NormalPlayerTask",
"taskType": "StartReactor",
"hudText": "Reactor: Start Reactor",
"length": "Long",
"consoles": [
{
@@ -182,21 +174,18 @@
"8": {
"type": "NormalPlayerTask",
"taskType": "EmptyChute",
"hudText": "O2: Empty Chute (0/2)",
"length": "Long",
"consoles": []
},
"9": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Cafeteria: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"10": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Communications: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -213,7 +202,6 @@
"11": {
"type": "NormalPlayerTask",
"taskType": "CalibrateDistributor",
"hudText": "Electrical: Calibrate Distributor",
"length": "Short",
"consoles": [
{
@@ -230,14 +218,12 @@
"12": {
"type": "NormalPlayerTask",
"taskType": "ChartCourse",
"hudText": "Navigation: Chart Course",
"length": "Short",
"consoles": []
},
"13": {
"type": "NormalPlayerTask",
"taskType": "CleanO2Filter",
"hudText": "O2: Clean O2 Filter",
"length": "Short",
"consoles": [
{
@@ -254,7 +240,6 @@
"14": {
"type": "NormalPlayerTask",
"taskType": "UnlockManifolds",
"hudText": "Reactor: Unlock Manifolds",
"length": "Short",
"consoles": [
{
@@ -271,7 +256,6 @@
"15": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Electrical: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -288,7 +272,6 @@
"16": {
"type": "NormalPlayerTask",
"taskType": "StabilizeSteering",
"hudText": "Navigation: Stabilize Steering",
"length": "Short",
"consoles": [
{
@@ -305,7 +288,6 @@
"17": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Weapons: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -322,7 +304,6 @@
"18": {
"type": "NormalPlayerTask",
"taskType": "PrimeShields",
"hudText": "Shields: Prime Shields",
"length": "Short",
"consoles": [
{
@@ -339,7 +320,6 @@
"19": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Cafeteria: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -356,7 +336,6 @@
"20": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Navigation: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -373,7 +352,6 @@
"21": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Shields (0/2)",
"length": "Short",
"consoles": [
{
@@ -390,7 +368,6 @@
"22": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Weapons (0/2)",
"length": "Short",
"consoles": [
{
@@ -407,7 +384,6 @@
"23": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Communications (0/2)",
"length": "Short",
"consoles": [
{
@@ -424,7 +400,6 @@
"24": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Upper Engine (0/2)",
"length": "Short",
"consoles": [
{
@@ -441,7 +416,6 @@
"25": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to O2 (0/2)",
"length": "Short",
"consoles": [
{
@@ -458,7 +432,6 @@
"26": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Navigation (0/2)",
"length": "Short",
"consoles": [
{
@@ -475,7 +448,6 @@
"27": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Lower Engine (0/2)",
"length": "Short",
"consoles": [
{
@@ -492,7 +464,6 @@
"28": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Security (0/2)",
"length": "Short",
"consoles": [
{
@@ -506,4 +477,4 @@
}
]
}
}
}
@@ -139,4 +139,4 @@
"center": null,
"right": 10
}
}
}
@@ -55,4 +55,4 @@
"y": 4.6280003
}
}
}
}
@@ -12,4 +12,4 @@
"y": 0
},
"spawnRadius": 1.5
}
}
@@ -5,4 +5,4 @@
"Doors": "DoorsSystemType",
"MushroomMixupSabotage": "MushroomMixupSabotageSystem",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "CollectSamples",
"hudText": "Jungle: Collect Samples (0/2)",
"length": "Common",
"consoles": [
{
@@ -19,7 +18,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "EnterIdCode",
"hudText": "Lookout: Enter Id Code",
"length": "Common",
"consoles": [
{
@@ -36,14 +34,12 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "ReplaceParts",
"hudText": "Reactor: Replace Parts (0/2)",
"length": "Common",
"consoles": []
},
"3": {
"type": "NormalPlayerTask",
"taskType": "RoastMarshmallow",
"hudText": "Beach: Collect Stick (0/2)",
"length": "Common",
"consoles": [
{
@@ -96,7 +92,6 @@
"4": {
"type": "NormalPlayerTask",
"taskType": "CatchFish",
"hudText": "Dock: Catch Fish (0/2)",
"length": "Long",
"consoles": [
{
@@ -122,35 +117,30 @@
"5": {
"type": "NormalPlayerTask",
"taskType": "CollectVegetables",
"hudText": "Greenhouse: Collect Vegetables (0/2)",
"length": "Long",
"consoles": []
},
"6": {
"type": "NormalPlayerTask",
"taskType": "ExtractFuel",
"hudText": "Upper Engine: Extract Fuel (0/2)",
"length": "Long",
"consoles": []
},
"7": {
"type": "NormalPlayerTask",
"taskType": "HelpCritter",
"hudText": "Jungle: Help Critter (0/2)",
"length": "Long",
"consoles": []
},
"8": {
"type": "NormalPlayerTask",
"taskType": "HoistSupplies",
"hudText": "Jungle: Hoist Supplies",
"length": "Long",
"consoles": []
},
"9": {
"type": "NormalPlayerTask",
"taskType": "PolishGem",
"hudText": "Mining Pit: Polish Gem",
"length": "Long",
"consoles": [
{
@@ -167,7 +157,6 @@
"10": {
"type": "NormalPlayerTask",
"taskType": "MineOres",
"hudText": "Mining Pit: Mine Ores",
"length": "Long",
"consoles": [
{
@@ -184,21 +173,18 @@
"11": {
"type": "NormalPlayerTask",
"taskType": "ReplaceWaterJug",
"hudText": "Storage: Replace Water Jug (0/2)",
"length": "Long",
"consoles": []
},
"12": {
"type": "NormalPlayerTask",
"taskType": "WaterPlants",
"hudText": "Storage: Water Plants (0/2)",
"length": "Long",
"consoles": []
},
"13": {
"type": "NormalPlayerTask",
"taskType": "AssembleArtifact",
"hudText": "Laboratory: Assemble Artifact",
"length": "Short",
"consoles": [
{
@@ -215,7 +201,6 @@
"14": {
"type": "NormalPlayerTask",
"taskType": "BuildSandcastle",
"hudText": "Splash Zone: Build Sandcastle",
"length": "Short",
"consoles": [
{
@@ -232,7 +217,6 @@
"15": {
"type": "NormalPlayerTask",
"taskType": "CollectShells",
"hudText": "Splash Zone: Collect Shells",
"length": "Short",
"consoles": [
{
@@ -276,7 +260,6 @@
"16": {
"type": "NormalPlayerTask",
"taskType": "CrankGenerator",
"hudText": "Kitchen: Crank Generator",
"length": "Short",
"consoles": [
{
@@ -302,21 +285,18 @@
"17": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Cafeteria: Empty Garbage",
"length": "Short",
"consoles": []
},
"18": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Splash Zone: Empty Garbage",
"length": "Short",
"consoles": []
},
"19": {
"type": "NormalPlayerTask",
"taskType": "FixAntenna",
"hudText": "Communications: Fix Antenna",
"length": "Short",
"consoles": [
{
@@ -333,7 +313,6 @@
"20": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "Dropship: Fix Wiring",
"length": "Short",
"consoles": [
{
@@ -359,7 +338,6 @@
"21": {
"type": "NormalPlayerTask",
"taskType": "LiftWeights",
"hudText": "Splash Zone: Lift Weights",
"length": "Short",
"consoles": [
{
@@ -376,7 +354,6 @@
"22": {
"type": "NormalPlayerTask",
"taskType": "MonitorMushroom",
"hudText": "Jungle: Monitor Mushroom",
"length": "Short",
"consoles": [
{
@@ -393,7 +370,6 @@
"23": {
"type": "NormalPlayerTask",
"taskType": "PlayVideogame",
"hudText": "The Dorm: Play Video Game (0/20)",
"length": "Short",
"consoles": [
{
@@ -410,7 +386,6 @@
"24": {
"type": "NormalPlayerTask",
"taskType": "RecordTemperature",
"hudText": "Laboratory: Record Temperature",
"length": "Short",
"consoles": [
{
@@ -445,7 +420,6 @@
"25": {
"type": "NormalPlayerTask",
"taskType": "RecordTemperature",
"hudText": "Greenhouse: Record Temperature",
"length": "Short",
"consoles": [
{
@@ -480,7 +454,6 @@
"26": {
"type": "NormalPlayerTask",
"taskType": "RecordTemperature",
"hudText": "Reactor: Record Temperature",
"length": "Short",
"consoles": [
{
@@ -515,7 +488,6 @@
"27": {
"type": "NormalPlayerTask",
"taskType": "TestFrisbee",
"hudText": "Splash Zone: Throw Frisbee",
"length": "Short",
"consoles": [
{
@@ -532,7 +504,6 @@
"28": {
"type": "NormalPlayerTask",
"taskType": "TuneRadio",
"hudText": "Communications: Find Signal (0/1)",
"length": "Short",
"consoles": [
{
@@ -546,4 +517,4 @@
}
]
}
}
}
@@ -99,4 +99,4 @@
"center": null,
"right": 3
}
}
}
@@ -1 +1 @@
{}
{}
@@ -12,4 +12,4 @@
"y": 0
},
"spawnRadius": 1.55
}
}
@@ -6,4 +6,4 @@
"LifeSupp": "LifeSuppSystemType",
"Ventilation": "VentilationSystem",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "Storage: Fix Wiring (0/3)",
"length": "Common",
"consoles": [
{
@@ -55,7 +54,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "EnterIdCode",
"hudText": "Admin: Enter Id Code",
"length": "Common",
"consoles": [
{
@@ -72,7 +70,6 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "SubmitScan",
"hudText": "MedBay: Submit Scan",
"length": "Long",
"consoles": [
{
@@ -89,7 +86,6 @@
"3": {
"type": "NormalPlayerTask",
"taskType": "ClearAsteroids",
"hudText": "Balcony: Clear Asteroids (0/20)",
"length": "Long",
"consoles": [
{
@@ -106,7 +102,6 @@
"4": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Admin (0/2)",
"length": "Long",
"consoles": [
{
@@ -123,7 +118,6 @@
"5": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Cafeteria (0/2)",
"length": "Long",
"consoles": [
{
@@ -140,7 +134,6 @@
"6": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Communications (0/2)",
"length": "Long",
"consoles": [
{
@@ -157,7 +150,6 @@
"7": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Launchpad (0/2)",
"length": "Long",
"consoles": [
{
@@ -174,7 +166,6 @@
"8": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to MedBay (0/2)",
"length": "Long",
"consoles": [
{
@@ -191,7 +182,6 @@
"9": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Office (0/2)",
"length": "Long",
"consoles": [
{
@@ -208,14 +198,12 @@
"10": {
"type": "NormalPlayerTask",
"taskType": "WaterPlants",
"hudText": "Storage: Water Plants (0/2)",
"length": "Long",
"consoles": []
},
"11": {
"type": "NormalPlayerTask",
"taskType": "StartReactor",
"hudText": "Reactor: Start Reactor",
"length": "Long",
"consoles": [
{
@@ -232,7 +220,6 @@
"12": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Greenhouse (0/2)",
"length": "Long",
"consoles": [
{
@@ -249,14 +236,12 @@
"13": {
"type": "NormalPlayerTask",
"taskType": "ChartCourse",
"hudText": "Admin: Chart Course",
"length": "Short",
"consoles": []
},
"14": {
"type": "NormalPlayerTask",
"taskType": "CleanO2Filter",
"hudText": "Greenhouse: Clean O2 Filter",
"length": "Short",
"consoles": [
{
@@ -273,14 +258,12 @@
"15": {
"type": "NormalPlayerTask",
"taskType": "FuelEngines",
"hudText": "Launchpad: Fuel Engines",
"length": "Short",
"consoles": []
},
"16": {
"type": "NormalPlayerTask",
"taskType": "AssembleArtifact",
"hudText": "Laboratory: Assemble Artifact",
"length": "Short",
"consoles": [
{
@@ -297,7 +280,6 @@
"17": {
"type": "NormalPlayerTask",
"taskType": "SortSamples",
"hudText": "Laboratory: Sort Samples",
"length": "Short",
"consoles": [
{
@@ -314,7 +296,6 @@
"18": {
"type": "NormalPlayerTask",
"taskType": "PrimeShields",
"hudText": "Admin: Prime Shields",
"length": "Short",
"consoles": [
{
@@ -331,14 +312,12 @@
"19": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Cafeteria: Empty Garbage",
"length": "Short",
"consoles": []
},
"20": {
"type": "NormalPlayerTask",
"taskType": "MeasureWeather",
"hudText": "Balcony: Measure Weather",
"length": "Short",
"consoles": [
{
@@ -355,7 +334,6 @@
"21": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Reactor: Divert Power to Laboratory (0/2)",
"length": "Short",
"consoles": [
{
@@ -372,7 +350,6 @@
"22": {
"type": "NormalPlayerTask",
"taskType": "BuyBeverage",
"hudText": "Cafeteria: Buy Beverage",
"length": "Short",
"consoles": [
{
@@ -389,7 +366,6 @@
"23": {
"type": "NormalPlayerTask",
"taskType": "ProcessData",
"hudText": "Office: Process Data",
"length": "Short",
"consoles": [
{
@@ -406,7 +382,6 @@
"24": {
"type": "NormalPlayerTask",
"taskType": "RunDiagnostics",
"hudText": "Launchpad: Run Diagnostics",
"length": "Short",
"consoles": [
{
@@ -423,7 +398,6 @@
"25": {
"type": "NormalPlayerTask",
"taskType": "UnlockManifolds",
"hudText": "Reactor: Unlock Manifolds",
"length": "Short",
"consoles": [
{
@@ -440,7 +414,6 @@
"26": {
"type": "NormalPlayerTask",
"taskType": "VentCleaning",
"hudText": "Admin: Clean Vent",
"length": "Short",
"consoles": [
{
@@ -544,4 +517,4 @@
}
]
}
}
}
@@ -109,4 +109,4 @@
"center": null,
"right": 10
}
}
}
@@ -111,4 +111,4 @@
"y": -11.361
}
}
}
}
@@ -12,4 +12,4 @@
"y": -17.515
},
"spawnRadius": 1
}
}
@@ -7,4 +7,4 @@
"Ventilation": "VentilationSystem",
"Laboratory": "ReactorSystemType",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "SwipeCard",
"hudText": "Office: Swipe Card",
"length": "Common",
"consoles": [
{
@@ -19,7 +18,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "InsertKeys",
"hudText": "Dropship: Insert Keys",
"length": "Common",
"consoles": [
{
@@ -36,7 +34,6 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "ScanBoardingPass",
"hudText": "Office: Scan Boarding Pass",
"length": "Common",
"consoles": [
{
@@ -53,7 +50,6 @@
"3": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "O2: Fix Wiring (0/3)",
"length": "Common",
"consoles": [
{
@@ -115,7 +111,6 @@
"4": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Weapons: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -132,7 +127,6 @@
"5": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Office: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -149,7 +143,6 @@
"6": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Electrical: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -166,7 +159,6 @@
"7": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Specimen Room: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -183,7 +175,6 @@
"8": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "O2: Download Data (0/2) ",
"length": "Long",
"consoles": [
{
@@ -200,7 +191,6 @@
"9": {
"type": "NormalPlayerTask",
"taskType": "StartReactor",
"hudText": "Specimen Room: Start Reactor",
"length": "Long",
"consoles": [
{
@@ -217,14 +207,12 @@
"10": {
"type": "NormalPlayerTask",
"taskType": "FuelEngines",
"hudText": "Storage: Fuel Engines (0/2)",
"length": "Long",
"consoles": []
},
"11": {
"type": "WaterWayTask",
"taskType": "OpenWaterways",
"hudText": "Boiler Room: Open Waterways (0/3)",
"length": "Long",
"consoles": [
{
@@ -259,7 +247,6 @@
"12": {
"type": "NormalPlayerTask",
"taskType": "InspectSample",
"hudText": "MedBay: Inspect Sample",
"length": "Long",
"consoles": [
{
@@ -276,14 +263,12 @@
"13": {
"type": "NormalPlayerTask",
"taskType": "ReplaceWaterJug",
"hudText": "Boiler Room: Replace Water Jug (0/2)",
"length": "Long",
"consoles": []
},
"14": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_GI: Fix Weather Node (0/2)",
"length": "Long",
"consoles": [
{
@@ -345,7 +330,6 @@
"15": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_IRO: Fix Weather Node (0/2)",
"length": "Long",
"consoles": [
{
@@ -407,7 +391,6 @@
"16": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_PD: Fix Weather Node (0/2)",
"length": "Long",
"consoles": [
{
@@ -469,7 +452,6 @@
"17": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_TB: Fix Weather Node (0/2)",
"length": "Long",
"consoles": [
{
@@ -531,7 +513,6 @@
"18": {
"type": "NormalPlayerTask",
"taskType": "RebootWifi",
"hudText": "Communications: Reboot Wifi",
"length": "Long",
"consoles": [
{
@@ -548,7 +529,6 @@
"19": {
"type": "NormalPlayerTask",
"taskType": "MonitorOxygen",
"hudText": "O2: Monitor Tree",
"length": "Short",
"consoles": [
{
@@ -565,7 +545,6 @@
"20": {
"type": "NormalPlayerTask",
"taskType": "UnlockManifolds",
"hudText": "Specimen Room: Unlock Manifolds",
"length": "Short",
"consoles": [
{
@@ -582,7 +561,6 @@
"21": {
"type": "NormalPlayerTask",
"taskType": "StoreArtifacts",
"hudText": "Specimen Room: Store Artifacts",
"length": "Short",
"consoles": [
{
@@ -599,7 +577,6 @@
"22": {
"type": "NormalPlayerTask",
"taskType": "FillCanisters",
"hudText": "O2: Fill Canisters",
"length": "Short",
"consoles": [
{
@@ -616,14 +593,12 @@
"23": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "O2: Empty Garbage",
"length": "Short",
"consoles": []
},
"24": {
"type": "NormalPlayerTask",
"taskType": "ChartCourse",
"hudText": "Dropship: Chart Course",
"length": "Short",
"consoles": [
{
@@ -640,7 +615,6 @@
"25": {
"type": "NormalPlayerTask",
"taskType": "SubmitScan",
"hudText": "MedBay: Submit Scan",
"length": "Short",
"consoles": [
{
@@ -657,7 +631,6 @@
"26": {
"type": "NormalPlayerTask",
"taskType": "ClearAsteroids",
"hudText": "Weapons: Clear Asteroids (0/20)",
"length": "Short",
"consoles": [
{
@@ -674,7 +647,6 @@
"27": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_CA: Fix Weather Node (0/2)",
"length": "Short",
"consoles": [
{
@@ -736,7 +708,6 @@
"28": {
"type": "WeatherNodeTask",
"taskType": "FixWeatherNode",
"hudText": "Node_MLG: Fix Weather Node (0/2)",
"length": "Short",
"consoles": [
{
@@ -798,7 +769,6 @@
"29": {
"type": "NormalPlayerTask",
"taskType": "AlignTelescope",
"hudText": "Laboratory: Align Telescope",
"length": "Short",
"consoles": [
{
@@ -815,7 +785,6 @@
"30": {
"type": "NormalPlayerTask",
"taskType": "RepairDrill",
"hudText": "Laboratory: Repair Drill",
"length": "Short",
"consoles": [
{
@@ -832,7 +801,6 @@
"31": {
"type": "NormalPlayerTask",
"taskType": "RecordTemperature",
"hudText": "Laboratory: Record Temperature",
"length": "Short",
"consoles": [
{
@@ -858,7 +826,6 @@
"32": {
"type": "NormalPlayerTask",
"taskType": "RecordTemperature",
"hudText": "Outside: Record Temperature",
"length": "Short",
"consoles": [
{
@@ -881,4 +848,4 @@
}
]
}
}
}
@@ -119,4 +119,4 @@
"center": null,
"right": 5
}
}
}
@@ -90,4 +90,4 @@
"y": -8.560801
}
}
}
}
@@ -12,4 +12,4 @@
"y": 0
},
"spawnRadius": 1.6
}
}
@@ -8,4 +8,4 @@
"LifeSupp": "LifeSuppSystemType",
"Ventilation": "VentilationSystem",
"Sabotage": "SabotageSystemType"
}
}
@@ -2,7 +2,6 @@
"0": {
"type": "NormalPlayerTask",
"taskType": "SwipeCard",
"hudText": "Admin: Swipe Card",
"length": "Common",
"consoles": [
{
@@ -19,7 +18,6 @@
"1": {
"type": "NormalPlayerTask",
"taskType": "FixWiring",
"hudText": "Storage: Fix Wiring (0/3)",
"length": "Common",
"consoles": [
{
@@ -81,7 +79,6 @@
"2": {
"type": "NormalPlayerTask",
"taskType": "ClearAsteroids",
"hudText": "Weapons: Clear Asteroids (0/20)",
"length": "Long",
"consoles": [
{
@@ -98,7 +95,6 @@
"3": {
"type": "NormalPlayerTask",
"taskType": "AlignEngineOutput",
"hudText": "Upper Engine: Align Engine Output (0/2)",
"length": "Long",
"consoles": [
{
@@ -124,7 +120,6 @@
"4": {
"type": "NormalPlayerTask",
"taskType": "SubmitScan",
"hudText": "MedBay: Submit Scan",
"length": "Long",
"consoles": [
{
@@ -141,7 +136,6 @@
"5": {
"type": "NormalPlayerTask",
"taskType": "InspectSample",
"hudText": "MedBay: Inspect Sample",
"length": "Long",
"consoles": [
{
@@ -158,14 +152,12 @@
"6": {
"type": "NormalPlayerTask",
"taskType": "FuelEngines",
"hudText": "Storage: Fuel Engines (0/2)",
"length": "Long",
"consoles": []
},
"7": {
"type": "NormalPlayerTask",
"taskType": "StartReactor",
"hudText": "Reactor: Start Reactor",
"length": "Long",
"consoles": [
{
@@ -182,21 +174,18 @@
"8": {
"type": "NormalPlayerTask",
"taskType": "EmptyChute",
"hudText": "O2: Empty Chute (0/2)",
"length": "Long",
"consoles": []
},
"9": {
"type": "NormalPlayerTask",
"taskType": "EmptyGarbage",
"hudText": "Cafeteria: Empty Garbage (0/2)",
"length": "Long",
"consoles": []
},
"10": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Communications: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -213,7 +202,6 @@
"11": {
"type": "NormalPlayerTask",
"taskType": "CalibrateDistributor",
"hudText": "Electrical: Calibrate Distributor",
"length": "Short",
"consoles": [
{
@@ -230,14 +218,12 @@
"12": {
"type": "NormalPlayerTask",
"taskType": "ChartCourse",
"hudText": "Navigation: Chart Course",
"length": "Short",
"consoles": []
},
"13": {
"type": "NormalPlayerTask",
"taskType": "CleanO2Filter",
"hudText": "O2: Clean O2 Filter",
"length": "Short",
"consoles": [
{
@@ -254,7 +240,6 @@
"14": {
"type": "NormalPlayerTask",
"taskType": "UnlockManifolds",
"hudText": "Reactor: Unlock Manifolds",
"length": "Short",
"consoles": [
{
@@ -271,7 +256,6 @@
"15": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Electrical: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -288,7 +272,6 @@
"16": {
"type": "NormalPlayerTask",
"taskType": "StabilizeSteering",
"hudText": "Navigation: Stabilize Steering",
"length": "Short",
"consoles": [
{
@@ -305,7 +288,6 @@
"17": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Weapons: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -322,7 +304,6 @@
"18": {
"type": "NormalPlayerTask",
"taskType": "PrimeShields",
"hudText": "Shields: Prime Shields",
"length": "Short",
"consoles": [
{
@@ -339,7 +320,6 @@
"19": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Cafeteria: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -356,7 +336,6 @@
"20": {
"type": "UploadDataTask",
"taskType": "UploadData",
"hudText": "Navigation: Download Data (0/2) ",
"length": "Short",
"consoles": [
{
@@ -373,7 +352,6 @@
"21": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Shields (0/2)",
"length": "Short",
"consoles": [
{
@@ -390,7 +368,6 @@
"22": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Weapons (0/2)",
"length": "Short",
"consoles": [
{
@@ -407,7 +384,6 @@
"23": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Communications (0/2)",
"length": "Short",
"consoles": [
{
@@ -424,7 +400,6 @@
"24": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Upper Engine (0/2)",
"length": "Short",
"consoles": [
{
@@ -441,7 +416,6 @@
"25": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to O2 (0/2)",
"length": "Short",
"consoles": [
{
@@ -458,7 +432,6 @@
"26": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Navigation (0/2)",
"length": "Short",
"consoles": [
{
@@ -475,7 +448,6 @@
"27": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Lower Engine (0/2)",
"length": "Short",
"consoles": [
{
@@ -492,7 +464,6 @@
"28": {
"type": "DivertPowerTask",
"taskType": "DivertPower",
"hudText": "Electrical: Divert Power to Security (0/2)",
"length": "Short",
"consoles": [
{
@@ -509,7 +480,6 @@
"29": {
"type": "NormalPlayerTask",
"taskType": "VentCleaning",
"hudText": "Electrical: Clean Vent",
"length": "Short",
"consoles": [
{
@@ -640,4 +610,4 @@
}
]
}
}
}
@@ -139,4 +139,4 @@
"center": null,
"right": null
}
}
}
+2
View File
@@ -5,5 +5,7 @@ namespace Impostor.Api.Innersloth
None,
Normal,
HideNSeek,
NormalFools,
SeekFools,
}
}
@@ -39,8 +39,8 @@ public static class GameOptionsFactory
return gameMode switch
{
GameModes.Normal => NormalGameOptions.Deserialize(optionsReader, version),
GameModes.HideNSeek => HideNSeekGameOptions.Deserialize(optionsReader, version),
GameModes.Normal or GameModes.NormalFools => NormalGameOptions.Deserialize(optionsReader, version),
GameModes.HideNSeek or GameModes.SeekFools => HideNSeekGameOptions.Deserialize(optionsReader, version),
_ => throw new ArgumentOutOfRangeException(),
};
}
@@ -62,9 +62,11 @@ public static class GameOptionsFactory
switch (gameMode)
{
case GameModes.Normal:
case GameModes.NormalFools:
((NormalGameOptions)gameOptions).Deserialize(optionsReader);
break;
case GameModes.HideNSeek:
case GameModes.SeekFools:
((HideNSeekGameOptions)gameOptions).Deserialize(optionsReader);
break;
default:
@@ -2,7 +2,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
public class HideNSeekGameOptions : IGameOptions
{
public const int LatestVersion = 7;
public const int LatestVersion = 8;
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;
@@ -96,6 +102,12 @@ public class HideNSeekGameOptions : IGameOptions
public void Deserialize(IMessageReader reader)
{
if (Version >= 8)
{
SpecialMode = (SpecialGameModes)reader.ReadByte();
RulesPreset = (RulesPresets)reader.ReadByte();
}
MaxPlayers = reader.ReadByte();
Keywords = (GameKeywords)reader.ReadInt32();
Map = (MapTypes)reader.ReadByte();
@@ -121,7 +133,7 @@ public class HideNSeekGameOptions : IGameOptions
MaxPingTime = reader.ReadSingle();
CrewmateTimeInVent = reader.ReadSingle();
if (Version > 7)
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<HideNSeekGameOptions>(Version);
}
@@ -129,6 +141,12 @@ public class HideNSeekGameOptions : IGameOptions
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 +172,7 @@ public class HideNSeekGameOptions : IGameOptions
writer.Write(MaxPingTime);
writer.Write(CrewmateTimeInVent);
if (Version > 7)
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>
@@ -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>
@@ -4,7 +4,7 @@ namespace Impostor.Api.Innersloth.GameOptions;
public class NormalGameOptions : IGameOptions
{
public const int LatestVersion = 7;
public const int LatestVersion = 8;
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;
@@ -133,6 +139,12 @@ public class NormalGameOptions : IGameOptions
public void Deserialize(IMessageReader reader)
{
if (Version >= 8)
{
SpecialMode = (SpecialGameModes)reader.ReadByte();
RulesPreset = (RulesPresets)reader.ReadByte();
}
MaxPlayers = reader.ReadByte();
Keywords = (GameKeywords)reader.ReadUInt32();
Map = (MapTypes)reader.ReadByte();
@@ -163,7 +175,7 @@ public class NormalGameOptions : IGameOptions
RoleOptions.Deserialize(reader);
if (Version > 7)
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
}
@@ -171,6 +183,12 @@ public class NormalGameOptions : IGameOptions
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);
@@ -201,7 +219,7 @@ public class NormalGameOptions : IGameOptions
RoleOptions.Serialize(writer);
if (Version > 7)
if (Version > LatestVersion)
{
IGameOptions.ThrowUnknownVersion<NormalGameOptions>(Version);
}
@@ -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);
}
}
@@ -31,6 +31,9 @@ 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),
_ => throw new ArgumentOutOfRangeException(nameof(roleType), roleType, null),
};
@@ -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);
}
}
@@ -14,6 +14,7 @@ public abstract class MapData
[MapTypes.Skeld] = new SkeldData(),
[MapTypes.MiraHQ] = new MiraData(),
[MapTypes.Polus] = new PolusData(),
[MapTypes.Dleks] = new AprilData(),
[MapTypes.Airship] = new AirshipData(),
[MapTypes.Fungle] = new FungleData(),
}.AsReadOnly();
+5 -1
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Impostor.Api.Innersloth;
@@ -7,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.
@@ -74,6 +75,9 @@ namespace Impostor.Api.Net
/// </summary>
PlatformSpecificData PlatformSpecificData { get; }
ValueTask<bool> ReportCheatAsync(CheatContext context, CheatCategory category, string message);
[Obsolete("Please use the overload that adds a cheat category")]
ValueTask<bool> ReportCheatAsync(CheatContext context, string message);
ValueTask HandleMessageAsync(IMessageReader message, MessageType messageType);
@@ -1,6 +1,6 @@
namespace Impostor.Api.Net.Inner.Objects
{
public interface IInnerGameData : IInnerNetObject
public interface IInnerGameData
{
}
}
@@ -0,0 +1,6 @@
namespace Impostor.Api.Net.Inner.Objects.ShipStatus
{
public interface IInnerDleksShipStatus : IInnerShipStatus
{
}
}
@@ -28,6 +28,7 @@ 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;
private static readonly Dictionary<byte, string> FlagCache;
@@ -41,7 +42,7 @@ namespace Impostor.Api.Net.Messages
public static string FlagToString(byte flag)
{
return FlagCache[flag];
return FlagCache.TryGetValue(flag, out var res) ? res : $"Unknown Flag {flag}";
}
}
}
@@ -1,22 +0,0 @@
using Impostor.Api.Innersloth.GameOptions;
namespace Impostor.Api.Net.Messages.Rpcs
{
public static class Rpc02SyncSettings
{
public static void Serialize(IMessageWriter writer, IGameOptions gameOptionsData)
{
GameOptionsFactory.Serialize(writer, gameOptionsData);
}
public static void Deserialize(IMessageReader reader, out IGameOptions gameOptionsData)
{
gameOptionsData = GameOptionsFactory.Deserialize(reader);
}
public static void DeserializeInto(IMessageReader reader, IGameOptions gameOptionsData)
{
GameOptionsFactory.DeserializeInto(reader, gameOptionsData);
}
}
}
@@ -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();
}
}
@@ -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();
}
}
}
Loaded 100 of 186 files, more files were not shown because too many files have changed in this diff. Show more