Some cleanups.

Load items json only if there's an inventory request.

Launch callbacks only when loading is done.

Copy over original unformatted json.hpp
inventory_pr
Mr_Goldberg 2019-07-17 13:25:35 -04:00
parent f56503fcd0
commit 59a9fcee0d
No known key found for this signature in database
GPG Key ID: 8597D87419DEF278
6 changed files with 17345 additions and 17416 deletions

View File

@ -16,18 +16,6 @@ If your game has an original steam_api(64).dll or libsteam_api.so older than may
For more information see: [The Release Readme](Readme_release.txt)
## How to add items to your steam inventory
Create a folder named steam_settings right beside steam_api.dll if there isn't one already. In that folder, create a file named items.json which will contain every item you want to have in your game.
An example can be found in steam_settings.EXAMPLE that works with Killing Floor 2.
The items.json syntax is simple, you SHOULD validate your .json file before trying to run your game or you won't have any item in your inventory. Just look for "online json validator" on your web brower to valide your file.
You can use https://steamdb.info/ to list items and attributes they have and put them into your .json.
Keep in mind that some item are not valid to have in your inventory. For example, in PayDay2 all items below item_id 50000 will make your game crash.
## Download Binaries
You can download the latest git builds for Linux and Windows on [the Gitlab pages website](https://mr_goldberg.gitlab.io/goldberg_emulator/) and the stable releases in the [release section](https://gitlab.com/Mr_Goldberg/goldberg_emulator/releases) of this repo.

View File

@ -65,6 +65,14 @@ If you want to set custom ips (or domains) which the emulator will send broadcas
If the custom ips/domains are specific for one game only you can put the custom_broadcasts.txt in the steam_settings\ folder.
An example is provided in steam_settings.EXAMPLE\custom_broadcasts.EXAMPLE.txt
Items or Inventory:
Create a folder named steam_settings right beside steam_api.dll if there isn't one already. In that folder, create a file named items.json which will contain every item you want to have in your game.
An example can be found in steam_settings.EXAMPLE that works with Killing Floor 2.
The items.json syntax is simple, you SHOULD validate your .json file before trying to run your game or you won't have any item in your inventory. Just look for "online json validator" on your web brower to valide your file.
You can use https://steamdb.info/ to list items and attributes they have and put them into your .json.
Keep in mind that some item are not valid to have in your inventory. For example, in PayDay2 all items below item_id 50000 will make your game crash.
Support for CPY steam_api(64).dll cracks: See the build in the experimental folder.

View File

@ -53,8 +53,10 @@ void read_items_db(std::string items_db, std::map<SteamItemDef_t, std::map<std::
}
catch (std::exception& e)
{
PRINT_DEBUG("Error while parsing json: %s", e.what());
PRINT_DEBUG("Error while parsing json: %s\n", e.what());
}
}
PRINT_DEBUG("Loaded json. Loaded %u items.\n", items->size());
*is_loadedb = true;
}

View File

@ -291,6 +291,8 @@ Steam_Client::Steam_Client()
}
}
std::string items_db_file_path = (Local_Storage::get_game_settings_path() + "items.json");
network = new Networking(settings_server->get_local_steam_id(), appid, port, &custom_broadcasts);
callback_results_client = new SteamCallResults();
@ -318,7 +320,7 @@ Steam_Client::Steam_Client()
steam_music = new Steam_Music(callbacks_client);
steam_musicremote = new Steam_MusicRemote();
steam_HTMLsurface = new Steam_HTMLsurface(settings_client, network, callback_results_client, callbacks_client);
steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client);
steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client, run_every_runcb, items_db_file_path);
steam_video = new Steam_Video();
steam_parental = new Steam_Parental();
steam_networking_sockets = new Steam_Networking_Sockets(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
@ -335,7 +337,7 @@ Steam_Client::Steam_Client()
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server);
steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb);
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);
steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server);
steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server, run_every_runcb, items_db_file_path);
steam_gameserver_ugc = new Steam_UGC(settings_server, callback_results_server, callbacks_server);
steam_gameserver_apps = new Steam_Apps(settings_server, callback_results_server);
steam_gameserver_networking_sockets = new Steam_Networking_Sockets(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);

View File

