Skip to content

Commit

Permalink
WIP: Archipelago
Browse files Browse the repository at this point in the history
  • Loading branch information
timoschwarzer committed Dec 16, 2024
1 parent d9d6316 commit c82921d
Show file tree
Hide file tree
Showing 13 changed files with 403 additions and 39 deletions.
1 change: 1 addition & 0 deletions projects/Common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(
"math_utils.h"
"settings.h"
"variant_cast.h"
"vx.h"
)

set(
Expand Down
162 changes: 162 additions & 0 deletions projects/Common/vx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#pragma once

#include <any>
#include <iostream>
#include <optional>
#include <tuple>
#include <type_traits>
#include <variant>

// https://github.com/AVasK/vx/blob/main/vx.hpp

namespace vx {

// ===== [ try_find ] =====
namespace detail {
template<typename X, typename... Ts>
struct try_find_impl {};

template<typename X, typename T, typename... Ts>
struct try_find_impl<X, T, Ts...> {
static constexpr std::optional<size_t> try_find(size_t index = 0) noexcept { return try_find_impl<X, Ts...>::try_find(index + 1); }
};

template<typename T, typename... Ts>
struct try_find_impl<T, T, Ts...> {
static constexpr std::optional<size_t> try_find(size_t index = 0) noexcept { return {index}; }
};

template<typename X>
struct try_find_impl<X> {
static constexpr std::optional<size_t> try_find(size_t = 0) noexcept { return {}; }
};
} // namespace detail

template<typename X, typename... Ts>
constexpr std::optional<size_t> try_find(size_t index = 0) {
return detail::try_find_impl<X, Ts...>::try_find(index);
}

// =====[ at ]=====
template<size_t I>
struct at_t : std::in_place_index_t<I> {};
template<size_t I>
inline constexpr at_t<I> at;

template<typename T, size_t I>
#if defined __cpp_concepts && __cplusplus >= __cpp_concepts
requires requires(T object) {
{ std::get<I>(object) };
}
#endif
decltype(auto) operator|(T && v, at_t<I>) {
return std::get<I>(std::forward<T>(v));
}


// =====[ as ]=====
template<typename T>
struct as_t : std::in_place_type_t<T> {};
template<typename T>
inline constexpr as_t<T> as;

// generic case : as acts as a static_cast (for non-variant types)
template<typename From, typename To>
constexpr auto operator|(From const& value, as_t<To>) {
return static_cast<To>(value);
}

// =====[ variant|as ]=====
template<typename... Ts, typename Type>
constexpr decltype(auto) operator|(std::variant<Ts...>& variant, as_t<Type>) {
return std::get<Type>(variant);
}

template<typename... Ts, typename Type>
constexpr decltype(auto) operator|(std::variant<Ts...> const& variant, as_t<Type>) {
return std::get<Type>(variant);
}

template<typename... Ts, typename Type>
constexpr decltype(auto) operator|(std::variant<Ts...>&& variant, as_t<Type>) {
return std::get<Type>(std::move(variant));
}

// =====[ any|as ]=====
template<typename Type>
constexpr decltype(auto) operator|(std::any & a, as_t<Type>) {
return std::any_cast<Type>(a);
}

template<typename Type>
constexpr decltype(auto) operator|(std::any const& a, as_t<Type>) {
return std::any_cast<Type>(a);
}

template<typename Type>
constexpr decltype(auto) operator|(std::any && a, as_t<Type>) {
return std::any_cast<Type>(std::move(a));
}


// =====[ is ]=====
template<typename T>
struct compare {};
template<typename T>
inline constexpr compare<T> is{};

// =====[ variant|is ]=====
template<typename... Ts, typename Type>
#if defined __cpp_concepts && __cplusplus >= __cpp_concepts
requires(try_find<Type, Ts...>() | as<bool>)
#endif
constexpr bool operator|(std::variant<Ts...> const& variant, compare<Type>) {
return std::holds_alternative<Type>(variant);
}

// =====[ any|is ]=====
//! constexpr just for being futureproof :)
#if defined __cpp_concepts && __cplusplus >= __cpp_concepts
template<typename Type, typename Any>
requires std::same_as<Any, std::any>
#else
template<typename Type, typename Any, typename = std::enable_if_t<std::is_same_v<Any, std::any>>>
#endif
constexpr bool operator|(Any const& a, compare<Type>) {
return a.type() == typeid(Type);
}


// =====[ match ]=====
template<typename... Fs>
struct match : Fs... {
using Fs::operator()...;

// constexpr match(Fs &&... fs) : Fs{fs}... {}
};
template<class... Ts>
match(Ts...) -> match<Ts...>;

template<typename... Ts, typename... Fs>
constexpr decltype(auto) operator|(std::variant<Ts...> const& v, match<Fs...> const& match) {
return std::visit(match, v);
}

template<typename... Ts, typename... Fs>
constexpr decltype(auto) operator|(std::variant<Ts...>& v, match<Fs...> const& match) {
return std::visit(match, v);
}

// =====[ optional match ]=====
template<typename T, typename... Fs>
#if defined __cpp_concepts && __cplusplus >= __cpp_concepts
requires(std::is_invocable_v<match<Fs...>, T> && std::is_invocable_v<match<Fs...>>) //&& requires(match<Fs...> const& match){ {match()}; })
#endif
constexpr decltype(auto) operator|(std::optional<T> const& o, match<Fs...> const& match) {
if (o.has_value())
return match(o.value());
else
return match();
}

} // namespace vx
1 change: 1 addition & 0 deletions projects/Core/enums/save_meta_slot.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ enum class SaveMetaSlot {
CheckpointGameStats,
SaveFileGameStats,
SeedMetaData,
ArchipelagoData,
};
4 changes: 3 additions & 1 deletion projects/Randomizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ set(
SOURCE_FILES
"archipelago/archipelago.cpp"
"archipelago/archipelago_ids.cpp"
"archipelago/archipelago_protocol.cpp"
"archipelago/archipelago_save_meta.cpp"
"conditions/condition_override.cpp"
"conditions/condition_uber_state.cpp"
"conditions/new_setup_state_override.cpp"
Expand Down Expand Up @@ -218,7 +220,7 @@ set(
PUBLIC_HEADER_FILES
"archipelago/archipelago.h"
"archipelago/archipelago_ids.h"
"archipelago/messages.h"
"archipelago/archipelago_protocol.h"
"conditions/condition_override.h"
"conditions/condition_uber_state.h"
"conditions/new_setup_state_override.h"
Expand Down
41 changes: 36 additions & 5 deletions projects/Randomizer/archipelago/archipelago.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#include <Core/events/task.h>
#include <Common/vx.h>
#include <Modloader/modloader.h>
#include <Randomizer/archipelago/archipelago.h>
#include <Randomizer/archipelago/messages.h>
#include <Randomizer/location_data/location.h>
#include <Randomizer/archipelago/archipelago_protocol.h>
#include <Randomizer/archipelago/archipelago_save_meta.h>

namespace randomizer::archipelago {
auto archipelago_save_data = std::make_shared<ArchipelagoSaveData>();

[[maybe_unused]]
auto on_game_ready = modloader::event_bus().register_handler(ModloaderEvent::GameReady, [](auto) {
core::save_meta::register_slot(SaveMetaSlot::ArchipelagoData, SaveMetaSlotPersistence::None, archipelago_save_data);
});

ArchipelagoClient::ArchipelagoClient() {
m_websocket.setOnMessageCallback([this](const auto& msg) { on_websocket_message(msg); });
}
Expand All @@ -30,9 +38,15 @@ namespace randomizer::archipelago {
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));
nlohmann::json json(message_string);

const auto message = messages::parse_server_message(json);

if (message.has_value()) {
handle_server_message(*message);
}
} catch (nlohmann::json::exception& e) {
modloader::error("archipelago", std::format("Failed to parse message: {}, {}", e.what(), message_string));
}
break;
}
Expand Down Expand Up @@ -77,4 +91,21 @@ namespace randomizer::archipelago {
break;
}
}

