Merge pull request #70 from jeromkiller/AddArchipelagoClientLib
Add archipelago client lib
This commit is contained in:
@@ -12,7 +12,7 @@ You can currently connect to the multiworld server, scout the items.
|
||||
If you have no randomizer genereted (the `Randomizer` folder is empty) `Connect` to the multiworld from the main screen, click the `Scout` button to load all of the item locations and finally press the `Link up` button, you'll be able to start a randomizer save file.
|
||||
with the items populated with the location from the server.
|
||||
|
||||
Sending and Recieving should be implemented, Saving the game doesn't save the current item index localy (the library is a bit anoying with that).
|
||||
Sending and Receiving should be implemented, Saving the game doesn't save the current item index localy (the library is a bit anoying with that).
|
||||
|
||||
Not all checks have been mapped, and some may be mapped incorrectly.
|
||||
The victory condition should be implemented but has largely gone untested.
|
||||
|
||||
@@ -71,5 +71,5 @@ DEFINE_HOOK(OnAssetAltChange, ());
|
||||
DEFINE_HOOK(OnKaleidoUpdate, ());
|
||||
|
||||
DEFINE_HOOK(OnRandomizerItemGivenHooks, (uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped));
|
||||
DEFINE_HOOK(OnArchipelagoItemRecieved, (uint32_t rg));
|
||||
DEFINE_HOOK(OnArchipelagoItemReceived, (uint32_t rg));
|
||||
DEFINE_HOOK(OnRandomizerExternalCheck, (uint32_t rc));
|
||||
|
||||
@@ -309,8 +309,8 @@ void GameInteractor_ExecuteOnRandomizerItemGivenHooks(uint32_t rc, GetItemEntry
|
||||
}
|
||||
|
||||
// MARK: Archipelago
|
||||
void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rg) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnArchipelagoItemRecieved>(rg);
|
||||
void GameInteractor_ExecuteOnArchipelagoItemReceived(uint32_t rg) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnArchipelagoItemReceived>(rg);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnRandomizerExternalCheck(uint32_t rc) {
|
||||
|
||||
@@ -85,7 +85,7 @@ void GameInteractor_ExecuteOnKaleidoUpdate();
|
||||
void GameInteractor_ExecuteOnRandomizerItemGivenHooks(uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped);
|
||||
|
||||
// Mark: - Archipelago
|
||||
void GameInteractor_ExecuteOnArchipelagoItemRecieved(uint32_t rg);
|
||||
void GameInteractor_ExecuteOnArchipelagoItemReceived(uint32_t rg);
|
||||
void GameInteractor_ExecuteOnRandomizerExternalCheck(uint32_t rc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -346,27 +346,27 @@ void Context::SetSpoilerLoaded(const bool spoilerLoaded) {
|
||||
mSpoilerLoaded = spoilerLoaded;
|
||||
}
|
||||
|
||||
void Context::AddRecievedArchipelagoItem(const RandomizerGet item) {
|
||||
mAPrecieveQueue.emplace(item);
|
||||
void Context::AddReceivedArchipelagoItem(const RandomizerGet item) {
|
||||
mAPreceiveQueue.emplace(item);
|
||||
std::string logMessage = "[LOG] Item Pushed: " + item;
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
}
|
||||
|
||||
GetItemEntry Context::GetArchipelagoGIEntry() {
|
||||
ArchipelagoConsole_SendMessage("[LOG] Trying to get Item Entry", true);
|
||||
if(mAPrecieveQueue.empty()) {
|
||||
if(mAPreceiveQueue.empty()) {
|
||||
// something must have gone wrong here, just give a rupee
|
||||
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GI_HEART);
|
||||
}
|
||||
|
||||
// get the first item from the archipelago queue
|
||||
RandomizerGet item_id = mAPrecieveQueue.front();
|
||||
RandomizerGet item_id = mAPreceiveQueue.front();
|
||||
assert(item_id != RG_NONE);
|
||||
|
||||
Item& item = StaticData::RetrieveItem(item_id);
|
||||
SPDLOG_TRACE("Found item! {}, {}", item.GetName().GetEnglish(), (int)item_id);
|
||||
GetItemEntry item_entry = item.GetGIEntry_Copy();
|
||||
mAPrecieveQueue.pop();
|
||||
mAPreceiveQueue.pop();
|
||||
return item_entry; // todo: add custom text maybe?
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class Context {
|
||||
*/
|
||||
RandoOptionLACSCondition LACSCondition() const;
|
||||
GetItemEntry GetFinalGIEntry(RandomizerCheck rc, bool checkObtainability = true, GetItemID ogItemId = GI_NONE);
|
||||
void AddRecievedArchipelagoItem(const RandomizerGet item);
|
||||
void AddReceivedArchipelagoItem(const RandomizerGet item);
|
||||
GetItemEntry GetArchipelagoGIEntry();
|
||||
void ParseSpoiler(const char* spoilerFileName);
|
||||
void ParseHashIconIndexesJson(nlohmann::json spoilerFileJson);
|
||||
@@ -189,6 +189,6 @@ class Context {
|
||||
std::string mHash;
|
||||
std::string mSeedString;
|
||||
uint32_t mFinalSeed = 0;
|
||||
std::queue<RandomizerGet> mAPrecieveQueue = {};
|
||||
std::queue<RandomizerGet> mAPreceiveQueue = {};
|
||||
};
|
||||
} // namespace Rando
|
||||
@@ -222,11 +222,11 @@ static std::queue<RandomizerCheck> randomizerQueuedChecks;
|
||||
static RandomizerCheck randomizerQueuedCheck = RC_UNKNOWN_CHECK;
|
||||
static GetItemEntry randomizerQueuedItemEntry = GET_ITEM_NONE;
|
||||
|
||||
void ArchipelagoOnRecieveItem(const int32_t item) {
|
||||
void ArchipelagoOnReceiveItem(const int32_t item) {
|
||||
std::string logMessage = "[LOG] Receive item handler called: " + item;
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
randomizerQueuedChecks.push(RC_ARCHIPELAGO_RECIEVED_ITEM);
|
||||
Rando::Context::GetInstance()->AddRecievedArchipelagoItem(static_cast<RandomizerGet>(item));
|
||||
randomizerQueuedChecks.push(RC_ARCHIPELAGO_RECEIVED_ITEM);
|
||||
Rando::Context::GetInstance()->AddReceivedArchipelagoItem(static_cast<RandomizerGet>(item));
|
||||
}
|
||||
|
||||
void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) {
|
||||
@@ -313,7 +313,7 @@ void RandomizerOnPlayerUpdateForRCQueueHandler() {
|
||||
auto loc = Rando::Context::GetInstance()->GetItemLocation(rc);
|
||||
uint8_t isGiSkipped = 0;
|
||||
|
||||
if (rc == RC_ARCHIPELAGO_RECIEVED_ITEM) {
|
||||
if (rc == RC_ARCHIPELAGO_RECEIVED_ITEM) {
|
||||
getItemEntry = Rando::Context::GetInstance()->GetArchipelagoGIEntry();
|
||||
} else {
|
||||
RandomizerGet vanillaRandomizerGet = Rando::StaticData::GetLocation(rc)->GetVanillaItem();
|
||||
@@ -2540,7 +2540,7 @@ void RandomizerRegisterHooks() {
|
||||
onCuccoOrChickenHatchHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnCuccoOrChickenHatch>(
|
||||
RandomizerOnCuccoOrChickenHatch);
|
||||
|
||||
COND_HOOK(GameInteractor::OnArchipelagoItemRecieved, IS_ARCHIPELAGO, ArchipelagoOnRecieveItem);
|
||||
COND_HOOK(GameInteractor::OnArchipelagoItemReceived, IS_ARCHIPELAGO, ArchipelagoOnReceiveItem);
|
||||
COND_HOOK(GameInteractor::OnRandomizerExternalCheck, IS_ARCHIPELAGO, RandomizerOnExternalCheckHandler)
|
||||
|
||||
if (RAND_GET_OPTION(RSK_FISHSANITY) != RO_FISHSANITY_OFF) {
|
||||
|
||||
@@ -133,7 +133,7 @@ bool ItemLocation::HasObtained() const {
|
||||
}
|
||||
|
||||
void ItemLocation::SetCheckStatus(RandomizerCheckStatus status_) {
|
||||
if(rc == RC_ARCHIPELAGO_RECIEVED_ITEM) // never count the AP recieve trigger as 'collected'
|
||||
if(rc == RC_ARCHIPELAGO_RECEIVED_ITEM) // never count the AP receive trigger as 'collected'
|
||||
return;
|
||||
status = status_;
|
||||
}
|
||||
|
||||
@@ -3472,7 +3472,7 @@ typedef enum {
|
||||
RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8,
|
||||
// End Grass
|
||||
|
||||
RC_ARCHIPELAGO_RECIEVED_ITEM,
|
||||
RC_ARCHIPELAGO_RECEIVED_ITEM,
|
||||
RC_MAX
|
||||
} RandomizerCheck;
|
||||
|
||||
|
||||
@@ -29,10 +29,7 @@ ArchipelagoClient::ArchipelagoClient() {
|
||||
|
||||
gameWon = false;
|
||||
itemQueued = false;
|
||||
|
||||
// call poll every frame
|
||||
COND_HOOK(GameInteractor::OnGameFrameUpdate, true, [](){ArchipelagoClient::GetInstance().Poll();});
|
||||
COND_HOOK(GameInteractor::OnLoadGame, true, [](int32_t file_id){ArchipelagoClient::GetInstance().GameLoaded();});
|
||||
disconnecting = false;
|
||||
}
|
||||
|
||||
ArchipelagoClient& ArchipelagoClient::GetInstance() {
|
||||
@@ -45,6 +42,7 @@ bool ArchipelagoClient::StartClient() {
|
||||
apClient.reset();
|
||||
}
|
||||
|
||||
disconnecting = false;
|
||||
apClient = std::unique_ptr<APClient>(
|
||||
new APClient(uuid, AP_Client_consts::AP_GAME_NAME,
|
||||
CVarGetString(CVAR_REMOTE_ARCHIPELAGO("ServerAddress"), "localhost:38281"), "cacert.pem"));
|
||||
@@ -66,12 +64,22 @@ bool ArchipelagoClient::StartClient() {
|
||||
// if we are already in game when we connect
|
||||
// we won't have to request an itemSynch
|
||||
if(GameInteractor::IsSaveLoaded(true)) {
|
||||
if(!isRightSaveLoaded()) {
|
||||
disconnecting = true;
|
||||
ArchipelagoConsole_SendMessage("[ERROR] Connected to incorrect slot, disconnecting...");
|
||||
return;
|
||||
}
|
||||
|
||||
SynchSentLocations();
|
||||
SynchRecievedLocations();
|
||||
SynchReceivedLocations();
|
||||
}
|
||||
});
|
||||
|
||||
apClient->set_items_received_handler([&](const std::list<APClient::NetworkItem>& items) {
|
||||
if(disconnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const APClient::NetworkItem& item : items) {
|
||||
ApItem apItem;
|
||||
const std::string game = apClient->get_player_game(item.player);
|
||||
@@ -85,6 +93,10 @@ bool ArchipelagoClient::StartClient() {
|
||||
});
|
||||
|
||||
apClient->set_location_info_handler([&](const std::list<APClient::NetworkItem>& items) {
|
||||
if(disconnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
scoutedItems.clear();
|
||||
|
||||
for(const APClient::NetworkItem& item: items) {
|
||||
@@ -108,25 +120,71 @@ bool ArchipelagoClient::StartClient() {
|
||||
}); // todo maybe move these functions to a lambda, since they don't have to be static anymore
|
||||
|
||||
apClient->set_location_checked_handler([&](const std::list<int64_t> locations) {
|
||||
if(disconnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(const int64_t apLoc : locations) {
|
||||
QueueExternalCheck(apLoc);
|
||||
}
|
||||
});
|
||||
|
||||
apClient->set_print_json_handler([&](const APClient::PrintJSONArgs& arg) {
|
||||
std::string tag = "[" + arg.type + "] ";
|
||||
|
||||
const int slot = apClient->get_player_number();
|
||||
if(arg.type == "ItemSend") {
|
||||
if((*arg.item).player == slot) {
|
||||
tag = "[Found] ";
|
||||
} else if (*arg.receiving == slot ) {
|
||||
tag = "[Received] ";
|
||||
}
|
||||
if(disconnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string text = tag + apClient->render_json(arg.data, APClient::RenderFormat::TEXT);
|
||||
ArchipelagoConsole_SendMessage(text.c_str(), false);
|
||||
std::vector<ColoredTextNode> coloredNodes;
|
||||
|
||||
for(const APClient::TextNode& node : arg.data) {
|
||||
APClient* client = apClient.get();
|
||||
std::string color;
|
||||
std::string text;
|
||||
|
||||
if(node.type == "player_id") {
|
||||
int id = std::stoi(node.text);
|
||||
if (color.empty() && id == client->get_player_number()) color = "magenta";
|
||||
else if(color.empty()) color = "yellow";
|
||||
text = client->get_player_alias(id);
|
||||
} else if (node.type == "item_id") {
|
||||
int64_t id = std::stoll(node.text);
|
||||
if(color.empty()) {
|
||||
if (node.flags & APClient::ItemFlags::FLAG_ADVANCEMENT) color = "plum";
|
||||
else if (node.flags & APClient::ItemFlags::FLAG_NEVER_EXCLUDE) color = "slateblue";
|
||||
else if (node.flags & APClient::ItemFlags::FLAG_TRAP) color = "salmon";
|
||||
else color = "cyan";
|
||||
}
|
||||
text = client->get_item_name(id, client->get_player_game(node.player));
|
||||
} else if (node.type == "location_id") {
|
||||
int64_t id = std::stoll(node.text);
|
||||
if (color.empty()) color = "blue";
|
||||
text = client->get_location_name(id, client->get_player_game(node.player));
|
||||
} else if (node.type == "hint_status") {
|
||||
text = node.text;
|
||||
if (node.hintStatus == APClient::HINT_FOUND) color = "green";
|
||||
else if (node.hintStatus == APClient::HINT_UNSPECIFIED) color = "grey";
|
||||
else if (node.hintStatus == APClient::HINT_NO_PRIORITY) color = "slateblue";
|
||||
else if (node.hintStatus == APClient::HINT_AVOID) color = "salmon";
|
||||
else if (node.hintStatus == APClient::HINT_PRIORITY) color = "plum";
|
||||
else color = "red"; // unknown status -> red
|
||||
} else if (node.type == "ERROR") {
|
||||
color = "ERROR";
|
||||
text = node.text;
|
||||
} else if (node.type == "LOG") {
|
||||
color = "LOG";
|
||||
text = node.text;
|
||||
} else {
|
||||
color = "white";
|
||||
text = node.text;
|
||||
}
|
||||
|
||||
ColoredTextNode Colornode;
|
||||
Colornode.color = color;
|
||||
Colornode.text = text;
|
||||
coloredNodes.push_back(Colornode);
|
||||
}
|
||||
|
||||
ArchipelagoConsole_PrintJson(coloredNodes);
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -139,7 +197,14 @@ void ArchipelagoClient::GameLoaded() {
|
||||
|
||||
// if its not an AP save, disconnect
|
||||
if(!IS_ARCHIPELAGO) {
|
||||
apClient->reset();
|
||||
ArchipelagoConsole_SendMessage("[ERROR] Loaded save is not not an archipelago save, disconnecting...");
|
||||
disconnecting = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isRightSaveLoaded()) {
|
||||
ArchipelagoConsole_SendMessage("[ERROR] Loaded save is not associated with connected slot, disconnecting...");
|
||||
disconnecting = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,7 +212,7 @@ void ArchipelagoClient::GameLoaded() {
|
||||
|
||||
SynchItems();
|
||||
SynchSentLocations();
|
||||
SynchRecievedLocations();
|
||||
SynchReceivedLocations();
|
||||
}
|
||||
|
||||
void ArchipelagoClient::StartLocationScouts() {
|
||||
@@ -185,7 +250,7 @@ void ArchipelagoClient::SynchSentLocations() {
|
||||
apClient->LocationChecks(checkedLocations);
|
||||
}
|
||||
|
||||
void ArchipelagoClient::SynchRecievedLocations() {
|
||||
void ArchipelagoClient::SynchReceivedLocations() {
|
||||
// Open checks that have been found previously but went unsaved
|
||||
for(const int64_t apLoc : apClient->get_checked_locations()) {
|
||||
QueueExternalCheck(apLoc);
|
||||
@@ -196,12 +261,17 @@ void ArchipelagoClient::QueueExternalCheck(const int64_t apLocation) {
|
||||
const std::string checkName = apClient->get_location_name(apLocation, AP_Client_consts::AP_GAME_NAME);
|
||||
const uint32_t RC = static_cast<uint32_t>(Rando::StaticData::locationNameToEnum[checkName]);
|
||||
|
||||
if(RC == RC_UNKNOWN_CHECK) {
|
||||
ArchipelagoConsole_SendMessage("[ERROR] Attempting to queue RC_UNKOWN_CHECK, skipping", false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't queue checks we already have
|
||||
if(Rando::Context::GetInstance()->GetItemLocation(RC)->HasObtained()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string locationLog = "[LOG] Externaly checking" + checkName;
|
||||
std::string locationLog = "[LOG] Externaly checking: " + checkName;
|
||||
ArchipelagoConsole_SendMessage(locationLog.c_str(), true);
|
||||
|
||||
GameInteractor_ExecuteOnRandomizerExternalCheck(RC);
|
||||
@@ -212,12 +282,17 @@ bool ArchipelagoClient::IsConnected() {
|
||||
}
|
||||
|
||||
void ArchipelagoClient::CheckLocation(RandomizerCheck sohCheckId) {
|
||||
if(sohCheckId == RC_UNKNOWN_CHECK) {
|
||||
ArchipelagoConsole_SendMessage("[ERROR] trying to send RC_UNKNOWN_CHECK, skipping", false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string apName = Rando::StaticData::GetLocation(sohCheckId)->GetName();
|
||||
if (apName.empty()) {
|
||||
return;
|
||||
}
|
||||
int64_t apItemId = apClient->get_location_id(std::string(apName));
|
||||
|
||||
int64_t apItemId = apClient->get_location_id(std::string(apName));
|
||||
std::string logMessage = "[LOG] Checked: " + apName + "(" + std::to_string(apItemId) + "), sending to AP server";
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
|
||||
@@ -234,21 +309,21 @@ void ArchipelagoClient::OnItemReceived(const ApItem apItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string logMessage = "[LOG] Recieved " + apItem.itemName;
|
||||
std::string logMessage = "[LOG] Received " + apItem.itemName;
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
|
||||
// add item to the queue
|
||||
recieveQueue.push(apItem);
|
||||
}
|
||||
|
||||
void ArchipelagoClient::QueueItem(const ApItem item) {
|
||||
if(item.index < gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex) {
|
||||
if(apItem.index < gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex) {
|
||||
// Skip queueing any items we already have
|
||||
std::string logMessage = "[LOG] Skipping giving " + item.itemName + ". We recieved this previously.";
|
||||
std::string logMessage = "[LOG] Skipping giving " + apItem.itemName + ". We received this previously.";
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// add item to the queue
|
||||
receiveQueue.push(apItem);
|
||||
}
|
||||
|
||||
void ArchipelagoClient::QueueItem(const ApItem item) {
|
||||
std::string logMessage = "[LOG] Giving " + item.itemName;
|
||||
ArchipelagoConsole_SendMessage(logMessage.c_str(), true);
|
||||
const RandomizerGet RG = Rando::StaticData::itemNameToEnum[item.itemName];
|
||||
@@ -257,7 +332,7 @@ void ArchipelagoClient::QueueItem(const ApItem item) {
|
||||
}
|
||||
|
||||
itemQueued = true;
|
||||
GameInteractor_ExecuteOnArchipelagoItemRecieved(static_cast<int32_t>(RG));
|
||||
GameInteractor_ExecuteOnArchipelagoItemReceived(static_cast<int32_t>(RG));
|
||||
}
|
||||
|
||||
void ArchipelagoClient::SendGameWon() {
|
||||
@@ -267,22 +342,49 @@ void ArchipelagoClient::SendGameWon() {
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoClient::SendMessage(const std::string message) {
|
||||
// local commands not implemented yet
|
||||
if(message.starts_with("/")) {
|
||||
ArchipelagoConsole_SendMessage("Ship of Harkinian does not have any local commands yet.\nUse !help\" to see server commands instead", false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(apClient == nullptr) {
|
||||
ArchipelagoConsole_SendMessage("[ERROR] Could not send message. Please Connect to your slot.", false);
|
||||
return;
|
||||
}
|
||||
|
||||
apClient->Say(message);
|
||||
}
|
||||
|
||||
void ArchipelagoClient::Poll() {
|
||||
if(apClient == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// queue another item to be recieved
|
||||
if(!itemQueued && recieveQueue.size() > 0) {
|
||||
if(disconnecting) {
|
||||
apClient->reset();
|
||||
apClient = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// queue another item to be received
|
||||
if(!itemQueued && receiveQueue.size() > 0) {
|
||||
|
||||
const ApItem item = recieveQueue.front();
|
||||
recieveQueue.pop();
|
||||
const ApItem item = receiveQueue.front();
|
||||
receiveQueue.pop();
|
||||
QueueItem(item);
|
||||
}
|
||||
|
||||
apClient->poll();
|
||||
}
|
||||
|
||||
bool ArchipelagoClient::isRightSaveLoaded() const {
|
||||
const bool seedMatch = apClient->get_seed().compare(gSaveContext.ship.quest.data.archipelago.roomHash) == 0;
|
||||
const bool slotMatch = GetSlotName().compare(gSaveContext.ship.quest.data.archipelago.slotName) == 0;
|
||||
return seedMatch && slotMatch;
|
||||
}
|
||||
|
||||
const std::string ArchipelagoClient::GetSlotName() const {
|
||||
if(apClient == NULL) {
|
||||
return "";
|
||||
@@ -301,7 +403,7 @@ const std::vector<ArchipelagoClient::ApItem>& ArchipelagoClient::GetScoutedItems
|
||||
|
||||
const char* ArchipelagoClient::GetConnectionStatus() {
|
||||
if (!apClient) {
|
||||
return "";
|
||||
return "Disconnected!";
|
||||
}
|
||||
|
||||
APClient::State clientStatus = apClient->get_state();
|
||||
@@ -317,7 +419,7 @@ const char* ArchipelagoClient::GetConnectionStatus() {
|
||||
return "Socket Connected!";
|
||||
}
|
||||
case APClient::State::ROOM_INFO: {
|
||||
return "Room info Recieved!";
|
||||
return "Room info Received!";
|
||||
}
|
||||
case APClient::State::SLOT_CONNECTED: {
|
||||
return "Slot Connected!";
|
||||
@@ -335,6 +437,12 @@ extern "C" void Archipelago_InitSaveFile() {
|
||||
|
||||
std::vector<ArchipelagoClient::ApItem> scoutedItems = ArchipelagoClient::GetInstance().GetScoutedItems();
|
||||
|
||||
ArchipelagoClient& client = ArchipelagoClient::GetInstance();
|
||||
SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.roomHash, client.apClient->get_seed(),
|
||||
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.roomHash));
|
||||
SohUtils::CopyStringToCharArray(gSaveContext.ship.quest.data.archipelago.slotName, client.apClient->get_slot(),
|
||||
ARRAY_COUNT(gSaveContext.ship.quest.data.archipelago.slotName));
|
||||
|
||||
for (uint32_t i = 0; i < scoutedItems.size(); i++) {
|
||||
RandomizerCheck rc = Rando::StaticData::locationNameToEnum[scoutedItems[i].locationName];
|
||||
|
||||
@@ -411,11 +519,16 @@ void InitArchipelagoData(bool isDebug) {
|
||||
}
|
||||
|
||||
void RegisterArchipelago() {
|
||||
// make sure the client is constructed
|
||||
ArchipelagoClient::GetInstance();
|
||||
|
||||
CVarSetInteger(CVAR_REMOTE_ARCHIPELAGO("Connected"), 0);
|
||||
|
||||
COND_HOOK(GameInteractor::OnGameFrameUpdate, true, [](){ArchipelagoClient::GetInstance().Poll();});
|
||||
COND_HOOK(GameInteractor::OnLoadGame, true, [](int32_t file_id){ArchipelagoClient::GetInstance().GameLoaded();});
|
||||
COND_HOOK(GameInteractor::OnRandomizerItemGivenHooks, IS_ARCHIPELAGO,
|
||||
[](uint32_t rc, GetItemEntry gi, uint8_t isGiSkipped) {
|
||||
if (rc == RC_ARCHIPELAGO_RECIEVED_ITEM) {
|
||||
if (rc == RC_ARCHIPELAGO_RECEIVED_ITEM) {
|
||||
gSaveContext.ship.quest.data.archipelago.lastReceivedItemIndex++;
|
||||
ArchipelagoClient::GetInstance().itemQueued = false;
|
||||
} else {
|
||||
|
||||
@@ -27,6 +27,11 @@ class ArchipelagoClient{
|
||||
uint64_t index;
|
||||
};
|
||||
|
||||
struct ColoredTextNode {
|
||||
std::string text;
|
||||
std::string color;
|
||||
};
|
||||
|
||||
static ArchipelagoClient& GetInstance();
|
||||
|
||||
bool StartClient();
|
||||
@@ -36,7 +41,7 @@ class ArchipelagoClient{
|
||||
void StartLocationScouts();
|
||||
void SynchItems();
|
||||
void SynchSentLocations();
|
||||
void SynchRecievedLocations();
|
||||
void SynchReceivedLocations();
|
||||
|
||||
// getters
|
||||
const std::string GetSlotName() const;
|
||||
@@ -53,11 +58,12 @@ class ArchipelagoClient{
|
||||
void QueueExternalCheck(int64_t apLocation);
|
||||
|
||||
void SendGameWon();
|
||||
|
||||
void SendMessage(const std::string message);
|
||||
void Poll();
|
||||
|
||||
std::unique_ptr<APClient> apClient;
|
||||
bool itemQueued;
|
||||
bool disconnecting;
|
||||
|
||||
protected:
|
||||
ArchipelagoClient();
|
||||
@@ -65,9 +71,12 @@ class ArchipelagoClient{
|
||||
private:
|
||||
ArchipelagoClient(ArchipelagoClient &) = delete;
|
||||
void operator=(const ArchipelagoClient &) = delete;
|
||||
|
||||
bool isRightSaveLoaded() const;
|
||||
|
||||
std::string uuid;
|
||||
|
||||
static std::shared_ptr<ArchipelagoClient> instance; // is this even used?
|
||||
static std::shared_ptr<ArchipelagoClient> instance;
|
||||
static bool initialized;
|
||||
|
||||
bool gameWon;
|
||||
@@ -75,7 +84,7 @@ class ArchipelagoClient{
|
||||
nlohmann::json slotData;
|
||||
std::set<int64_t> locations;
|
||||
std::vector<ApItem> scoutedItems;
|
||||
std::queue<ApItem> recieveQueue;
|
||||
std::queue<ApItem> receiveQueue;
|
||||
};
|
||||
|
||||
void LoadArchipelagoData();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "soh/SohGui/SohGui.hpp"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
ImVector<char*> Items;
|
||||
std::vector<std::vector<ArchipelagoClient::ColoredTextNode>> Items;
|
||||
bool autoScroll = true;
|
||||
|
||||
using namespace UIWidgets;
|
||||
@@ -19,7 +19,21 @@ void ArchipelagoConsole_SendMessage(const char* fmt, bool debugMessage, ...) {
|
||||
vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
|
||||
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
||||
va_end(args);
|
||||
Items.push_back(strdup(buf));
|
||||
ArchipelagoClient::ColoredTextNode node;
|
||||
node.text = std::string(buf);
|
||||
node.color = "white";
|
||||
if (strstr(buf, "[ERROR]")) {
|
||||
node.color = "ERROR";
|
||||
} else if (strstr(buf, "[LOG]")) {
|
||||
node.color = "LOG";
|
||||
}
|
||||
std::vector<ArchipelagoClient::ColoredTextNode> line;
|
||||
line.push_back(node);
|
||||
Items.push_back(line);
|
||||
}
|
||||
|
||||
void ArchipelagoConsole_PrintJson(const std::vector<ArchipelagoClient::ColoredTextNode> nodes) {
|
||||
Items.push_back(nodes);
|
||||
}
|
||||
|
||||
void ArchipelagoConsoleWindow::DrawElement() {
|
||||
@@ -28,57 +42,19 @@ void ArchipelagoConsoleWindow::DrawElement() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.15f, 0.15f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 8.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(15.0f, 12.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 1.0f));
|
||||
|
||||
if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 400), ImGuiChildFlags_AlwaysUseWindowPadding,
|
||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
|
||||
for (int i = 0; i < Items.Size; i++) {
|
||||
const char* item = Items[i];
|
||||
ImVec4 color;
|
||||
bool hasColor = false;
|
||||
if (strstr(item, "[ERROR]")) {
|
||||
color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[LOG]")) {
|
||||
color = ImVec4(0.7f, 0.7f, 1.0f, 1.0f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Found]")) {
|
||||
color = ImVec4(0.24f, 0.64f, 0.69f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Received]")) {
|
||||
color = ImVec4(0.18f, 0.76f, 0.42f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[ItemCheat]")) {
|
||||
color = ImVec4(0.97f, 0.26f, 0.26f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Hint]")) {
|
||||
color = ImVec4(0.89f, 0.45f, 1.00f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Join]")) {
|
||||
color = ImVec4(0.00f, 0.80f, 0.11f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Part]")) {
|
||||
color = ImVec4(1.00f, 0.01f, 0.01f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Goal]")) {
|
||||
color = ImVec4(0.00f, 0.70f, 0.21f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Release]")) {
|
||||
color = ImVec4(0.51f, 0.21f, 0.61f, 1.00f);
|
||||
hasColor = true;
|
||||
} else if (strstr(item, "[Collect]")) {
|
||||
color = ImVec4(0.51f, 0.21f, 0.61f, 1.00f);
|
||||
hasColor = true;
|
||||
}
|
||||
if (hasColor) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||
}
|
||||
|
||||
ImGui::TextUnformatted(item);
|
||||
|
||||
if (hasColor) {
|
||||
for(const std::vector<ArchipelagoClient::ColoredTextNode>& line : Items) {
|
||||
for(const ArchipelagoClient::ColoredTextNode& node : line) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, getColorVal(node.color));
|
||||
ImGui::TextUnformatted(node.text.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
|
||||
@@ -89,5 +65,58 @@ void ArchipelagoConsoleWindow::DrawElement() {
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
static char textEntryBuf[1024];
|
||||
static bool keepFocus = false;
|
||||
|
||||
if(keepFocus) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
keepFocus = false;
|
||||
}
|
||||
if(ImGui::InputText("##AP_MessageField", textEntryBuf, 1023, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
ArchipelagoClient::GetInstance().SendMessage(std::string(textEntryBuf));
|
||||
textEntryBuf[0] = '\0';
|
||||
keepFocus = true;
|
||||
}
|
||||
//keepFocus = ImGui::IsItemActive();
|
||||
ImGui::SameLine();
|
||||
if(ImGui::Button("Send")) {
|
||||
ArchipelagoClient::GetInstance().SendMessage(std::string(textEntryBuf));
|
||||
textEntryBuf[0] = '\0';
|
||||
keepFocus = true;
|
||||
}
|
||||
};
|
||||
|
||||
ImVec4 getColorVal(const std::string& color) { // TODO change color strings to an enum
|
||||
if (color == "ERROR") {
|
||||
return ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
|
||||
} else if(color =="LOG") {
|
||||
return ImVec4(0.7f, 0.7f, 1.0f, 1.0f);
|
||||
} else if(color == "black") {
|
||||
return ImVec4(0.000f, 0.000f, 0.000f, 1.00f);
|
||||
} else if(color == "red") {
|
||||
return ImVec4(0.933f, 0.000f, 0.000f, 1.00f);
|
||||
} else if(color == "green") {
|
||||
return ImVec4(0.000f, 1.000f, 0.498f, 1.00f);
|
||||
} else if(color == "yellow") {
|
||||
return ImVec4(0.980f, 0.980f, 0.824f, 1.00f);
|
||||
} else if(color == "blue") {
|
||||
return ImVec4(0.392f, 0.584f, 0.929f, 1.00f);
|
||||
} else if(color == "cyan") {
|
||||
return ImVec4(0.000f, 0.933f, 0.933f, 1.00f);
|
||||
} else if(color == "magenta") {
|
||||
return ImVec4(0.933f, 0.000f, 0.933f, 1.00f);
|
||||
} else if(color == "slateblue") {
|
||||
return ImVec4(0.427f, 0.545f, 0.910f, 1.00f);
|
||||
} else if(color == "plum") {
|
||||
return ImVec4(0.686f, 0.600f, 0.937f, 1.00f);
|
||||
} else if(color == "salmon") {
|
||||
return ImVec4(0.980f, 0.502f, 0.447f, 1.00f);
|
||||
} else if(color == "white") {
|
||||
return ImVec4(0.93f, 0.93f, 0.93f, 1.00f);
|
||||
} else if(color == "orange") {
|
||||
return ImVec4(1.000, 0.467f, 0.000f, 1.000f);
|
||||
}
|
||||
return ImVec4(0.93f, 0.93f, 0.93f, 1.00f);
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
#define ARCHIPELAGO_CONSOLE_WINDOW_H
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "Archipelago.h"
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class ArchipelagoConsoleWindow final : public Ship::GuiWindow {
|
||||
public:
|
||||
@@ -16,5 +19,7 @@ class ArchipelagoConsoleWindow final : public Ship::GuiWindow {
|
||||
};
|
||||
|
||||
void ArchipelagoConsole_SendMessage(const char* fmt, bool debugMessage = false, ...);
|
||||
void ArchipelagoConsole_PrintJson(const std::vector<ArchipelagoClient::ColoredTextNode> nodes);
|
||||
ImVec4 getColorVal(const std::string& color);
|
||||
|
||||
#endif // ARCHIPELAGO_CONSOLE_WINDOW_H
|
||||
Reference in New Issue
Block a user