@ -20,6 +20,8 @@
struct Steam_Inventory_Requests {
double timeout = 0.1;
bool done = false;
bool full_query;
SteamInventoryResult_t inventory_result;
std::chrono::system_clock::time_point time_created;
@ -27,7 +29,7 @@ struct Steam_Inventory_Requests {
std::vector<SteamItemInstanceID_t> instance_ids;
bool result_done() {
return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::system_clock::now() - time_created).count() > timeout;
return done;
}
uint32 timestamp() {
@ -43,30 +45,30 @@ class Steam_Inventory :
class Settings *settings;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::vector<struct Steam_Inventory_Requests> inventory_requests;
static std::once_flag items_loading;
static std::atomic_bool items_loaded;
static std::map<SteamItemDef_t, std::map<std::string, std::string>> items;
std::map<SteamItemDef_t, std::map<std::string, std::string>> items;
// Like typedefs
using item_iterator = std::map<SteamItemDef_t, std::map<std::string, std::string>>::iterator;
using attr_iterator = std::map<std::string, std::string>::iterator;
// Set this to false when we have cached everything,
// reset to true if something changed in the item db.
// Could use inotify on linux
// Could use FindFirstChangeNotificationA + WaitForSingleObject + FindNextChangeNotification on Windows to monitor the db file
// Or find a server somewhere to hold the data for us then cache on local settings.
bool need_load_definitions = true;
std::atomic_bool items_loaded;
std::string items_db_file;
std::once_flag load_items_flag;
bool call_definition_update;
bool definition_update_called;
bool full_update_called;
struct Steam_Inventory_Requests* new_inventory_result(const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
struct Steam_Inventory_Requests* new_inventory_result(bool full_query=true, const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
{
static SteamInventoryResult_t result;
++result;
struct Steam_Inventory_Requests request;
request.inventory_result = result;
request.full_query = full_query;
if (pInstanceIDs && unCountInstanceIDs) {
for (int i = 0; i < unCountInstanceIDs; ++i)
request.instance_ids.push_back(pInstanceIDs[i]);
@ -89,19 +91,34 @@ struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t res
public:
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
static void run_every_runcb_cb(void *object)
{
std::call_once(items_loading, [&]()
PRINT_DEBUG("Steam_Inventory::run_every_runcb\n");
Steam_Inventory *obj = (Steam_Inventory *)object;
obj->RunCallbacks();
}
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, std::string items_db_file_path)
{
std::string items_db_file(Local_Storage::get_game_settings_path() + "items.json");
items_db_file = items_db_file_path;
PRINT_DEBUG("Items file path: %s\n", items_db_file.c_str());
std::thread items_load_thread(read_items_db, items_db_file, &items, &items_loaded);
items_load_thread.detach();
});
items_loaded = false;
this->settings = settings;
this->callbacks = callbacks;
this->callback_results = callback_results;
this->run_every_runcb = run_every_runcb;
this->run_every_runcb->add(&Steam_Inventory::run_every_runcb_cb, this);
call_definition_update = false;
definition_update_called = false;
full_update_called = false;
}
~Steam_Inventory()
{
this->run_every_runcb->remove(&Steam_Inventory::run_every_runcb_cb, this);
}
// INVENTORY ASYNC RESULT MANAGEMENT
@ -147,6 +164,8 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
if (pOutItemsArray != nullptr)
{
uint32 max_items = *punOutItemsArraySize;
if (request->full_query) {
// We end if we reached the end of items or the end of buffer
for( auto i = items.begin(); i != items.end() && max_items; ++i, --max_items )
{
@ -157,6 +176,17 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
++pOutItemsArray;
}
*punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast<uint32>(items.size()));
} else {
for (auto &itemid : request->instance_ids) {
if (!max_items) break;
pOutItemsArray->m_iDefinition = itemid;
pOutItemsArray->m_itemId = itemid;
pOutItemsArray->m_unQuantity = 1;
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
--max_items;
}
}
}
else if (punOutItemsArraySize != nullptr)
{
@ -248,48 +278,10 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result();
// Can't call LoadItemDefinitions because it sends a SteamInventoryResultReady_t.
if( need_load_definitions )
{
if (items_loaded)
{
need_load_definitions = false;
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
if (!need_load_definitions)
{
{
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
//TODO: should this always be returned for each get all item calls?
struct SteamInventoryFullUpdate_t data;
data.m_handle = request->inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
if (!definition_update_called) call_definition_update = true;
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
}
else
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultPending;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
}
return true;
}
@ -309,14 +301,7 @@ bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unC
PRINT_DEBUG("GetItemsByID\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result(pInstanceIDs, unCountInstanceIDs);
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
struct Steam_Inventory_Requests *request = new_inventory_result(false, pInstanceIDs, unCountInstanceIDs);
*pResultHandle = request->inventory_result;
return true;
}
@ -393,14 +378,7 @@ bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_C
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (pOutResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result();
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
struct Steam_Inventory_Requests *request = new_inventory_result(false);
*pOutResultHandle = request->inventory_result;
return true;
}
@ -551,32 +529,12 @@ STEAM_METHOD_DESC(LoadItemDefinitions triggers the automatic load and refresh of
bool LoadItemDefinitions()
{
PRINT_DEBUG("LoadItemDefinitions\n");
if (need_load_definitions)
{
if (!items_loaded)
{
SteamInventoryResultReady_t data;
data.m_result = k_EResultPending;
data.m_handle = new_inventory_result()->inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
else
{
need_load_definitions = false;
{
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
{
SteamInventoryResultReady_t data = {};
data.m_result = k_EResultOK;
data.m_handle = new_inventory_result()->inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
if (!definition_update_called) {
call_definition_update = true;
}
//real steam launches a SteamInventoryResultReady_t which is why I create a new inventory result
new_inventory_result(false);
return true;
}
@ -817,4 +775,52 @@ bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventory
PRINT_DEBUG("SubmitUpdateProperties\n");
}
void RunCallbacks()
{
if (call_definition_update || inventory_requests.size()) {
std::call_once(load_items_flag, [&]() {
std::thread items_load_thread(read_items_db, items_db_file, &items, &items_loaded);
items_load_thread.detach();
});
}
if (items_loaded) {
if (call_definition_update) {
//call this callback even when 0 items?
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
call_definition_update = false;
definition_update_called = true;
}
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
for (auto & r : inventory_requests) {
if (!r.done && std::chrono::duration_cast<std::chrono::duration<double>>(now - r.time_created).count() > r.timeout) {
if (r.full_query) {
if (!full_update_called) {
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
//TODO: should this always be returned for each get all item calls?
struct SteamInventoryFullUpdate_t data;
data.m_handle = r.inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
full_update_called = true;
}
}
{
struct SteamInventoryResultReady_t data;
data.m_handle = r.inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
r.done = true;
}
}
}
}
};

View File

@ -469,14 +469,6 @@ namespace nlohmann
#endif
#endif
// C++ language standard detection
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14
#endif
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic push
@ -501,11 +493,7 @@ namespace nlohmann
// allow for portable nodiscard warnings
#if defined(__has_cpp_attribute)
#if __has_cpp_attribute(nodiscard)
#if defined(__clang__) && !defined(JSON_HAS_CPP_17) // issue #1535
#define JSON_NODISCARD
#else
#define JSON_NODISCARD [[nodiscard]]
#endif
#elif __has_cpp_attribute(gnu::warn_unused_result)
#define JSON_NODISCARD [[gnu::warn_unused_result]]
#else
@ -558,6 +546,14 @@ namespace nlohmann
#define JSON_UNLIKELY(x) x
#endif
// C++ language standard detection
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14
#endif
/*!
@brief macro to briefly define a mapping between an enum and JSON
@def NLOHMANN_JSON_SERIALIZE_ENUM
@ -1059,19 +1055,10 @@ namespace nlohmann
using object_t = typename BasicJsonType::object_t;
static constexpr bool value =
(std::is_default_constructible<ConstructibleObjectType>::value and
(std::is_move_assignable<ConstructibleObjectType>::value or
std::is_copy_assignable<ConstructibleObjectType>::value) and
(std::is_constructible<typename ConstructibleObjectType::key_type,
typename object_t::key_type>::value and
std::is_same <
typename object_t::mapped_type,
typename ConstructibleObjectType::mapped_type >::value)) or
(has_from_json<BasicJsonType,
typename ConstructibleObjectType::mapped_type>::value or
has_non_default_from_json <
BasicJsonType,
typename ConstructibleObjectType::mapped_type >::value);
(std::is_constructible<typename ConstructibleObjectType::key_type, typename object_t::key_type>::value and
std::is_same<typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type>::value) or
(has_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type>::value or
has_non_default_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type >::value);
};
template <typename BasicJsonType, typename ConstructibleObjectType>
@ -1154,9 +1141,6 @@ namespace nlohmann
BasicJsonType, ConstructibleArrayType,
enable_if_t<not std::is_same<ConstructibleArrayType,
typename BasicJsonType::value_type>::value and
std::is_default_constructible<ConstructibleArrayType>::value and
(std::is_move_assignable<ConstructibleArrayType>::value or
std::is_copy_assignable<ConstructibleArrayType>::value) and
is_detected<value_type_t, ConstructibleArrayType>::value and
is_detected<iterator_t, ConstructibleArrayType>::value and
is_complete_type<
@ -1164,14 +1148,13 @@ namespace nlohmann
{
static constexpr bool value =
// This is needed because json_reverse_iterator has a ::iterator type,
// furthermore, std::back_insert_iterator (and other iterators) have a
// base class `iterator`... Therefore it is detected as a
// ConstructibleArrayType. The real fix would be to have an Iterable
// concept.
not is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value and
// furthermore, std::back_insert_iterator (and other iterators) have a base class `iterator`...
// Therefore it is detected as a ConstructibleArrayType.
// The real fix would be to have an Iterable concept.
not is_iterator_traits <
iterator_traits<ConstructibleArrayType >>::value and
(std::is_same<typename ConstructibleArrayType::value_type,
typename BasicJsonType::array_t::value_type>::value or
(std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or
has_from_json<BasicJsonType,
typename ConstructibleArrayType::value_type>::value or
has_non_default_from_json <
@ -1424,7 +1407,6 @@ namespace nlohmann
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
l.clear();
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
{
@ -1445,16 +1427,6 @@ namespace nlohmann
std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));
}
template <typename BasicJsonType, typename T, std::size_t N>
auto from_json(const BasicJsonType& j, T(&arr)[N])
-> decltype(j.template get<T>(), void())
{
for (std::size_t i = 0; i < N; ++i)
{
arr[i] = j.at(i).template get<T>();
}
}
template<typename BasicJsonType>
void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
@ -1481,16 +1453,14 @@ namespace nlohmann
{
using std::end;
ConstructibleArrayType ret;
ret.reserve(j.size());
arr.reserve(j.size());
std::transform(j.begin(), j.end(),
std::inserter(ret, end(ret)), [](const BasicJsonType& i)
std::inserter(arr, end(arr)), [](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template <typename BasicJsonType, typename ConstructibleArrayType>
@ -1499,16 +1469,14 @@ namespace nlohmann
{
using std::end;
ConstructibleArrayType ret;
std::transform(
j.begin(), j.end(), std::inserter(ret, end(ret)),
j.begin(), j.end(), std::inserter(arr, end(arr)),
[](const BasicJsonType & i)
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
return i.template get<typename ConstructibleArrayType::value_type>();
});
arr = std::move(ret);
}
template <typename BasicJsonType, typename ConstructibleArrayType,
@ -1542,17 +1510,15 @@ namespace nlohmann
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name())));
}
ConstructibleObjectType ret;
auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
using value_type = typename ConstructibleObjectType::value_type;
std::transform(
inner_object->begin(), inner_object->end(),
std::inserter(ret, ret.begin()),
std::inserter(obj, obj.begin()),
[](typename BasicJsonType::object_t::value_type const & p)
{
return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
});
obj = std::move(ret);
}
// overload for arithmetic types, not chosen for basic_json template arguments
@ -1624,7 +1590,6 @@ namespace nlohmann
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
m.clear();
for (const auto& p : j)
{
if (JSON_UNLIKELY(not p.is_array()))
@ -1644,7 +1609,6 @@ namespace nlohmann
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
}
m.clear();
for (const auto& p : j)
{
if (JSON_UNLIKELY(not p.is_array()))
@ -5368,7 +5332,8 @@ namespace nlohmann
do
{
get();
} while (current == 'N');
}
while (current == 'N');
return current;
}
@ -6948,7 +6913,8 @@ namespace nlohmann
do
{
get();
} while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
}
while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
switch (current)
{
@ -7819,16 +7785,6 @@ namespace nlohmann
to iterator is not defined.
*/
/*!
@brief const copy constructor
@param[in] other const iterator to copy from
@note This copy constuctor had to be defined explicitely to circumvent a bug
occuring on msvc v19.0 compiler (VS 2015) debug build. For more
information refer to: https://github.com/nlohmann/json/issues/1608
*/
iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
: m_object(other.m_object), m_it(other.m_it) {}
/*!
@brief converting constructor
@param[in] other non-const iterator to copy from
@ -9238,8 +9194,7 @@ namespace nlohmann
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{
}
{}
}
/// escape "~" to "~0" and "/" to "~1"
@ -15494,19 +15449,6 @@ namespace nlohmann
return v;
}
template <
typename T, std::size_t N,
typename Array = T(&)[N],
detail::enable_if_t <
detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
Array get_to(T(&v)[N]) const
noexcept(noexcept(JSONSerializer<Array>::from_json(
std::declval<const basic_json_t&>(), v)))
{
JSONSerializer<Array>::from_json(*this, v);
return v;
}
/*!
@brief get a pointer value (implicit)
@ -16198,8 +16140,6 @@ namespace nlohmann
@return copy of the element at key @a key or @a default_value if @a key
is not found
@throw type_error.302 if @a default_value does not match the type of the
value at @a key
@throw type_error.306 if the JSON value is not an object; in that case,
using `value()` with a key makes no sense.
@ -16273,8 +16213,6 @@ namespace nlohmann
@return copy of the element at key @a key or @a default_value if @a key
is not found
@throw type_error.302 if @a default_value does not match the type of the
value at @a ptr
@throw type_error.306 if the JSON value is not an object; in that case,
using `value()` with a key makes no sense.
@ -20779,21 +20717,6 @@ namespace nlohmann
/// @}
};
/*!
@brief user-defined to_string function for JSON values
This function implements a user-defined to_string for JSON objects.
@param[in] j a JSON object
@return a std::string object
*/
NLOHMANN_BASIC_JSON_TPL_DECLARATION
std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
{
return j.dump();
}
} // namespace nlohmann
///////////////////////