void ArchipelagoClient::handle_server_message(messages::ap_server_message_t const& message) {
message | vx::match {
[](const messages::Connected& message) {
// TODO
},
[](const messages::ConnectionRefused& message) {
// TODO
},
[](const messages::RoomInfo& message) {
// TODO
},
[](const messages::ReceivedItem& message) {
// TODO
},
};
}
} // namespace randomizer::archipelago
3 changes: 3 additions & 0 deletions projects/Randomizer/archipelago/archipelago.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <nlohmann/json.hpp>
#include <string>

#include "archipelago_protocol.h"

namespace randomizer::archipelago {
// TODO:
// - Add save meta slot for AP
Expand All @@ -24,6 +26,7 @@ namespace randomizer::archipelago {
}

void on_websocket_message(ix::WebSocketMessagePtr const& msg);
void handle_server_message(messages::ap_server_message_t const& message);

bool m_connected = false;
bool m_should_connect = false;
Expand Down
6 changes: 0 additions & 6 deletions projects/Randomizer/archipelago/archipelago_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,16 @@ namespace randomizer::archipelago::ids {
int8_t uber_group;
int16_t uber_state;
int8_t value;

NLOHMANN_DEFINE_TYPE_INTRUSIVE(Location, uber_group, uber_state, value);
};

struct BooleanItem {
int16_t uber_group;
int16_t uber_state;

NLOHMANN_DEFINE_TYPE_INTRUSIVE(BooleanItem, uber_group, uber_state);
};

struct ResourceItem {
ResourceType type;
int16_t value; // Only used for Spirit Light

NLOHMANN_DEFINE_TYPE_INTRUSIVE(ResourceItem, type, value);
};

archipelago_id_t get_boolean_item_id(int uber_group, int uber_state);
Expand Down
31 changes: 31 additions & 0 deletions projects/Randomizer/archipelago/archipelago_protocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <Modloader/modloader.h>
#include <Randomizer/archipelago/archipelago_protocol.h>
#include <nlohmann/json.hpp>

namespace randomizer::archipelago::messages {
std::optional<ap_server_message_t> parse_server_message(const nlohmann::json& message) {
const auto command = message.at("cmd").get<std::string>();

if (command == "Connected") {
return message.get<Connected>();
}

if (command == "ConnectionRefused") {
return message.get<ConnectionRefused>();
}

if (command == "RoomInfo") {
return message.get<RoomInfo>();
}

if (command == "ReceivedItem") {
return message.get<ReceivedItem>();
}

modloader::warn("archipelago", std::format("Failed to parse server message: Unknown command {}", command));

return std::nullopt;
}
} // namespace randomizer::archipelago::messages
Loading

0 comments on commit c82921d

Please sign in to comment.