Merge branch 'issue_#5' of https://gitlab.com/Nemirtingas/goldberg_emulator into inventory_pr

This commit is contained in:
Mr_Goldberg 2019-07-17 13:22:35 -04:00
commit f56503fcd0
No known key found for this signature in database
GPG Key ID: 8597D87419DEF278
7 changed files with 21300 additions and 21 deletions

View File

@ -16,6 +16,18 @@ 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.

60
dll/item_db_loader.cpp Normal file
View File

@ -0,0 +1,60 @@
/* Copyright (C) 2019 Nemirtingas (Maxime P)
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "item_db_loader.h"
#include <fstream>
#include "../json/json.hpp"
void read_items_db(std::string items_db, std::map<SteamItemDef_t, std::map<std::string, std::string>> *items, std::atomic_bool *is_loadedb)
{
std::ifstream items_file(items_db);
// If there is a file and we opened it
if( items_file )
{
items_file.seekg(0, std::ios::end);
size_t size = items_file.tellg();
std::string buffer(size, '\0');
items_file.seekg(0);
// Read it entirely, if the .json file gets too big,
// I should look into this and split reads into smaller parts.
items_file.read(&buffer[0], size);
items_file.close();
try
{
std::map<SteamItemDef_t, std::map<std::string, std::string>> tmp;
nlohmann::json json = nlohmann::json::parse(buffer);
for (auto& i : json.items())
{
SteamItemDef_t key = std::stoi((*i).key());
nlohmann::json& value = (*i).value();
for (auto& j : value.items())
{
tmp[key][(*j).key()] = (*j).value();
}
}
items->swap(tmp);
}
catch (std::exception& e)
{
PRINT_DEBUG("Error while parsing json: %s", e.what());
}
}
*is_loadedb = true;
}

24
dll/item_db_loader.h Normal file
View File

@ -0,0 +1,24 @@
/* Copyright (C) 2019 Nemirtingas (Maxime P)
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator;*/
#ifndef __ITEM_DB_LOADER_INCLUDED__
#define __ITEM_DB_LOADER_INCLUDED__
#include "base.h" // For SteamItemDef_t
#include <atomic>
void read_items_db(std::string items_db, std::map<SteamItemDef_t, std::map<std::string, std::string>> *items, std::atomic_bool *is_loaded);
#endif//__ITEM_DB_LOADER_INCLUDED__

5
dll/steam_inventory.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "steam_inventory.h"
std::once_flag Steam_Inventory::items_loading;
std::atomic_bool Steam_Inventory::items_loaded(false);
std::map<SteamItemDef_t, std::map<std::string, std::string>> Steam_Inventory::items;

View File

