From f5cbc1242a96b06fcca9c47a7b2def5379bbb8db Mon Sep 17 00:00:00 2001 From: cddjr Date: Sat, 26 Mar 2022 20:49:09 +0800 Subject: [PATCH 1/2] #260: Reveal Votes --- appdata/il2cpp-functions.h | 7 +++ appdata/il2cpp-types.h | 59 ++++++++++++++++++++ gui/tabs/self_tab.cpp | 4 ++ hooks/MeetingHud.cpp | 109 +++++++++++++++++++++++++++++++++++-- hooks/_hooks.cpp | 2 + hooks/_hooks.h | 1 + user/state.cpp | 4 ++ user/state.hpp | 4 +- 8 files changed, 183 insertions(+), 7 deletions(-) diff --git a/appdata/il2cpp-functions.h b/appdata/il2cpp-functions.h index 4fa1cfac..bde58f62 100644 --- a/appdata/il2cpp-functions.h +++ b/appdata/il2cpp-functions.h @@ -6,6 +6,11 @@ DO_APP_FUNC(Type*, Type_GetType, (String* typeName, MethodInfo* method), "mscorl DO_APP_FUNC(GameObject*, Component_get_gameObject, (Component_1* __this, MethodInfo* method), "UnityEngine.CoreModule, UnityEngine.GameObject UnityEngine.Component::get_gameObject()"); DO_APP_FUNC(Transform*, Component_get_transform, (Component_1* __this, MethodInfo* method), "UnityEngine.CoreModule, UnityEngine.Transform UnityEngine.Component::get_transform()"); + +DO_APP_FUNC(void, Object_DestroyImmediate, (Object_1* obj, MethodInfo* method), "UnityEngine.CoreModule, System.Void UnityEngine.Object::DestroyImmediate(UnityEngine.Object)"); +DO_APP_FUNC(Component_1*, Component_GetComponent, (Component_1* __this, Type* type, MethodInfo* method), "UnityEngine.CoreModule, UnityEngine.Component UnityEngine.Component::GetComponent(System.Type)"); + +DO_APP_FUNC(Transform*, GameObject_get_transform, (GameObject* __this, MethodInfo* method), "UnityEngine.CoreModule, UnityEngine.Transform UnityEngine.GameObject::get_transform()"); DO_APP_FUNC(String*, Component_get_tag, (Component_1* __this, MethodInfo* method), "UnityEngine.CoreModule, System.String UnityEngine.Component::get_tag()"); DO_APP_FUNC(void, GameObject_set_layer, (GameObject* __this, int32_t value, MethodInfo* method), "UnityEngine.CoreModule, System.Void UnityEngine.GameObject::set_layer(System.Int32)"); DO_APP_FUNC(int32_t, GameObject_get_layer, (GameObject* __this, MethodInfo* method), "UnityEngine.CoreModule, System.Int32 UnityEngine.GameObject::get_layer()"); @@ -61,6 +66,8 @@ DO_APP_FUNC(void, MainMenuManager_Start, (MainMenuManager* __this, MethodInfo* m DO_APP_FUNC(void, MeetingHud_Awake, (MeetingHud* __this, MethodInfo* method), "Assembly-CSharp, System.Void MeetingHud::Awake()"); DO_APP_FUNC(void, MeetingHud_Close, (MeetingHud* __this, MethodInfo* method), "Assembly-CSharp, System.Void MeetingHud::Close()"); DO_APP_FUNC(void, MeetingHud_Update, (MeetingHud* __this, MethodInfo* method), "Assembly-CSharp, System.Void MeetingHud::Update()"); +DO_APP_FUNC(void, MeetingHud_BloopAVoteIcon, (MeetingHud* __this, GameData_PlayerInfo* voterPlayer, int index, Transform* parent, MethodInfo* method), "Assembly-CSharp, System.Void MeetingHud::BloopAVoteIcon(GameData.PlayerInfo, System.Int32, UnityEngine.Transform)"); +DO_APP_FUNC(void, MeetingHud_PopulateResults, (MeetingHud* __this, void* states, MethodInfo* method), "Assembly-CSharp, System.Void MeetingHud::PopulateResults(MeetingHud.VoterState[])"); DO_APP_FUNC(void, MovingPlatformBehaviour_SetSide, (MovingPlatformBehaviour* __this, bool isLeft, MethodInfo* method), "Assembly-CSharp, System.Void MovingPlatformBehaviour::SetSide(System.Boolean)"); diff --git a/appdata/il2cpp-types.h b/appdata/il2cpp-types.h index d18cdcb5..4d70183d 100644 --- a/appdata/il2cpp-types.h +++ b/appdata/il2cpp-types.h @@ -8747,6 +8747,65 @@ namespace app }; #pragma endregion +#pragma region SpriteRenderer__Array + struct SpriteRenderer__Array + { + Il2CppObject obj; + Il2CppArrayBounds* bounds; + il2cpp_array_size_t max_length; + struct SpriteRenderer* vector[32]; + }; +#pragma endregion + +#pragma region List_SpriteRenderer_ + struct __declspec(align(4)) List_SpriteRenderer__Fields + { + struct SpriteRenderer__Array* _items; + int32_t _size; + int32_t _version; + struct Object* _syncRoot; + }; + + struct List_SpriteRenderer_ + { + void* klass; + void* monitor; + struct List_SpriteRenderer__Fields fields; + }; +#pragma endregion + +#pragma region VoteSpreader + struct VoteSpreader__Fields { + struct MonoBehaviour__Fields _; + struct List_SpriteRenderer_* Votes; + void* VoteRange; + int32_t maxVotesBeforeSmoosh; + float CounterY; + float adjustRate; + }; + + struct VoteSpreader { + struct VoteSpreader__Class* klass; + void* monitor; + struct VoteSpreader__Fields fields; + }; + + struct VoteSpreader__VTable { + VirtualInvokeData Equals; + VirtualInvokeData Finalize; + VirtualInvokeData GetHashCode; + VirtualInvokeData ToString; + }; + + struct VoteSpreader__Class { + Il2CppClass_0 _0; + void* static_fields; + const Il2CppRGCTXData* rgctx_data; + Il2CppClass_1 _1; + struct VoteSpreader__VTable vtable; + }; +#pragma endregion + #pragma region MeetingHud #if defined(_CPLUSPLUS_) diff --git a/gui/tabs/self_tab.cpp b/gui/tabs/self_tab.cpp index c069f106..b2aeb87f 100644 --- a/gui/tabs/self_tab.cpp +++ b/gui/tabs/self_tab.cpp @@ -60,6 +60,10 @@ namespace SelfTab { State.Save(); } + if (ImGui::Checkbox("Reveal Votes", &State.RevealVotes)) { + State.Save(); + } + if (ImGui::Checkbox("See Ghosts", &State.ShowGhosts)) { State.Save(); } diff --git a/hooks/MeetingHud.cpp b/hooks/MeetingHud.cpp index 6f1f95ba..2f4720f7 100644 --- a/hooks/MeetingHud.cpp +++ b/hooks/MeetingHud.cpp @@ -5,10 +5,16 @@ #include "logger.h" #include +static app::Type* voteSpreaderType; + void dMeetingHud_Awake(MeetingHud* __this, MethodInfo* method) { - State.voteMonitor.reset(); + State.voteMonitor.clear(); State.InMeeting = true; + if (!voteSpreaderType) { + voteSpreaderType = app::Type_GetType(convert_to_string(translate_type_name("VoteSpreader, Assembly-CSharp")), nullptr); + } + MeetingHud_Awake(__this, method); } @@ -23,6 +29,38 @@ void dMeetingHud_Close(MeetingHud* __this, MethodInfo* method) { MeetingHud_Close(__this, method); } +static void Transform_RemoveAllVotes(app::Transform* transform) { + auto voteSpreader = (VoteSpreader*)app::Component_GetComponent((app::Component_1*)transform, voteSpreaderType, nullptr); + if (!voteSpreader) return; + auto votes = voteSpreader->fields.Votes; + if (votes->fields._size == 0) return; + for (size_t j = 0; j < votes->fields._size; j++) { + auto spriteRenderer = votes->fields._items->vector[j]; + app::Object_DestroyImmediate((app::Object_1*)spriteRenderer, nullptr); + } + //TODO: List_Clear + votes->fields._size = 0; + votes->fields._version++; +} + +void dMeetingHud_PopulateResults(MeetingHud* __this, void* states, MethodInfo* method) { + // remove all votes before populating results + do { + PlayerVoteArea__Array* playerStates = __this->fields.playerStates; + for (size_t i = 0; i < playerStates->max_length; i++) { + auto votedForArea = playerStates->vector[i]; + auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); + Transform_RemoveAllVotes(transform); + } + if (__this->fields.SkippedVoting) { + auto transform = app::GameObject_get_transform(__this->fields.SkippedVoting, nullptr); + Transform_RemoveAllVotes(transform); + } + } while (false); + + MeetingHud_PopulateResults(__this, states, method); +} + void dMeetingHud_Update(MeetingHud* __this, MethodInfo* method) { PlayerVoteArea__Array* playerStates = __this->fields.playerStates; for (size_t i = 0; i < playerStates->max_length; i++) { @@ -61,19 +99,78 @@ void dMeetingHud_Update(MeetingHud* __this, MethodInfo* method) { bool didVote = (playerVoteArea->fields.VotedFor != 0xFF); // We are goign to check to see if they voted, then we are going to check to see who they voted for, finally we are going to check to see if we already recorded a vote for them // votedFor will either contain the id of the person they voted for, -1 if they skipped, or -2 if they didn't vote. We don't want to record people who didn't vote - if (isVotingState && didVote && playerVoteArea->fields.VotedFor != -2 && !State.voteMonitor[playerData->fields.PlayerId]) + if (isVotingState && didVote && playerVoteArea->fields.VotedFor != -2 && State.voteMonitor.find(playerData->fields.PlayerId) == State.voteMonitor.end()) { State.rawEvents.push_back(std::make_unique(GetEventPlayer(playerData).value(), GetEventPlayer(GetPlayerDataById(playerVoteArea->fields.VotedFor)))); State.liveReplayEvents.push_back(std::make_unique(GetEventPlayer(playerData).value(), GetEventPlayer(GetPlayerDataById(playerVoteArea->fields.VotedFor)))); - State.voteMonitor[playerData->fields.PlayerId] = true; + State.voteMonitor[playerData->fields.PlayerId] = playerVoteArea->fields.VotedFor; STREAM_DEBUG("Id " << +playerData->fields.PlayerId << " voted for " << +playerVoteArea->fields.VotedFor); + + if (playerVoteArea->fields.VotedFor != 253) { + for (size_t j = 0; j < playerStates->max_length; j++) { + auto votedForArea = playerStates->vector[j]; + if (votedForArea->fields.TargetPlayerId == playerVoteArea->fields.VotedFor) { + auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); + MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); + break; + } + } + } + else if (__this->fields.SkippedVoting) { + auto transform = app::GameObject_get_transform(__this->fields.SkippedVoting, nullptr); + MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); + } } - else if (!didVote && State.voteMonitor[playerData->fields.PlayerId]) + else if (!didVote && State.voteMonitor.find(playerData->fields.PlayerId) != State.voteMonitor.end()) { - State.voteMonitor[playerData->fields.PlayerId] = false; //Likely disconnected player + auto it = State.voteMonitor.find(playerData->fields.PlayerId); + auto votedFor = it->second; + State.voteMonitor.erase(it); //Likely disconnected player + + // Remove all votes for disconnected player + for (size_t i = 0; i < playerStates->max_length; i++) { + auto votedForArea = playerStates->vector[i]; + if (playerStates->vector[i]->fields.TargetPlayerId == votedFor) { + auto votedForArea = playerStates->vector[i]; + auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); + Transform_RemoveAllVotes(transform); + break; + } + } } } } - + + do { + bool isVotingState = __this->fields.state == app::MeetingHud_VoteStates__Enum::NotVoted + || __this->fields.state == app::MeetingHud_VoteStates__Enum::Voted; + if (!isVotingState) { + break; + } + + for (size_t i = 0; i < playerStates->max_length; i++) { + auto votedForArea = playerStates->vector[i]; + auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); + auto voteSpreader = (VoteSpreader*)app::Component_GetComponent((app::Component_1*)transform, voteSpreaderType, nullptr); + if (!voteSpreader) continue; + auto votes = voteSpreader->fields.Votes; + for (size_t j = 0; j < votes->fields._size; j++) { + auto spriteRenderer = votes->fields._items->vector[j]; + auto gameObject = app::Component_get_gameObject((app::Component_1*)spriteRenderer, nullptr); + app::GameObject_SetActive(gameObject, State.RevealVotes, nullptr); + } + } + + if (__this->fields.SkippedVoting) { + bool showSkipped = false; + for (auto pair : State.voteMonitor) { + if (pair.second == 253) { + showSkipped = State.RevealVotes; + break; + } + } + app::GameObject_SetActive(__this->fields.SkippedVoting, showSkipped, nullptr); + } + } while (false); MeetingHud_Update(__this, method); } \ No newline at end of file diff --git a/hooks/_hooks.cpp b/hooks/_hooks.cpp index 2162f89b..1cd16d4a 100644 --- a/hooks/_hooks.cpp +++ b/hooks/_hooks.cpp @@ -68,6 +68,7 @@ void DetourInitilization() { HOOKFUNC(PlayerControl_Shapeshift); HOOKFUNC(PlayerControl_ProtectPlayer); HOOKFUNC(MeetingHud_Update); + HOOKFUNC(MeetingHud_PopulateResults); HOOKFUNC(ShipStatus_CalculateLightRadius); HOOKFUNC(AirshipStatus_CalculateLightRadius); HOOKFUNC(ShipStatus_OnEnable); @@ -151,6 +152,7 @@ void DetourUninitialization() UNHOOKFUNC(PlayerControl_Shapeshift); UNHOOKFUNC(PlayerControl_ProtectPlayer); UNHOOKFUNC(MeetingHud_Update); + UNHOOKFUNC(MeetingHud_PopulateResults); UNHOOKFUNC(AirshipStatus_CalculateLightRadius); UNHOOKFUNC(ShipStatus_CalculateLightRadius); UNHOOKFUNC(ShipStatus_OnEnable); diff --git a/hooks/_hooks.h b/hooks/_hooks.h index a6dd9362..50af6509 100644 --- a/hooks/_hooks.h +++ b/hooks/_hooks.h @@ -29,6 +29,7 @@ void dScreenJoystick_FixedUpdate(ScreenJoystick* __this, MethodInfo* method); void dMeetingHud_Awake(MeetingHud* __this, MethodInfo* method); void dMeetingHud_Close(MeetingHud* __this, MethodInfo* method); void dMeetingHud_Update(MeetingHud* __this, MethodInfo* method); +void dMeetingHud_PopulateResults(MeetingHud* __this, void* states, MethodInfo* method); void dPlainDoor_SetDoorway(PlainDoor* __this, bool open, MethodInfo* method); void dPlayerControl_CompleteTask(PlayerControl* __this, uint32_t idx, MethodInfo* method); void dPlayerControl_FixedUpdate(PlayerControl* __this, MethodInfo* method); diff --git a/user/state.cpp b/user/state.cpp index fb492dba..2acea617 100644 --- a/user/state.cpp +++ b/user/state.cpp @@ -64,6 +64,8 @@ void Settings::Load() { j.at("AutoOpenDoors").get_to(this->AutoOpenDoors); j.at("MoveInVent").get_to(this->MoveInVent); + j.at("RevealVotes").get_to(this->RevealVotes); + j.at("ShowConsole").get_to(this->ShowConsole); j.at("ShowUnityLogs").get_to(this->ShowUnityLogs); } catch (...) { @@ -124,6 +126,8 @@ void Settings::Save() { {"AutoOpenDoors", this->AutoOpenDoors}, {"MoveInVent", this->MoveInVent}, + {"RevealVotes", this->RevealVotes}, + {"ShowConsole", this->ShowConsole}, {"ShowUnityLogs", this->ShowUnityLogs} }; diff --git a/user/state.hpp b/user/state.hpp index 41e1a07c..b59117fd 100644 --- a/user/state.hpp +++ b/user/state.hpp @@ -38,6 +38,8 @@ class Settings { bool UnlockVents = false; bool ShowGhosts = false; + bool RevealVotes = false; + bool RevealRoles = false; bool AbbreviatedRoleNames = false; int PrevKillDistance = 0; @@ -100,7 +102,7 @@ class Settings { bool Replay_IsPlaying = true; bool Replay_IsLive = true; - std::bitset<0xFF> voteMonitor; + std::map voteMonitor; std::vector aumUsers; int32_t rpcCooldown = 15; From 39f99a278990c19505c89e43372ff6e4d2fef2b2 Mon Sep 17 00:00:00 2001 From: cddjr Date: Wed, 30 Mar 2022 01:43:31 +0800 Subject: [PATCH 2/2] avoid duplicate votes --- hooks/MeetingHud.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/hooks/MeetingHud.cpp b/hooks/MeetingHud.cpp index 2f4720f7..779c8465 100644 --- a/hooks/MeetingHud.cpp +++ b/hooks/MeetingHud.cpp @@ -106,19 +106,25 @@ void dMeetingHud_Update(MeetingHud* __this, MethodInfo* method) { State.voteMonitor[playerData->fields.PlayerId] = playerVoteArea->fields.VotedFor; STREAM_DEBUG("Id " << +playerData->fields.PlayerId << " voted for " << +playerVoteArea->fields.VotedFor); - if (playerVoteArea->fields.VotedFor != 253) { - for (size_t j = 0; j < playerStates->max_length; j++) { - auto votedForArea = playerStates->vector[j]; - if (votedForArea->fields.TargetPlayerId == playerVoteArea->fields.VotedFor) { - auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); - MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); - break; + // avoid duplicate votes + if (__this->fields.state < app::MeetingHud_VoteStates__Enum::Results) { + //auto isAnonymousVotes = (*Game::pGameOptionsData)->fields.AnonymousVotes; + //(*Game::pGameOptionsData)->fields.AnonymousVotes = false; + if (playerVoteArea->fields.VotedFor != 253) { + for (size_t j = 0; j < playerStates->max_length; j++) { + auto votedForArea = playerStates->vector[j]; + if (votedForArea->fields.TargetPlayerId == playerVoteArea->fields.VotedFor) { + auto transform = app::Component_get_transform((app::Component_1*)votedForArea, nullptr); + MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); + break; + } } } - } - else if (__this->fields.SkippedVoting) { - auto transform = app::GameObject_get_transform(__this->fields.SkippedVoting, nullptr); - MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); + else if (__this->fields.SkippedVoting) { + auto transform = app::GameObject_get_transform(__this->fields.SkippedVoting, nullptr); + MeetingHud_BloopAVoteIcon(__this, playerData, 0, transform, nullptr); + } + //(*Game::pGameOptionsData)->fields.AnonymousVotes = isAnonymousVotes; } } else if (!didVote && State.voteMonitor.find(playerData->fields.PlayerId) != State.voteMonitor.end())