-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d9ad36e
commit cb0311c
Showing
8 changed files
with
341 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#include <Core/events/task.h> | ||
#include <Modloader/modloader.h> | ||
#include <Randomizer/archipelago/archipelago.h> | ||
#include <Randomizer/archipelago/messages.h> | ||
#include <Randomizer/location_data/location.h> | ||
|
||
namespace randomizer::archipelago { | ||
ArchipelagoClient::ArchipelagoClient() { | ||
m_websocket.setOnMessageCallback([this](const auto& msg) { on_websocket_message(msg); }); | ||
} | ||
|
||
void ArchipelagoClient::connect(const std::string_view url, const std::string_view password) { | ||
m_password = password; | ||
m_websocket.stop(); | ||
m_websocket.setUrl(std::string(url)); | ||
m_websocket.setPingInterval(30); | ||
m_websocket.disableAutomaticReconnection(); | ||
m_websocket.start(); | ||
m_should_connect = true; | ||
modloader::info("archipelago", "AP client connected."); | ||
} | ||
|
||
bool ArchipelagoClient::is_connected() { | ||
return m_websocket.getReadyState() == ix::ReadyState::Open; | ||
} | ||
|
||
void ArchipelagoClient::on_websocket_message(ix::WebSocketMessagePtr const& msg) { | ||
switch (msg->type) { | ||
case ix::WebSocketMessageType::Message: { | ||
auto message_string = msg.get()->str; | ||
|
||
try { | ||
nlohmann::json message(message_string); | ||
} catch (nlohmann::json::exception e) { | ||
modloader::error("archipelago", std::format("Failed to parse message {}", message_string)); | ||
} | ||
break; | ||
} | ||
case ix::WebSocketMessageType::Open: { | ||
modloader::info("archipelago", "Connected to server"); | ||
send_message(messages::Connect{ | ||
m_password, | ||
"Ori and the Will of the Wisps", | ||
"Player", // TODO | ||
"12345667", // TODO | ||
messages::NetworkVersion{0, 5, 0}, | ||
0b101, | ||
{}, | ||
false, | ||
}); | ||
break; | ||
} | ||
case ix::WebSocketMessageType::Close: { | ||
auto closed_reason = std::format("websocket closed '{}': {}", msg->closeInfo.code, msg->closeInfo.reason.c_str()); | ||
modloader::warn("archipelago", closed_reason); | ||
|
||
if (m_should_connect) { | ||
// If we are in here we did not expect this disconnect, underlying socket will auto reconnect. | ||
core::events::schedule_task(3.f, [this] { | ||
if (m_should_connect && !is_connected()) { | ||
connect(m_websocket.getUrl(), m_password); | ||
} | ||
}); | ||
} | ||
break; | ||
} | ||
case ix::WebSocketMessageType::Error: | ||
core::events::schedule_task(10.f, [this]() { | ||
if (m_should_connect) { | ||
connect(m_websocket.getUrl(), m_password); | ||
} | ||
}); | ||
break; | ||
case ix::WebSocketMessageType::Ping: | ||
case ix::WebSocketMessageType::Pong: | ||
case ix::WebSocketMessageType::Fragment: | ||
break; | ||
} | ||
} | ||
} // namespace randomizer::archipelago |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#pragma once | ||
|
||
#include <IXWebSocket.h> | ||
#include <nlohmann/json.hpp> | ||
#include <string> | ||
|
||
namespace randomizer::archipelago { | ||
// TODO: | ||
// - Add save meta slot for AP | ||
// - Keep track of received items from AP | ||
|
||
class ArchipelagoClient { | ||
public: | ||
ArchipelagoClient(); | ||
void connect(std::string_view url, std::string_view password); | ||
void disconnect(); | ||
bool is_connected(); | ||
|
||
private: | ||
template<typename Jsonable> | ||
void send_message(const Jsonable& message) { | ||
const nlohmann::json json(message); | ||
m_websocket.send(json.dump()); | ||
} | ||
|
||
void on_websocket_message(ix::WebSocketMessagePtr const& msg); | ||
|
||
bool m_connected = false; | ||
bool m_should_connect = false; | ||
ix::WebSocket m_websocket; | ||
std::string m_password; | ||
int m_last_item_index = 0; | ||
}; | ||
} // namespace randomizer::archipelago |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
#include <Randomizer/archipelago/archipelago_ids.h> | ||
|
||
namespace randomizer::archipelago::ids { | ||
archipelago_id_t get_boolean_item_id(int uber_group, int uber_state) { | ||
assert(uber_group <= 0b11111111'11111111); | ||
assert(uber_state <= 0b11111111'11111111); | ||
|
||
return BASE_ID | ||
+ (static_cast<archipelago_id_t>(IdType::BooleanItem) << 32) | ||
+ (uber_group << 16) | ||
+ uber_state; | ||
} | ||
|
||
archipelago_id_t get_resource_item_id(ResourceType type, int16_t value) { | ||
return BASE_ID | ||
+ (static_cast<archipelago_id_t>(IdType::ResourceItem) << 32) | ||
+ (static_cast<archipelago_id_t>(type) << 16) | ||
+ value; | ||
} | ||
|
||
archipelago_id_t get_location_id(const location_data::Location& location) { | ||
assert(location.condition.value <= 0b11111111); | ||
assert(location.condition.state.state() <= 0b11111111'11111111); | ||
|
||
const archipelago_id_t group = (location.condition.state.group_int() & 0b11111111) << 24; | ||
const archipelago_id_t state = (location.condition.state.state() & 0b11111111'11111111) << 8; | ||
const archipelago_id_t value = static_cast<archipelago_id_t>(location.condition.value); | ||
|
||
return BASE_ID + (static_cast<archipelago_id_t>(IdType::Location) << 32) + group + state + value; | ||
} | ||
|
||
std::variant<Location, BooleanItem, ResourceItem> get_item(archipelago_id_t id) { | ||
const auto id_type = static_cast<IdType>(id >> 32); | ||
|
||
switch (id_type) { | ||
case IdType::Location: | ||
return Location { | ||
static_cast<int8_t>(id >> 24), | ||
static_cast<int16_t>(id >> 8), | ||
static_cast<int8_t>(id), | ||
}; | ||
case IdType::BooleanItem: | ||
return BooleanItem { | ||
static_cast<int16_t>(id >> 16), | ||
static_cast<int16_t>(id), | ||
}; | ||
case IdType::ResourceItem: | ||
return ResourceItem { | ||
static_cast<ResourceType>(id >> 16), | ||
static_cast<int16_t>(id), | ||
}; | ||
} | ||
|
||
throw std::runtime_error(std::format("Could not process archipelago id {}", id)); | ||
} | ||
} // namespace randomizer::archipelago::ids |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#pragma once | ||
|
||
#include <Randomizer/location_data/location.h> | ||
#include <variant> | ||
|
||
namespace randomizer::archipelago::ids { | ||
using archipelago_id_t = uint64_t; | ||
|
||
/** | ||
* Prefix (18 bits): "101111110010101001" | ||
* | ||
* Locations (34 bits): "00", 8 bits group, 16 bits state, 8 bits value (operator is implied, 0 ^= greater, everything else ^= greater or equal) | ||
* Boolean items (34 bits): "01", 16 bits group, 16 bits state | ||
* Resource items (34 bits): "10", 16 bits resource type, 16 bits resource value (spirit light only) | ||
* | ||
* Resource Types: | ||
* 0b001: 100 spirit light | ||
* 0b010: gorlek ore | ||
* 0b011: keystones | ||
* 0b100: shard slots | ||
* 0b101: health fragments | ||
* 0b110: energy fragments | ||
* | ||
* Decimal range: 3363010932375552 - 3363028112244735 | ||
*/ | ||
|
||
constexpr archipelago_id_t BASE_ID = 0b101111110010101001LL << 34; // ori (18 bit) | ||
|
||
enum class IdType : int8_t { | ||
Location = 0b00, | ||
BooleanItem = 0b01, | ||
ResourceItem = 0b10, | ||
}; | ||
|
||
enum class ResourceType : int16_t { | ||
SpiritLight = 0b001, | ||
GorlekOre = 0b010, | ||
Keystone = 0b011, | ||
ShardSlot = 0b100, | ||
HealthFragment = 0b101, | ||
EnergyFragment = 0b110, | ||
}; | ||
|
||
struct Location { | ||
int8_t uber_group; | ||
int16_t uber_state; | ||
int8_t value; | ||
}; | ||
|
||
struct BooleanItem { | ||
int16_t uber_group; | ||
int16_t uber_state; | ||
}; | ||
|
||
struct ResourceItem { | ||
ResourceType type; | ||
int16_t value; // Only used for Spirit Light | ||
}; | ||
|
||
archipelago_id_t get_boolean_item_id(int uber_group, int uber_state); | ||
archipelago_id_t get_resource_item_id(ResourceType type, archipelago_id_t uid); | ||
archipelago_id_t get_location_id(const location_data::Location& location); | ||
std::variant<Location, BooleanItem, ResourceItem> get_item(archipelago_id_t id); | ||
} // namespace randomizer::archipelago::ids |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#pragma once | ||
|
||
#include <nlohmann/json.hpp> | ||
|
||
namespace randomizer::archipelago::messages { | ||
struct NetworkVersion { | ||
int major; | ||
int minor; | ||
int patch; | ||
}; | ||
|
||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NetworkVersion, major, minor, patch); | ||
|
||
struct Connect { | ||
std::string password; | ||
std::string game; | ||
std::string name; | ||
std::string uuid; | ||
NetworkVersion version; | ||
int items_handling; | ||
std::vector<std::string> tags; | ||
bool slot_data; | ||
}; | ||
|
||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Connect, password, game, name, uuid, version, items_handling, tags, slot_data); | ||
} |
Oops, something went wrong.