@ -15,7 +15,8 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include "item_db_loader.h"
#include <thread>
struct Steam_Inventory_Requests {
double timeout = 0.1;
@ -34,11 +35,10 @@ struct Steam_Inventory_Requests {
}
};
class Steam_Inventory :
public ISteamInventory001,
public ISteamInventory002,
public ISteamInventory
public ISteamInventory001,
public ISteamInventory002,
public ISteamInventory
{
class Settings *settings;
class SteamCallResults *callback_results;
@ -46,7 +46,21 @@ public ISteamInventory
std::vector<struct Steam_Inventory_Requests> inventory_requests;
struct Steam_Inventory_Requests *new_inventory_result(const SteamItemInstanceID_t *pInstanceIDs=NULL, uint32 unCountInstanceIDs=0)
static std::once_flag items_loading;
static std::atomic_bool items_loaded;
static 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;
struct Steam_Inventory_Requests* new_inventory_result(const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
{
static SteamInventoryResult_t result;
++result;
@ -77,6 +91,14 @@ public:
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
{
std::call_once(items_loading, [&]()
{
std::string items_db_file(Local_Storage::get_game_settings_path() + "items.json");
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();
});
this->settings = settings;
this->callbacks = callbacks;
this->callback_results = callback_results;
@ -122,7 +144,25 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
if (!request) return false;
if (!request->result_done()) return false;
if (punOutItemsArraySize) *punOutItemsArraySize = 0;
if (pOutItemsArray != nullptr)
{
uint32 max_items = *punOutItemsArraySize;
// 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 )
{
pOutItemsArray->m_iDefinition = i->first;
pOutItemsArray->m_itemId = i->first;
pOutItemsArray->m_unQuantity = 1;
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
}
*punOutItemsArraySize = std::min(*punOutItemsArraySize, static_cast<uint32>(items.size()));
}
else if (punOutItemsArraySize != nullptr)
{
*punOutItemsArraySize = items.size();
}
PRINT_DEBUG("GetResultItems good\n");
return true;
}
@ -206,8 +246,21 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG("GetAllItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result();
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
@ -224,11 +277,21 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
*pResultHandle = request->inventory_result;
return 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 false;
return true;
}
@ -488,6 +551,32 @@ 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));
}
}
}
return true;
}
@ -502,18 +591,24 @@ bool GetItemDefinitionIDs(
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
{
PRINT_DEBUG("GetItemDefinitionIDs\n");
if (!punItemDefIDsArraySize) {
if (!punItemDefIDsArraySize)
return false;
}
PRINT_DEBUG("array_size %u\n", *punItemDefIDsArraySize);
/*
if (pItemDefIDs) {
*pItemDefIDs = 0;
if (pItemDefIDs == nullptr)
{
*punItemDefIDsArraySize = items.size();
return true;
}
*/
//*punItemDefIDsArraySize = 0;
return false;
if (*punItemDefIDsArraySize < items.size())
return false;
for (auto& i : items)
*pItemDefIDs++ = i.first;
return true;
}
@ -530,6 +625,75 @@ bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPrope
STEAM_OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG("GetItemDefinitionProperty\n");
item_iterator item;
if ((item = items.find(iDefinition)) != items.end())
{
attr_iterator attr;
if (pchPropertyName != nullptr)
{
// Should I check for punValueBufferSizeOut == nullptr ?
// Try to get the property
if ((attr = item->second.find(pchPropertyName)) != items[iDefinition].end())
{
std::string const& val = attr->second;
if (pchValueBuffer != nullptr)
{
// copy what we can
strncpy(pchValueBuffer, val.c_str(), *punValueBufferSizeOut);
}
// Set punValueBufferSizeOut to the property size
*punValueBufferSizeOut = std::min(static_cast<uint32>(val.length() + 1), *punValueBufferSizeOut);
if (pchValueBuffer != nullptr)
{
// Make sure we have a null terminator
pchValueBuffer[*punValueBufferSizeOut-1] = '\0';
}
}
// Property not found
else
{
*punValueBufferSizeOut = 0;
PRINT_DEBUG("Attr %s not found for item %d\n", pchPropertyName, iDefinition);
}
}
else // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available property names.
{
// If pchValueBuffer is NULL, *punValueBufferSize will contain the suggested buffer size
if (pchValueBuffer == nullptr)
{
// Should I check for punValueBufferSizeOut == nullptr ?
*punValueBufferSizeOut = 0;
for (auto& i : item->second)
*punValueBufferSizeOut += i.first.length() + 1; // Size of key + comma, and the last is not a comma but null char
}
else
{
// strncat always add the null terminator, so remove 1 to the string length
uint32_t len = *punValueBufferSizeOut-1;
*punValueBufferSizeOut = 0;
memset(pchValueBuffer, 0, len);
for( auto i = item->second.begin(); i != item->second.end() && len > 0; ++i )
{
strncat(pchValueBuffer, i->first.c_str(), len);
// Count how many chars we copied
// Either the string length or the buffer size if its too small
uint32 x = std::min(len, static_cast<uint32>(i->first.length()));
*punValueBufferSizeOut += x;
len -= x;
if (len && std::distance(i, item->second.end()) != 1) // If this is not the last item, add a comma
strncat(pchValueBuffer, ",", len--);
// Always add 1, its a comma or the null terminator
++*punValueBufferSizeOut;
}
}
}
}
return true;
}
@ -653,4 +817,4 @@ bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventory
PRINT_DEBUG("SubmitUpdateProperties\n");
}
};
};

View File

