mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-21 23:58:51 -05:00
Refactor CrowdControl Setup (#1890)
This commit is contained in:
parent
be72f48cea
commit
e6e7a7b549
@ -14,6 +14,10 @@ set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux")
|
||||
set(BUILD_CROWD_CONTROL ON)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
include(CMake/automate-vcpkg.cmake)
|
||||
|
||||
|
@ -49,6 +49,15 @@ RUN curl -sLO https://libsdl.org/release/SDL2-${SDL2VER}.tar.gz && \
|
||||
rm ../SDL2-${SDL2VER}.tar.gz && \
|
||||
cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
|
||||
|
||||
ENV SDL2NETVER=2.2.0
|
||||
RUN curl -sLO https://www.libsdl.org/projects/SDL_net/release/SDL2_net-${SDL2NETVER}.tar.gz && \
|
||||
tar -xzf SDL2_net-${SDL2NETVER}.tar.gz && \
|
||||
cd SDL2_net-${SDL2NETVER} && \
|
||||
./configure --build=x86_64-linux-gnu && \
|
||||
make -j$(nproc) && make install && \
|
||||
rm ../SDL2_net-${SDL2NETVER}.tar.gz && \
|
||||
cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
|
||||
|
||||
RUN \
|
||||
ln -sf /proc/self/mounts /etc/mtab && \
|
||||
mkdir -p /usr/local/share/keyring/ && \
|
||||
|
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -60,7 +60,7 @@ pipeline {
|
||||
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
|
||||
unstash 'assets'
|
||||
bat """
|
||||
"${env.CMAKE}" -S . -B "build\\${env.PLATFORM}" -G "Visual Studio 17 2022" -T ${env.TOOLSET} -A ${env.PLATFORM} -D Python_EXECUTABLE=${env.PYTHON} -D CMAKE_BUILD_TYPE:STRING=Release
|
||||
"${env.CMAKE}" -S . -B "build\\${env.PLATFORM}" -G "Visual Studio 17 2022" -T ${env.TOOLSET} -A ${env.PLATFORM} -D Python_EXECUTABLE=${env.PYTHON} -DCMAKE_BUILD_TYPE:STRING=Release
|
||||
"${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --target OTRGui --config Release
|
||||
"${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --config Release
|
||||
cd ".\\build\\${env.PLATFORM}"
|
||||
|
@ -250,7 +250,7 @@ set(Header_Files__soh__Enhancements__item_tables
|
||||
|
||||
source_group("Header Files\\soh\\Enhancements\\item-tables" FILES ${Header_Files__soh__Enhancements__item_tables})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if (BUILD_CROWD_CONTROL)
|
||||
set(Header_Files__soh__Enhancements__crowd_control
|
||||
"soh/Enhancements/crowd-control/CrowdControl.h"
|
||||
)
|
||||
@ -397,7 +397,7 @@ set(Source_Files__soh__Enhancements__item_tables
|
||||
|
||||
source_group("Source Files\\soh\\Enhancements\\item-tables" FILES ${Source_Files__soh__Enhancements__item_tables})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if (BUILD_CROWD_CONTROL)
|
||||
set(Source_Files__soh__Enhancements__crowd_control
|
||||
"soh/Enhancements/crowd-control/CrowdControl.cpp"
|
||||
)
|
||||
@ -1710,7 +1710,7 @@ endif()
|
||||
find_package(SDL2)
|
||||
set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
if (BUILD_CROWD_CONTROL)
|
||||
find_package(SDL2_net)
|
||||
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
|
||||
endif()
|
||||
@ -1741,6 +1741,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
"$<$<CONFIG:Release>:"
|
||||
"NDEBUG"
|
||||
">"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
|
||||
"INCLUDE_GAME_PRINTF;"
|
||||
"ENABLE_CROWD_CONTROL;"
|
||||
"UNICODE;"
|
||||
@ -1787,6 +1788,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
|
||||
"$<$<CONFIG:Release>:"
|
||||
"NDEBUG"
|
||||
">"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
|
||||
"SPDLOG_ACTIVE_LEVEL=0;"
|
||||
"_CONSOLE;"
|
||||
"_CRT_SECURE_NO_WARNINGS;"
|
||||
@ -1984,7 +1986,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
"glu32;"
|
||||
"SDL2::SDL2;"
|
||||
"SDL2::SDL2main;"
|
||||
"SDL2_net::SDL2_net-static;"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>"
|
||||
"glfw;"
|
||||
"winmm;"
|
||||
"imm32;"
|
||||
@ -2036,6 +2038,7 @@ else()
|
||||
"libultraship;"
|
||||
"ZAPDUtils;"
|
||||
SDL2::SDL2
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>"
|
||||
${CMAKE_DL_LIBS}
|
||||
Threads::Threads
|
||||
)
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <libultraship/Console.h>
|
||||
#include <libultraship/ImGuiImpl.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <regex>
|
||||
|
||||
extern "C" {
|
||||
@ -19,268 +21,305 @@ extern PlayState* gPlayState;
|
||||
|
||||
#define CMD_EXECUTE SohImGui::GetConsole()->Dispatch
|
||||
|
||||
void CrowdControl::InitCrowdControl() {
|
||||
#define EFFECT_HIGH_GRAVITY "high_gravity"
|
||||
#define EFFECT_LOW_GRAVITY "low_gravity"
|
||||
#define EFFECT_DAMAGE_MULTIPLIER "damage_multiplier"
|
||||
#define EFFECT_DEFENSE_MULTIPLIER "defense_multiplier"
|
||||
#define EFFECT_GIANT_LINK "giant_link"
|
||||
#define EFFECT_MINISH_LINK "minish_link"
|
||||
#define EFFECT_INVISIBLE_LINK "invisible"
|
||||
#define EFFECT_PAPER_LINK "paper_link"
|
||||
#define EFFECT_FREEZE "freeze"
|
||||
#define EFFECT_DAMAGE "damage"
|
||||
#define EFFECT_HEAL "heal"
|
||||
#define EFFECT_KNOCKBACK "knockback"
|
||||
#define EFFECT_ELECTROCUTE "electrocute"
|
||||
#define EFFECT_BURN "burn"
|
||||
#define EFFECT_KILL "kill"
|
||||
#define EFFECT_HOVER_BOOTS "hover_boots"
|
||||
#define EFFECT_IRON_BOOTS "iron_boots"
|
||||
#define EFFECT_ADD_HEART_CONTAINER "add_heart_container"
|
||||
#define EFFECT_REMOVE_HEART_CONTAINER "remove_heart_container"
|
||||
#define EFFECT_NO_UI "no_ui"
|
||||
#define EFFECT_FILL_MAGIC "fill_magic"
|
||||
#define EFFECT_EMPTY_MAGIC "empty_magic"
|
||||
#define EFFECT_OHKO "ohko"
|
||||
#define EFFECT_PACIFIST "pacifist"
|
||||
#define EFFECT_RAINSTORM "rainstorm"
|
||||
#define EFFECT_REVERSE_CONTROLS "reverse"
|
||||
#define EFFECT_ADD_RUPEES "add_rupees"
|
||||
#define EFFECT_REMOVE_RUPEES "remove_rupees"
|
||||
#define EFFECT_INCREASE_SPEED "increase_speed"
|
||||
#define EFFECT_DECREASE_SPEED "decrease_speed"
|
||||
#define EFFECT_NO_Z_TARGETING "no_z"
|
||||
|
||||
#define EFFECT_SPAWN_WALLMASTER "spawn_wallmaster"
|
||||
#define EFFECT_SPAWN_ARWING "spawn_arwing"
|
||||
#define EFFECT_SPAWN_DARK_LINK "spawn_darklink"
|
||||
#define EFFECT_SPAWN_STALFOS "spawn_stalfos"
|
||||
#define EFFECT_SPAWN_WOLFOS "spawn_wolfos"
|
||||
#define EFFECT_SPAWN_FREEZARD "spawn_freezard"
|
||||
#define EFFECT_SPAWN_KEESE "spawn_keese"
|
||||
#define EFFECT_SPAWN_ICE_KEESE "spawn_icekeese"
|
||||
#define EFFECT_SPAWN_FIRE_KEESE "spawn_firekeese"
|
||||
#define EFFECT_SPAWN_TEKTITE "spawn_tektite"
|
||||
#define EFFECT_SPAWN_LIKE_LIKE "spawn_likelike"
|
||||
#define EFFECT_SPAWN_CUCCO_STORM "cucco_storm"
|
||||
|
||||
|
||||
void CrowdControl::Init() {
|
||||
SDLNet_Init();
|
||||
}
|
||||
|
||||
void CrowdControl::Shutdown() {
|
||||
SDLNet_Quit();
|
||||
}
|
||||
|
||||
void CrowdControl::Enable() {
|
||||
if (isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDLNet_ResolveHost(&ip, "127.0.0.1", 43384) == -1) {
|
||||
printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_ResolveHost: {}", SDLNet_GetError());
|
||||
}
|
||||
|
||||
ccThreadReceive = std::thread(&CrowdControl::ReceiveFromCrowdControl, this);
|
||||
isEnabled = true;
|
||||
ccThreadReceive = std::thread(&CrowdControl::ListenToServer, this);
|
||||
ccThreadProcess = std::thread(&CrowdControl::ProcessActiveEffects, this);
|
||||
}
|
||||
|
||||
void CrowdControl::RunCrowdControl(CCPacket* packet) {
|
||||
bool paused = false;
|
||||
EffectResult lastResult = EffectResult::NotReady;
|
||||
bool isTimed = packet->timeRemaining > 0;
|
||||
|
||||
while (connected) {
|
||||
EffectResult effectResult = ExecuteEffect(packet->effectType.c_str(), packet->effectValue, 0);
|
||||
if (effectResult == EffectResult::Success) {
|
||||
// If we have a success after being previously paused, we fire the Resumed event
|
||||
if (paused && packet->timeRemaining > 0) {
|
||||
paused = false;
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = EffectResult::Resumed;
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
}
|
||||
|
||||
// If time remaining has reached 0 or was 0, we have finished let's remove the command and end the thread
|
||||
if (packet->timeRemaining <= 0) {
|
||||
receivedCommandsMutex.lock();
|
||||
receivedCommands.erase(std::remove(receivedCommands.begin(), receivedCommands.end(), packet), receivedCommands.end());
|
||||
receivedCommandsMutex.unlock();
|
||||
RemoveEffect(packet->effectType.c_str());
|
||||
|
||||
// If not timed, let's fire the one and only success
|
||||
if (!isTimed) {
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = EffectResult::Success;
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
|
||||
delete packet;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrement remaining time by the second that elapsed between thread runs
|
||||
packet->timeRemaining -= 1000;
|
||||
// We don't want to emit repeated events, so ensure we only emit changes in events
|
||||
if (effectResult != lastResult) {
|
||||
lastResult = effectResult;
|
||||
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = effectResult;
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
}
|
||||
} else if (effectResult == EffectResult::Retry && paused == false && packet->timeRemaining > 0) {
|
||||
paused = true;
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = EffectResult::Paused;
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
void CrowdControl::Disable() {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
isEnabled = false;
|
||||
ccThreadReceive.join();
|
||||
ccThreadProcess.join();
|
||||
}
|
||||
|
||||
void CrowdControl::ReceiveFromCrowdControl()
|
||||
{
|
||||
printf("Waiting for a connection from Crowd Control...");
|
||||
void CrowdControl::ListenToServer() {
|
||||
while (isEnabled) {
|
||||
while (!connected) {
|
||||
SPDLOG_TRACE("[CrowdControl] Attempting to make connection to server...");
|
||||
tcpsock = SDLNet_TCP_Open(&ip);
|
||||
|
||||
while (!connected && CVar_GetS32("gCrowdControl", 0)) {
|
||||
tcpsock = SDLNet_TCP_Open(&ip);
|
||||
|
||||
if (tcpsock) {
|
||||
connected = true;
|
||||
printf("Connected to Crowd Control!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (connected && CVar_GetS32("gCrowdControl", 0) && tcpsock) {
|
||||
int len = SDLNet_TCP_Recv(tcpsock, &received, sizeof(received));
|
||||
|
||||
if (!len || !tcpsock || len == -1) {
|
||||
printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError());
|
||||
break;
|
||||
if (tcpsock) {
|
||||
connected = true;
|
||||
SPDLOG_TRACE("[CrowdControl] Connection to server established!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
nlohmann::json dataReceived = nlohmann::json::parse(received);
|
||||
auto socketSet = SDLNet_AllocSocketSet(1);
|
||||
SDLNet_TCP_AddSocket(socketSet, tcpsock);
|
||||
|
||||
CCPacket* packet = new CCPacket();
|
||||
packet->packetId = dataReceived["id"];
|
||||
auto parameters = dataReceived["parameters"];
|
||||
if (parameters.size() > 0) {
|
||||
packet->effectValue = dataReceived["parameters"][0];
|
||||
}
|
||||
packet->effectType = dataReceived["code"].get<std::string>();
|
||||
// Listen to socket messages
|
||||
while (connected && tcpsock && isEnabled) {
|
||||
// we check first if socket has data, to not block in the TCP_Recv
|
||||
int socketsReady = SDLNet_CheckSockets(socketSet, 0);
|
||||
|
||||
if (packet->effectType == "high_gravity" ||
|
||||
packet->effectType == "low_gravity") {
|
||||
packet->effectCategory = "gravity";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "damage_multiplier"
|
||||
|| packet->effectType == "defense_multiplier"
|
||||
) {
|
||||
packet->effectCategory = "defense";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "giant_link" ||
|
||||
packet->effectType == "minish_link" ||
|
||||
packet->effectType == "invisible" ||
|
||||
packet->effectType == "paper_link") {
|
||||
packet->effectCategory = "link_size";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "freeze" ||
|
||||
packet->effectType == "damage" ||
|
||||
packet->effectType == "heal" ||
|
||||
packet->effectType == "knockback" ||
|
||||
packet->effectType == "electrocute" ||
|
||||
packet->effectType == "burn" ||
|
||||
packet->effectType == "kill") {
|
||||
packet->effectCategory = "link_damage";
|
||||
}
|
||||
else if (packet->effectType == "hover_boots" ||
|
||||
packet->effectType == "iron_boots") {
|
||||
packet->effectCategory = "boots";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "add_heart_container" ||
|
||||
packet->effectType == "remove_heart_container") {
|
||||
packet->effectCategory = "heart_container";
|
||||
}
|
||||
else if (packet->effectType == "no_ui") {
|
||||
packet->effectCategory = "ui";
|
||||
packet->timeRemaining = 60000;
|
||||
}
|
||||
else if (packet->effectType == "fill_magic" ||
|
||||
packet->effectType == "empty_magic") {
|
||||
packet->effectCategory = "magic";
|
||||
}
|
||||
else if (packet->effectType == "ohko") {
|
||||
packet->effectCategory = "ohko";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "pacifist") {
|
||||
packet->effectCategory = "pacifist";
|
||||
packet->timeRemaining = 15000;
|
||||
}
|
||||
else if (packet->effectType == "rainstorm") {
|
||||
packet->effectCategory = "weather";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "reverse") {
|
||||
packet->effectCategory = "controls";
|
||||
packet->timeRemaining = 60000;
|
||||
}
|
||||
else if (packet->effectType == "add_rupees"
|
||||
|| packet->effectType == "remove_rupees"
|
||||
) {
|
||||
packet->effectCategory = "rupees";
|
||||
}
|
||||
else if (packet->effectType == "increase_speed"
|
||||
|| packet->effectType == "decrease_speed"
|
||||
) {
|
||||
packet->effectCategory = "speed";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "no_z") {
|
||||
packet->effectCategory = "no_z";
|
||||
packet->timeRemaining = 30000;
|
||||
}
|
||||
else if (packet->effectType == "spawn_wallmaster" ||
|
||||
packet->effectType == "spawn_arwing" ||
|
||||
packet->effectType == "spawn_darklink" ||
|
||||
packet->effectType == "spawn_stalfos" ||
|
||||
packet->effectType == "spawn_wolfos" ||
|
||||
packet->effectType == "spawn_freezard" ||
|
||||
packet->effectType == "spawn_keese" ||
|
||||
packet->effectType == "spawn_icekeese" ||
|
||||
packet->effectType == "spawn_firekeese" ||
|
||||
packet->effectType == "spawn_tektite" ||
|
||||
packet->effectType == "spawn_likelike" ||
|
||||
packet->effectType == "cucco_storm") {
|
||||
packet->effectCategory = "spawn";
|
||||
}
|
||||
else {
|
||||
packet->effectCategory = "none";
|
||||
packet->timeRemaining = 0;
|
||||
if (socketsReady == -1) {
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_CheckSockets: {}", SDLNet_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if running effect is possible
|
||||
EffectResult effectResult = ExecuteEffect(packet->effectType.c_str(), packet->effectValue, 1);
|
||||
if (effectResult == EffectResult::Retry || effectResult == EffectResult::Failure) {
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = effectResult;
|
||||
if (socketsReady == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
int len = SDLNet_TCP_Recv(tcpsock, &received, sizeof(received));
|
||||
if (!len || !tcpsock || len == -1) {
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_TCP_Recv: {}", SDLNet_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
Effect* incomingEffect = ParseMessage(received);
|
||||
if (!incomingEffect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If effect is a one off run, let's execute
|
||||
if (!incomingEffect->timeRemaining) {
|
||||
EffectResult result =
|
||||
ExecuteEffect(incomingEffect->type.c_str(), incomingEffect->value, false);
|
||||
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result);
|
||||
} else {
|
||||
bool anotherEffectOfCategoryActive = false;
|
||||
for (CCPacket* pack : receivedCommands) {
|
||||
if (pack != packet && pack->effectCategory == packet->effectCategory && pack->packetId < packet->packetId) {
|
||||
anotherEffectOfCategoryActive = true;
|
||||
|
||||
nlohmann::json dataSend;
|
||||
dataSend["id"] = packet->packetId;
|
||||
dataSend["type"] = 0;
|
||||
dataSend["timeRemaining"] = packet->timeRemaining;
|
||||
dataSend["status"] = EffectResult::Retry;
|
||||
|
||||
std::string jsonResponse = dataSend.dump();
|
||||
SDLNet_TCP_Send(tcpsock, jsonResponse.c_str(), jsonResponse.size() + 1);
|
||||
// check if a conflicting event is already active
|
||||
bool isConflictingEffectActive = false;
|
||||
for (Effect* pack : activeEffects) {
|
||||
if (pack != incomingEffect && pack->category == incomingEffect->category &&
|
||||
pack->id < incomingEffect->id) {
|
||||
isConflictingEffectActive = true;
|
||||
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining,
|
||||
EffectResult::Retry);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anotherEffectOfCategoryActive != true) {
|
||||
receivedCommandsMutex.lock();
|
||||
receivedCommands.push_back(packet);
|
||||
receivedCommandsMutex.unlock();
|
||||
std::thread t = std::thread(&CrowdControl::RunCrowdControl, this, packet);
|
||||
t.detach();
|
||||
// check if effect can be executed
|
||||
EffectResult result =
|
||||
ExecuteEffect(incomingEffect->type.c_str(), incomingEffect->value, true);
|
||||
if (result == EffectResult::Retry || result == EffectResult::Failure) {
|
||||
EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isConflictingEffectActive) {
|
||||
activeEffectsMutex.lock();
|
||||
activeEffects.push_back(incomingEffect);
|
||||
activeEffectsMutex.unlock();
|
||||
}
|
||||
}
|
||||
} catch (nlohmann::json::parse_error& e) {
|
||||
printf("Error parsing JSON: %s\n", e.what());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
SDLNet_TCP_Close(tcpsock);
|
||||
connected = false;
|
||||
SPDLOG_TRACE("[CrowdControl] Ending Listen thread...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
SDLNet_TCP_Close(tcpsock);
|
||||
SDLNet_Quit();
|
||||
connected = false;
|
||||
void CrowdControl::ProcessActiveEffects() {
|
||||
while (isEnabled) {
|
||||
// we only want to send events when status changes, on start we send Success,
|
||||
// if it fails at some point, we send Pause, and when it starts to succeed again we send Success.
|
||||
activeEffectsMutex.lock();
|
||||
auto it = activeEffects.begin();
|
||||
|
||||
while (it != activeEffects.end()) {
|
||||
Effect *effect = *it;
|
||||
EffectResult result = ExecuteEffect(effect->type.c_str(), effect->value, false);
|
||||
if (result == EffectResult::Success) {
|
||||
// If time remaining has reached 0, we have finished the effect
|
||||
if (effect->timeRemaining <= 0) {
|
||||
it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect),
|
||||
activeEffects.end());
|
||||
RemoveEffect(effect->type.c_str());
|
||||
|
||||
delete effect;
|
||||
} else {
|
||||
// If we have a success after previously being paused, fire Resume event
|
||||
if (effect->isPaused) {
|
||||
effect->isPaused = false;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Resumed);
|
||||
} else {
|
||||
effect->timeRemaining -= 1000;
|
||||
if (result != effect->lastExecutionResult) {
|
||||
effect->lastExecutionResult = result;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Success);
|
||||
}
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
} else { // Timed effects only do Success or Retry
|
||||
if (!effect->isPaused && effect->timeRemaining > 0) {
|
||||
effect->isPaused = true;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Paused);
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
activeEffectsMutex.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
SPDLOG_TRACE("[CrowdControl] Ending Process thread...");
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) {
|
||||
nlohmann::json payload;
|
||||
|
||||
payload["id"] = eventId;
|
||||
payload["type"] = 0;
|
||||
payload["timeRemaining"] = timeRemaining;
|
||||
payload["status"] = status;
|
||||
|
||||
std::string jsonPayload = payload.dump();
|
||||
SDLNet_TCP_Send(socket, jsonPayload.c_str(), jsonPayload.size() + 1);
|
||||
}
|
||||
|
||||
CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
nlohmann::json dataReceived = nlohmann::json::parse(payload, nullptr, false);
|
||||
if (dataReceived.is_discarded()) {
|
||||
SPDLOG_ERROR("Error parsing JSON");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Effect* effect = new Effect();
|
||||
effect->lastExecutionResult = EffectResult::Initiate;
|
||||
effect->id = dataReceived["id"];
|
||||
auto parameters = dataReceived["parameters"];
|
||||
if (parameters.size() > 0) {
|
||||
effect->value = dataReceived["parameters"][0];
|
||||
}
|
||||
effect->type = dataReceived["code"].get<std::string>();
|
||||
|
||||
if (effect->type == EFFECT_HIGH_GRAVITY || effect->type == EFFECT_LOW_GRAVITY) {
|
||||
effect->category = "gravity";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_DAMAGE_MULTIPLIER || effect->type == EFFECT_DEFENSE_MULTIPLIER) {
|
||||
effect->category = "defense";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_GIANT_LINK || effect->type == EFFECT_MINISH_LINK ||
|
||||
effect->type == EFFECT_INVISIBLE_LINK || effect->type == EFFECT_PAPER_LINK) {
|
||||
effect->category = "link_size";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_FREEZE || effect->type == EFFECT_DAMAGE || effect->type == EFFECT_HEAL ||
|
||||
effect->type == EFFECT_KNOCKBACK || effect->type == EFFECT_ELECTROCUTE ||
|
||||
effect->type == EFFECT_BURN || effect->type == EFFECT_KILL) {
|
||||
effect->category = "link_damage";
|
||||
} else if (effect->type == EFFECT_HOVER_BOOTS || effect->type == EFFECT_IRON_BOOTS) {
|
||||
effect->category = "boots";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_ADD_HEART_CONTAINER || effect->type == EFFECT_REMOVE_HEART_CONTAINER) {
|
||||
effect->category = "heart_container";
|
||||
} else if (effect->type == EFFECT_NO_UI) {
|
||||
effect->category = "ui";
|
||||
effect->timeRemaining = 60000;
|
||||
} else if (effect->type == EFFECT_FILL_MAGIC || effect->type == EFFECT_EMPTY_MAGIC) {
|
||||
effect->category = "magic";
|
||||
} else if (effect->type == EFFECT_OHKO) {
|
||||
effect->category = "ohko";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_PACIFIST) {
|
||||
effect->category = "pacifist";
|
||||
effect->timeRemaining = 15000;
|
||||
} else if (effect->type == EFFECT_RAINSTORM) {
|
||||
effect->category = "weather";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_REVERSE_CONTROLS) {
|
||||
effect->category = "controls";
|
||||
effect->timeRemaining = 60000;
|
||||
} else if (effect->type == EFFECT_ADD_RUPEES || effect->type == EFFECT_REMOVE_RUPEES) {
|
||||
effect->category = "rupees";
|
||||
} else if (effect->type == EFFECT_INCREASE_SPEED || effect->type == EFFECT_DECREASE_SPEED) {
|
||||
effect->category = "speed";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_NO_Z_TARGETING) {
|
||||
effect->category = "no_z";
|
||||
effect->timeRemaining = 30000;
|
||||
} else if (effect->type == EFFECT_SPAWN_WALLMASTER || effect->type == EFFECT_SPAWN_ARWING ||
|
||||
effect->type == EFFECT_SPAWN_DARK_LINK || effect->type == EFFECT_SPAWN_STALFOS ||
|
||||
effect->type == EFFECT_SPAWN_WOLFOS || effect->type == EFFECT_SPAWN_FREEZARD ||
|
||||
effect->type == EFFECT_SPAWN_KEESE || effect->type == EFFECT_SPAWN_ICE_KEESE ||
|
||||
effect->type == EFFECT_SPAWN_FIRE_KEESE || effect->type == EFFECT_SPAWN_TEKTITE ||
|
||||
effect->type == EFFECT_SPAWN_LIKE_LIKE || effect->type == EFFECT_SPAWN_CUCCO_STORM) {
|
||||
effect->category = "spawn";
|
||||
} else {
|
||||
effect->category = "none";
|
||||
effect->timeRemaining = 0;
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
CrowdControl::EffectResult CrowdControl::ExecuteEffect(std::string effectId, uint32_t value, bool dryRun) {
|
||||
@ -293,21 +332,21 @@ CrowdControl::EffectResult CrowdControl::ExecuteEffect(std::string effectId, uin
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
|
||||
if (player != NULL) {
|
||||
if (effectId == "add_heart_container") {
|
||||
if (effectId == EFFECT_ADD_HEART_CONTAINER) {
|
||||
if (gSaveContext.healthCapacity >= 0x140) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE("add_heart_container");
|
||||
if (dryRun == 0) CMD_EXECUTE(EFFECT_ADD_HEART_CONTAINER);
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "remove_heart_container") {
|
||||
} else if (effectId == EFFECT_REMOVE_HEART_CONTAINER) {
|
||||
if ((gSaveContext.healthCapacity - 0x10) <= 0) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE("remove_heart_container");
|
||||
if (dryRun == 0) CMD_EXECUTE(EFFECT_REMOVE_HEART_CONTAINER);
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "fill_magic") {
|
||||
} else if (effectId == EFFECT_FILL_MAGIC) {
|
||||
if (!gSaveContext.magicAcquired) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
@ -316,87 +355,87 @@ CrowdControl::EffectResult CrowdControl::ExecuteEffect(std::string effectId, uin
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE("fill_magic");
|
||||
if (dryRun == 0) CMD_EXECUTE(EFFECT_FILL_MAGIC);
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "empty_magic") {
|
||||
} else if (effectId == EFFECT_EMPTY_MAGIC) {
|
||||
if (!gSaveContext.magicAcquired || gSaveContext.magic <= 0) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE("empty_magic");
|
||||
if (dryRun == 0) CMD_EXECUTE(EFFECT_EMPTY_MAGIC);
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "add_rupees") {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("update_rupees {}", value));
|
||||
} else if (effectId == EFFECT_ADD_RUPEES) {
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("update_rupees {}", value));
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "remove_rupees") {
|
||||
} else if (effectId == EFFECT_REMOVE_RUPEES) {
|
||||
if (gSaveContext.rupees - value < 0) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("update_rupees -{}", value));
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("update_rupees -{}", value));
|
||||
return EffectResult::Success;
|
||||
}
|
||||
}
|
||||
|
||||
if (player != NULL && !Player_InBlockingCsMode(gPlayState, player) && gPlayState->pauseCtx.state == 0
|
||||
&& gPlayState->msgCtx.msgMode == 0) {
|
||||
if (effectId == "high_gravity") {
|
||||
if (effectId == EFFECT_HIGH_GRAVITY) {
|
||||
if (dryRun == 0) CMD_EXECUTE("gravity 2");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "low_gravity") {
|
||||
} else if (effectId == EFFECT_LOW_GRAVITY) {
|
||||
if (dryRun == 0) CMD_EXECUTE("gravity 0");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "kill"
|
||||
|| effectId == "freeze"
|
||||
|| effectId == "burn"
|
||||
|| effectId == "electrocute"
|
||||
|| effectId == "cucco_storm"
|
||||
} else if (effectId == EFFECT_KILL
|
||||
|| effectId == EFFECT_FREEZE
|
||||
|| effectId == EFFECT_BURN
|
||||
|| effectId == EFFECT_ELECTROCUTE
|
||||
|| effectId == EFFECT_SPAWN_CUCCO_STORM
|
||||
) {
|
||||
if (PlayerGrounded(player)) {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("{}", effectId));
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("{}", effectId));
|
||||
return EffectResult::Success;
|
||||
}
|
||||
return EffectResult::Failure;
|
||||
} else if (effectId == "heal"
|
||||
|| effectId == "knockback"
|
||||
} else if (effectId == EFFECT_HEAL
|
||||
|| effectId == EFFECT_KNOCKBACK
|
||||
) {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("{} {}", effectId, value));
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("{} {}", effectId, value));
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "giant_link"
|
||||
|| effectId == "minish_link"
|
||||
|| effectId == "no_ui"
|
||||
|| effectId == "invisible"
|
||||
|| effectId == "paper_link"
|
||||
|| effectId == "no_z"
|
||||
|| effectId == "ohko"
|
||||
|| effectId == "pacifist"
|
||||
|| effectId == "rainstorm"
|
||||
} else if (effectId == EFFECT_GIANT_LINK
|
||||
|| effectId == EFFECT_MINISH_LINK
|
||||
|| effectId == EFFECT_NO_UI
|
||||
|| effectId == EFFECT_INVISIBLE_LINK
|
||||
|| effectId == EFFECT_PAPER_LINK
|
||||
|| effectId == EFFECT_NO_Z_TARGETING
|
||||
|| effectId == EFFECT_OHKO
|
||||
|| effectId == EFFECT_PACIFIST
|
||||
|| effectId == EFFECT_RAINSTORM
|
||||
) {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("{} 1", effectId));
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("{} 1", effectId));
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "reverse") {
|
||||
} else if (effectId == EFFECT_REVERSE_CONTROLS) {
|
||||
if (dryRun == 0) CMD_EXECUTE("reverse_controls 1");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "iron_boots") {
|
||||
} else if (effectId == EFFECT_IRON_BOOTS) {
|
||||
if (dryRun == 0) CMD_EXECUTE("boots iron");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "hover_boots") {
|
||||
} else if (effectId == EFFECT_HOVER_BOOTS) {
|
||||
if (dryRun == 0) CMD_EXECUTE("boots hover");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "give_dekushield") {
|
||||
if (dryRun == 0) CMD_EXECUTE("givedekushield");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "spawn_wallmaster"
|
||||
|| effectId == "spawn_arwing"
|
||||
|| effectId == "spawn_darklink"
|
||||
|| effectId == "spawn_stalfos"
|
||||
|| effectId == "spawn_wolfos"
|
||||
|| effectId == "spawn_freezard"
|
||||
|| effectId == "spawn_keese"
|
||||
|| effectId == "spawn_icekeese"
|
||||
|| effectId == "spawn_firekeese"
|
||||
|| effectId == "spawn_tektite"
|
||||
|| effectId == "spawn_likelike"
|
||||
} else if (effectId == EFFECT_SPAWN_WALLMASTER
|
||||
|| effectId == EFFECT_SPAWN_ARWING
|
||||
|| effectId == EFFECT_SPAWN_DARK_LINK
|
||||
|| effectId == EFFECT_SPAWN_STALFOS
|
||||
|| effectId == EFFECT_SPAWN_WOLFOS
|
||||
|| effectId == EFFECT_SPAWN_FREEZARD
|
||||
|| effectId == EFFECT_SPAWN_KEESE
|
||||
|| effectId == EFFECT_SPAWN_ICE_KEESE
|
||||
|| effectId == EFFECT_SPAWN_FIRE_KEESE
|
||||
|| effectId == EFFECT_SPAWN_TEKTITE
|
||||
|| effectId == EFFECT_SPAWN_LIKE_LIKE
|
||||
) {
|
||||
if (dryRun == 0) {
|
||||
if (CrowdControl::SpawnEnemy(effectId)) {
|
||||
@ -406,24 +445,24 @@ CrowdControl::EffectResult CrowdControl::ExecuteEffect(std::string effectId, uin
|
||||
}
|
||||
}
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "increase_speed") {
|
||||
} else if (effectId == EFFECT_INCREASE_SPEED) {
|
||||
if (dryRun == 0) CMD_EXECUTE("speed_modifier 2");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "decrease_speed") {
|
||||
} else if (effectId == EFFECT_DECREASE_SPEED) {
|
||||
if (dryRun == 0) CMD_EXECUTE("speed_modifier -2");
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "damage_multiplier") {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("defense_modifier -{}", value));
|
||||
} else if (effectId == EFFECT_DAMAGE_MULTIPLIER) {
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("defense_modifier -{}", value));
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "defense_multiplier") {
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("defense_modifier {}", value));
|
||||
} else if (effectId == EFFECT_DEFENSE_MULTIPLIER) {
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("defense_modifier {}", value));
|
||||
return EffectResult::Success;
|
||||
} else if (effectId == "damage") {
|
||||
} else if (effectId == EFFECT_DAMAGE) {
|
||||
if ((gSaveContext.healthCapacity - 0x10) <= 0) {
|
||||
return EffectResult::Failure;
|
||||
}
|
||||
|
||||
if (dryRun == 0) CMD_EXECUTE(std::format("{} {}", effectId, value));
|
||||
if (dryRun == 0) CMD_EXECUTE(fmt::format("{} {}", effectId, value));
|
||||
return EffectResult::Success;
|
||||
}
|
||||
}
|
||||
@ -440,49 +479,49 @@ bool CrowdControl::SpawnEnemy(std::string effectId) {
|
||||
float posYOffset = 0;
|
||||
float posZOffset = 0;
|
||||
|
||||
if (effectId == "spawn_wallmaster") {
|
||||
if (effectId == EFFECT_SPAWN_WALLMASTER) {
|
||||
enemyId = 17;
|
||||
} else if (effectId == "spawn_arwing") {
|
||||
} else if (effectId == EFFECT_SPAWN_ARWING) {
|
||||
enemyId = 315;
|
||||
enemyParams = 1;
|
||||
posYOffset = 100;
|
||||
} else if (effectId == "spawn_darklink") {
|
||||
} else if (effectId == EFFECT_SPAWN_DARK_LINK) {
|
||||
enemyId = 51;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_stalfos") {
|
||||
} else if (effectId == EFFECT_SPAWN_STALFOS) {
|
||||
enemyId = 2;
|
||||
enemyParams = 2;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_wolfos") {
|
||||
} else if (effectId == EFFECT_SPAWN_WOLFOS) {
|
||||
enemyId = 431;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_freezard") {
|
||||
} else if (effectId == EFFECT_SPAWN_FREEZARD) {
|
||||
enemyId = 289;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_keese") {
|
||||
} else if (effectId == EFFECT_SPAWN_KEESE) {
|
||||
enemyId = 19;
|
||||
enemyParams = 2;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_icekeese") {
|
||||
} else if (effectId == EFFECT_SPAWN_ICE_KEESE) {
|
||||
enemyId = 19;
|
||||
enemyParams = 4;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_firekeese") {
|
||||
} else if (effectId == EFFECT_SPAWN_FIRE_KEESE) {
|
||||
enemyId = 19;
|
||||
enemyParams = 1;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_tektite") {
|
||||
} else if (effectId == EFFECT_SPAWN_TEKTITE) {
|
||||
enemyId = 27;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
} else if (effectId == "spawn_likelike") {
|
||||
} else if (effectId == EFFECT_SPAWN_LIKE_LIKE) {
|
||||
enemyId = 221;
|
||||
posXOffset = 75;
|
||||
posYOffset = 50;
|
||||
@ -501,34 +540,34 @@ void CrowdControl::RemoveEffect(std::string effectId) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
|
||||
if (player != NULL) {
|
||||
if (effectId == "giant_link"
|
||||
|| effectId == "minish_link"
|
||||
|| effectId == "no_ui"
|
||||
|| effectId == "invisible"
|
||||
|| effectId == "paper_link"
|
||||
|| effectId == "no_z"
|
||||
|| effectId == "ohko"
|
||||
|| effectId == "pacifist"
|
||||
|| effectId == "rainstorm"
|
||||
if (effectId == EFFECT_GIANT_LINK
|
||||
|| effectId == EFFECT_MINISH_LINK
|
||||
|| effectId == EFFECT_NO_UI
|
||||
|| effectId == EFFECT_INVISIBLE_LINK
|
||||
|| effectId == EFFECT_PAPER_LINK
|
||||
|| effectId == EFFECT_NO_Z_TARGETING
|
||||
|| effectId == EFFECT_OHKO
|
||||
|| effectId == EFFECT_PACIFIST
|
||||
|| effectId == EFFECT_RAINSTORM
|
||||
) {
|
||||
CMD_EXECUTE(std::format("{} 0", effectId));
|
||||
CMD_EXECUTE(fmt::format("{} 0", effectId));
|
||||
return;
|
||||
} else if (effectId == "iron_boots" || effectId == "hover_boots") {
|
||||
} else if (effectId == EFFECT_IRON_BOOTS || effectId == EFFECT_HOVER_BOOTS) {
|
||||
CMD_EXECUTE("boots kokiri");
|
||||
return;
|
||||
} else if (effectId == "high_gravity" || effectId == "low_gravity") {
|
||||
} else if (effectId == EFFECT_HIGH_GRAVITY || effectId == EFFECT_LOW_GRAVITY) {
|
||||
CMD_EXECUTE("gravity 1");
|
||||
return;
|
||||
} else if (effectId == "reverse") {
|
||||
} else if (effectId == EFFECT_REVERSE_CONTROLS) {
|
||||
CMD_EXECUTE("reverse_controls 0");
|
||||
return;
|
||||
} else if (effectId == "increase_speed"
|
||||
|| effectId == "decrease_speed"
|
||||
} else if (effectId == EFFECT_INCREASE_SPEED
|
||||
|| effectId == EFFECT_DECREASE_SPEED
|
||||
) {
|
||||
CMD_EXECUTE("speed_modifier 0");
|
||||
return;
|
||||
} else if (effectId == "damage_multiplier"
|
||||
|| effectId == "defense_multiplier"
|
||||
} else if (effectId == EFFECT_DAMAGE_MULTIPLIER
|
||||
|| effectId == EFFECT_DEFENSE_MULTIPLIER
|
||||
) {
|
||||
CMD_EXECUTE("defense_modifier 0");
|
||||
return;
|
||||
|
@ -19,14 +19,6 @@
|
||||
|
||||
class CrowdControl {
|
||||
private:
|
||||
typedef struct CCPacket {
|
||||
uint32_t packetId;
|
||||
std::string effectType;
|
||||
uint32_t effectValue;
|
||||
std::string effectCategory;
|
||||
long timeRemaining;
|
||||
} CCPacket;
|
||||
|
||||
enum EffectResult {
|
||||
/// <summary>The effect executed successfully.</summary>
|
||||
Success = 0x00,
|
||||
@ -46,6 +38,8 @@ class CrowdControl {
|
||||
Resumed = 0x07,
|
||||
/// <summary>The timed effect has finished.</summary>
|
||||
Finished = 0x08,
|
||||
/// <summary>Effect is being initiated. SoH exclusive to check against if an effect state has changed or not.</summary>
|
||||
Initiate = 0xFE,
|
||||
/// <summary>The processor isn't ready to start or has shut down.</summary>
|
||||
NotReady = 0xFF
|
||||
};
|
||||
@ -65,27 +59,48 @@ class CrowdControl {
|
||||
ResponseType type = ResponseType::EffectRequest;
|
||||
};
|
||||
|
||||
typedef struct Effect {
|
||||
uint32_t id;
|
||||
std::string type;
|
||||
uint32_t value;
|
||||
std::string category;
|
||||
long timeRemaining;
|
||||
|
||||
// Metadata used while executing (only for timed effects)
|
||||
bool isPaused;
|
||||
EffectResult lastExecutionResult;
|
||||
} Effect;
|
||||
|
||||
std::thread ccThreadReceive;
|
||||
std::thread ccThreadProcess;
|
||||
|
||||
TCPsocket tcpsock;
|
||||
IPaddress ip;
|
||||
|
||||
bool isEnabled;
|
||||
bool connected;
|
||||
|
||||
char received[512];
|
||||
|
||||
std::vector<CCPacket*> receivedCommands;
|
||||
std::mutex receivedCommandsMutex;
|
||||
std::vector<Effect*> activeEffects;
|
||||
std::mutex activeEffectsMutex;
|
||||
|
||||
void RunCrowdControl(CCPacket* packet);
|
||||
void ReceiveFromCrowdControl();
|
||||
void ListenToServer();
|
||||
void ProcessActiveEffects();
|
||||
|
||||
void EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining,
|
||||
CrowdControl::EffectResult status);
|
||||
Effect* ParseMessage(char payload[512]);
|
||||
EffectResult ExecuteEffect(std::string effectId, uint32_t value, bool dryRun);
|
||||
bool SpawnEnemy(std::string effectId);
|
||||
void RemoveEffect(std::string effectId);
|
||||
bool SpawnEnemy(std::string effectId);
|
||||
|
||||
public:
|
||||
static CrowdControl* Instance;
|
||||
void InitCrowdControl();
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void Enable();
|
||||
void Disable();
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
@ -28,6 +28,10 @@
|
||||
#include "soh/SaveManager.h"
|
||||
#include "OTRGlobals.h"
|
||||
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#include "Enhancements/crowd-control/CrowdControl.h"
|
||||
#endif
|
||||
|
||||
#define EXPERIMENTAL() \
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 50, 50, 255)); \
|
||||
UIWidgets::Spacer(3.0f); \
|
||||
@ -1545,9 +1549,17 @@ namespace GameMenuBar {
|
||||
}
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(1);
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
UIWidgets::PaddedEnhancementCheckbox("Crowd Control", "gCrowdControl", true, false);
|
||||
UIWidgets::Tooltip("Requires a full SoH restart to take effect!\n\nEnables CrowdControl. Will attempt to connect to the local Crowd Control server.");
|
||||
|
||||
if (CVar_GetS32("gCrowdControl", 0)) {
|
||||
CrowdControl::Instance->Enable();
|
||||
} else {
|
||||
CrowdControl::Instance->Disable();
|
||||
}
|
||||
#endif
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
|
||||
if (ImGui::BeginMenu("Rando Enhancements"))
|
||||
|
@ -451,12 +451,15 @@ extern "C" void InitOTR() {
|
||||
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
CrowdControl::Instance = new CrowdControl();
|
||||
CrowdControl::Instance->InitCrowdControl();
|
||||
CrowdControl::Instance->Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" void DeinitOTR() {
|
||||
OTRAudio_Exit();
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
CrowdControl::Instance->Shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
Loading…
Reference in New Issue
Block a user