@ -0,0 +1,95 @@
{
"2001": {
"Timestamp": "2018-01-09T19:30:03Z",
"modified": "20180109T193003Z",
"date_created": "20180109T193003Z",
"type": "bundle",
"display_type": "Bundle",
"name": "Foster Classic Bundle",
"bundle": "2011x1;2012x1;3358x1",
"description": "Comes with Foster's Classic Suit Uniform (includes 9 skin styles), Classic Tie Accessory (includes 8 skin styles) and Bow Tie Accessory. Not tradeable or marketable.",
"background_color": "000000",
"icon_url": "http://art.tripwirecdn.com/TestItemIcons/Bundle_ClassicFoster_96.png",
"icon_url_large": "http://art.tripwirecdn.com/TestItemIcons/Bundle_ClassicFoster_360.png",
"name_color": "7a0000",
"tradable": "false",
"marketable": "false",
"commodity": "false",
"drop_interval": "0",
"drop_max_per_window": "0",
"workshopid": "0",
"tw_unique_to_own": "true",
"item_quality": "0",
"tw_price": "$4.99",
"tw_type": "skc",
"tw_client_visible": "1",
"tw_icon_small": "CHR_MrFoster_Item_TEX.ClassicSuit.UniformBundle_FostersSuit",
"tw_icon_large": "CHR_MrFoster_Item_TEX.ClassicSuit.UniformBundle_FostersSuit",
"tw_description": "<FosterClassicBundleDescription:Bundles>",
"tw_client_name": "<FosterClassicBundleName:Bundles>",
"tw_client_type": "<BundleType:Bundles>",
"tw_rarity": "crate"
},
"2002": {
"Timestamp": "2018-01-09T19:30:03Z",
"modified": "20180109T193003Z",
"date_created": "20180109T193003Z",
"type": "bundle",
"display_type": "Bundle",
"name": "Briar's Bobby Bundle",
"bundle": "2021x1;2022x1",
"description": "Comes with Briar's London Uniform (includes 5 skin styles), and Custodian Helmet Cosmetic Accessory (includes 3 skin styles) Not tradeable or marketable.",
"background_color": "000000",
"icon_url": "http://art.tripwirecdn.com/TestItemIcons/Bundle_BriarBobby_96.png",
"icon_url_large": "http://art.tripwirecdn.com/TestItemIcons/Bundle_BriarBobby_360.png",
"name_color": "7a0000",
"tradable": "false",
"marketable": "false",
"commodity": "false",
"drop_interval": "0",
"drop_max_per_window": "0",
"workshopid": "0",
"tw_unique_to_own": "true",
"item_quality": "0",
"tw_price": "$4.99",
"tw_type": "skc",
"tw_client_visible": "1",
"tw_icon_small": "CHR_Briar_Item_TEX.BobbyUniform.UniformBundle_BriarBobby",
"tw_icon_large": "CHR_Briar_Item_TEX.BobbyUniform.UniformBundle_BriarBobby",
"tw_description": "<BriarsBobbyBundleDescription:Bundles>",
"tw_client_name": "<BriarsBobbyBundleName:Bundles>",
"tw_client_type": "<BundleType:Bundles>",
"tw_rarity": "crate"
},
"2003": {
"Timestamp": "2018-01-09T19:30:03Z",
"modified": "20180109T193003Z",
"date_created": "20180109T193003Z",
"type": "bundle",
"display_type": "Bundle",
"name": "Tanaka's Biker Bundle",
"bundle": "2031x1;2032x1",
"description": "Comes with Tanaka's Motorcycle Uniform (includes 7 skin styles) and Helmet Cosmetic Accessory (includes 7 skin styles) Not tradeable or marketable.",
"background_color": "000000",
"icon_url": "http://art.tripwirecdn.com/TestItemIcons/Bundle_BikerTanaka_96.png",
"icon_url_large": "http://art.tripwirecdn.com/TestItemIcons/Bundle_BikerTanaka_360.png",
"name_color": "7a0000",
"tradable": "false",
"marketable": "false",
"commodity": "false",
"drop_interval": "0",
"drop_max_per_window": "0",
"workshopid": "0",
"tw_unique_to_own": "true",
"item_quality": "0",
"tw_price": "$4.99",
"tw_type": "skc",
"tw_client_visible": "1",
"tw_icon_small": "CHR_Tanaka_01_Item_TEX.BikerUniform.UniformBundle_TanakaBiker",
"tw_icon_large": "CHR_Tanaka_01_Item_TEX.BikerUniform.UniformBundle_TanakaBiker",
"tw_description": "<TanakasBikerBundleDescription:Bundles>",
"tw_client_name": "<TanakasBikerBundleName:Bundles>",
"tw_client_type": "<BundleType:Bundles>",
"tw_rarity": "crate"
}
}

20919
json/json.hpp Normal file

File diff suppressed because it is too large Load Diff