feat: remove network related content (#510)
This commit is contained in:
parent
9c6d6a6098
commit
b74a9e371e
@ -1,199 +0,0 @@
|
||||
#pragma once
|
||||
#include "http_request.hpp"
|
||||
|
||||
namespace big::api
|
||||
{
|
||||
const std::string domain = "http://home.damon.sh:8089/api/v1";
|
||||
//const std::string domain = "http://localhost:8080/api/v1";
|
||||
inline std::string session_id;
|
||||
|
||||
namespace util
|
||||
{
|
||||
static std::string authorization_header()
|
||||
{
|
||||
return std::string("Authorization: ") + api::session_id;
|
||||
}
|
||||
|
||||
static bool parse_body(http::Response& res, nlohmann::json& out)
|
||||
{
|
||||
try
|
||||
{
|
||||
out = nlohmann::json::parse(res.body.begin(), res.body.end());
|
||||
|
||||
return out["status"] == std::string("success");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
out = nullptr;
|
||||
|
||||
LOG(INFO) << "Failed to parse request body: " << std::endl << e.what();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool signed_in()
|
||||
{
|
||||
return !session_id.empty();
|
||||
}
|
||||
}
|
||||
|
||||
namespace auth
|
||||
{
|
||||
static bool create_session()
|
||||
{
|
||||
static std::atomic_bool busy = false;
|
||||
if (busy || g_local_player == nullptr) return false;
|
||||
busy = true;
|
||||
|
||||
const std::string path = "/auth/create_session";
|
||||
|
||||
http::Request request(domain + path);
|
||||
|
||||
auto player_info = g_local_player->m_player_info;
|
||||
auto& netData = player_info->m_net_player_data;
|
||||
|
||||
nlohmann::json body = {
|
||||
{ "username", std::string(netData.m_name) },
|
||||
{ "rockstar_id", netData.m_gamer_handle_2.m_rockstar_id }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
http::Response res = request.send("POST", body.dump(), {
|
||||
"Content-Type: application/json"
|
||||
}, 10000ms);
|
||||
|
||||
nlohmann::json json;
|
||||
if (util::parse_body(res, json))
|
||||
{
|
||||
session_id = json["data"]["sessionId"].get<std::string>();
|
||||
|
||||
LOG(INFO) << "Create session and received ID: " << session_id.c_str();
|
||||
|
||||
busy = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG(INFO) << "Host is down, unable to create session.";
|
||||
|
||||
busy = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Failed to create a session.";
|
||||
|
||||
busy = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace vehicle
|
||||
{
|
||||
namespace handling
|
||||
{
|
||||
static bool create_profile(uint32_t handling_hash, const char* name, const char* description, nlohmann::json& handling_data, nlohmann::json& out)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/create";
|
||||
|
||||
http::Request request(domain + path);
|
||||
|
||||
out["handling_hash"] = handling_hash;
|
||||
out["name"] = std::string(name);
|
||||
out["description"] = std::string(description);
|
||||
out["data"] = handling_data;
|
||||
|
||||
http::Response res = request.send("POST", out.dump(), {
|
||||
util::authorization_header()
|
||||
});
|
||||
return util::parse_body(res, out);
|
||||
}
|
||||
|
||||
static bool get_by_share_code(std::string share_code, nlohmann::json& out)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/get_by_share_code?share_code=";
|
||||
|
||||
http::Request request(domain + path + share_code);
|
||||
|
||||
http::Response res = request.send("GET", "", {
|
||||
util::authorization_header()
|
||||
});
|
||||
|
||||
return util::parse_body(res, out);
|
||||
}
|
||||
|
||||
static bool get_my_handling(uint32_t handling_hash, nlohmann::json &out)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/get_mine?handling_hash=";
|
||||
|
||||
http::Request request(domain + path + std::to_string(handling_hash));
|
||||
|
||||
http::Response res = request.send("GET", "", {
|
||||
util::authorization_header()
|
||||
});
|
||||
|
||||
return util::parse_body(res, out);
|
||||
}
|
||||
|
||||
static bool get_saved_handling(uint32_t handling_hash, nlohmann::json& out)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/get_saved?handling_hash=";
|
||||
|
||||
http::Request request(domain + path + std::to_string(handling_hash));
|
||||
|
||||
http::Response res = request.send("GET", "", {
|
||||
util::authorization_header()
|
||||
});
|
||||
|
||||
return util::parse_body(res, out);
|
||||
}
|
||||
|
||||
static bool save_profile(std::string share_code)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/save_profile";
|
||||
|
||||
http::Request request(domain + path);
|
||||
|
||||
nlohmann::json body = { { "share_code", share_code } };
|
||||
|
||||
http::Response res = request.send("POST", body.dump(), {
|
||||
util::authorization_header()
|
||||
});
|
||||
return util::parse_body(res, body);
|
||||
}
|
||||
|
||||
static bool update(uint32_t handling_hash, const char* name, const char* description, std::string share_code, nlohmann::json &update)
|
||||
{
|
||||
if (!util::signed_in()) return false;
|
||||
|
||||
const std::string path = "/vehicle/handling/update";
|
||||
|
||||
http::Request request(domain + path);
|
||||
|
||||
nlohmann::json json;
|
||||
json["handling_hash"] = handling_hash;
|
||||
json["name"] = std::string(name);
|
||||
json["description"] = std::string(description);
|
||||
json["data"] = update;
|
||||
json["share_code"] = share_code;
|
||||
|
||||
http::Response res = request.send("POST", json.dump(), {
|
||||
util::authorization_header()
|
||||
});
|
||||
return util::parse_body(res, update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,772 +0,0 @@
|
||||
//
|
||||
// HTTPRequest
|
||||
//
|
||||
|
||||
#ifndef HTTPREQUEST_HPP
|
||||
#define HTTPREQUEST_HPP
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
# pragma push_macro("WIN32_LEAN_AND_MEAN")
|
||||
# pragma push_macro("NOMINMAX")
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif // WIN32_LEAN_AND_MEAN
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif // NOMINMAX
|
||||
# include <winsock2.h>
|
||||
# if _WIN32_WINNT < _WIN32_WINNT_WINXP
|
||||
extern "C" char* _strdup(const char* strSource);
|
||||
# define strdup _strdup
|
||||
# include <wspiapi.h>
|
||||
# endif // _WIN32_WINNT < _WIN32_WINNT_WINXP
|
||||
# include <ws2tcpip.h>
|
||||
# pragma pop_macro("WIN32_LEAN_AND_MEAN")
|
||||
# pragma pop_macro("NOMINMAX")
|
||||
#else
|
||||
# include <errno.h>
|
||||
# include <fcntl.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netdb.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
namespace http
|
||||
{
|
||||
class RequestError final : public std::logic_error
|
||||
{
|
||||
public:
|
||||
explicit RequestError(const char* str) : std::logic_error{ str } {}
|
||||
explicit RequestError(const std::string& str) : std::logic_error{ str } {}
|
||||
};
|
||||
|
||||
class ResponseError final : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit ResponseError(const char* str) : std::runtime_error{ str } {}
|
||||
explicit ResponseError(const std::string& str) : std::runtime_error{ str } {}
|
||||
};
|
||||
|
||||
enum class InternetProtocol : std::uint8_t
|
||||
{
|
||||
V4,
|
||||
V6
|
||||
};
|
||||
|
||||
inline namespace detail
|
||||
{
|
||||
#ifdef _WIN32
|
||||
class WinSock final
|
||||
{
|
||||
public:
|
||||
WinSock()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
const auto error = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (error != 0)
|
||||
throw std::system_error(error, std::system_category(), "WSAStartup failed");
|
||||
|
||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
|
||||
{
|
||||
WSACleanup();
|
||||
throw std::runtime_error("Invalid WinSock version");
|
||||
}
|
||||
|
||||
started = true;
|
||||
}
|
||||
|
||||
~WinSock()
|
||||
{
|
||||
if (started) WSACleanup();
|
||||
}
|
||||
|
||||
WinSock(WinSock&& other) noexcept :
|
||||
started{ other.started }
|
||||
{
|
||||
other.started = false;
|
||||
}
|
||||
|
||||
WinSock& operator=(WinSock&& other) noexcept
|
||||
{
|
||||
if (&other == this) return *this;
|
||||
if (started) WSACleanup();
|
||||
started = other.started;
|
||||
other.started = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool started = false;
|
||||
};
|
||||
#endif // _WIN32
|
||||
|
||||
inline int getLastError() noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
constexpr int getAddressFamily(InternetProtocol internetProtocol)
|
||||
{
|
||||
return (internetProtocol == InternetProtocol::V4) ? AF_INET :
|
||||
(internetProtocol == InternetProtocol::V6) ? AF_INET6 :
|
||||
throw RequestError("Unsupported protocol");
|
||||
}
|
||||
|
||||
class Socket final
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
using Type = SOCKET;
|
||||
static constexpr Type invalid = INVALID_SOCKET;
|
||||
#else
|
||||
using Type = int;
|
||||
static constexpr Type invalid = -1;
|
||||
#endif // _WIN32
|
||||
|
||||
explicit Socket(InternetProtocol internetProtocol) :
|
||||
endpoint{ socket(getAddressFamily(internetProtocol), SOCK_STREAM, IPPROTO_TCP) }
|
||||
{
|
||||
if (endpoint == invalid)
|
||||
throw std::system_error(getLastError(), std::system_category(), "Failed to create socket");
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long mode = 1;
|
||||
if (ioctlsocket(endpoint, FIONBIO, &mode) != 0)
|
||||
{
|
||||
close();
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to get socket flags");
|
||||
}
|
||||
#else
|
||||
const auto flags = fcntl(endpoint, F_GETFL);
|
||||
if (flags == -1)
|
||||
{
|
||||
close();
|
||||
throw std::system_error(errno, std::system_category(), "Failed to get socket flags");
|
||||
}
|
||||
|
||||
if (fcntl(endpoint, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
{
|
||||
close();
|
||||
throw std::system_error(errno, std::system_category(), "Failed to set socket flags");
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef __APPLE__
|
||||
const int value = 1;
|
||||
if (setsockopt(endpoint, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)) == -1)
|
||||
{
|
||||
close();
|
||||
throw std::system_error(errno, std::system_category(), "Failed to set socket option");
|
||||
}
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
~Socket()
|
||||
{
|
||||
if (endpoint != invalid) close();
|
||||
}
|
||||
|
||||
Socket(Socket&& other) noexcept :
|
||||
endpoint{ other.endpoint }
|
||||
{
|
||||
other.endpoint = invalid;
|
||||
}
|
||||
|
||||
Socket& operator=(Socket&& other) noexcept
|
||||
{
|
||||
if (&other == this) return *this;
|
||||
if (endpoint != invalid) close();
|
||||
endpoint = other.endpoint;
|
||||
other.endpoint = invalid;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void connect(const struct sockaddr* address, const socklen_t addressSize, const std::int64_t timeout)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto result = ::connect(endpoint, address, addressSize);
|
||||
while (result == -1 && WSAGetLastError() == WSAEINTR)
|
||||
result = ::connect(endpoint, address, addressSize);
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
{
|
||||
select(SelectType::write, timeout);
|
||||
|
||||
char socketErrorPointer[sizeof(int)];
|
||||
socklen_t optionLength = sizeof(socketErrorPointer);
|
||||
if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, socketErrorPointer, &optionLength) == -1)
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to get socket option");
|
||||
|
||||
int socketError;
|
||||
std::memcpy(&socketError, socketErrorPointer, sizeof(socketErrorPointer));
|
||||
|
||||
if (socketError != 0)
|
||||
throw std::system_error(socketError, std::system_category(), "Failed to connect");
|
||||
}
|
||||
else
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to connect");
|
||||
}
|
||||
#else
|
||||
auto result = ::connect(endpoint, address, addressSize);
|
||||
while (result == -1 && errno == EINTR)
|
||||
result = ::connect(endpoint, address, addressSize);
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
if (errno == EINPROGRESS)
|
||||
{
|
||||
select(SelectType::write, timeout);
|
||||
|
||||
int socketError;
|
||||
socklen_t optionLength = sizeof(socketError);
|
||||
if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, &socketError, &optionLength) == -1)
|
||||
throw std::system_error(errno, std::system_category(), "Failed to get socket option");
|
||||
|
||||
if (socketError != 0)
|
||||
throw std::system_error(socketError, std::system_category(), "Failed to connect");
|
||||
}
|
||||
else
|
||||
throw std::system_error(errno, std::system_category(), "Failed to connect");
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
std::size_t send(const void* buffer, const std::size_t length, const std::int64_t timeout)
|
||||
{
|
||||
select(SelectType::write, timeout);
|
||||
#ifdef _WIN32
|
||||
auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
|
||||
static_cast<int>(length), 0);
|
||||
|
||||
while (result == -1 && WSAGetLastError() == WSAEINTR)
|
||||
result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
|
||||
static_cast<int>(length), 0);
|
||||
|
||||
if (result == -1)
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to send data");
|
||||
#else
|
||||
auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
|
||||
length, noSignal);
|
||||
|
||||
while (result == -1 && errno == EINTR)
|
||||
result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
|
||||
length, noSignal);
|
||||
|
||||
if (result == -1)
|
||||
throw std::system_error(errno, std::system_category(), "Failed to send data");
|
||||
#endif // _WIN32
|
||||
return static_cast<std::size_t>(result);
|
||||
}
|
||||
|
||||
std::size_t recv(void* buffer, const std::size_t length, const std::int64_t timeout)
|
||||
{
|
||||
select(SelectType::read, timeout);
|
||||
#ifdef _WIN32
|
||||
auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
|
||||
static_cast<int>(length), 0);
|
||||
|
||||
while (result == -1 && WSAGetLastError() == WSAEINTR)
|
||||
result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
|
||||
static_cast<int>(length), 0);
|
||||
|
||||
if (result == -1)
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to read data");
|
||||
#else
|
||||
auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
|
||||
length, noSignal);
|
||||
|
||||
while (result == -1 && errno == EINTR)
|
||||
result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
|
||||
length, noSignal);
|
||||
|
||||
if (result == -1)
|
||||
throw std::system_error(errno, std::system_category(), "Failed to read data");
|
||||
#endif // _WIN32
|
||||
return static_cast<std::size_t>(result);
|
||||
}
|
||||
|
||||
operator Type() const noexcept { return endpoint; }
|
||||
|
||||
private:
|
||||
enum class SelectType
|
||||
{
|
||||
read,
|
||||
write
|
||||
};
|
||||
|
||||
void select(const SelectType type, const std::int64_t timeout)
|
||||
{
|
||||
fd_set descriptorSet;
|
||||
FD_ZERO(&descriptorSet);
|
||||
FD_SET(endpoint, &descriptorSet);
|
||||
|
||||
timeval selectTimeout{
|
||||
static_cast<decltype(timeval::tv_sec)>(timeout / 1000),
|
||||
static_cast<decltype(timeval::tv_usec)>((timeout % 1000) * 1000)
|
||||
};
|
||||
#ifdef _WIN32
|
||||
auto count = ::select(0,
|
||||
(type == SelectType::read) ? &descriptorSet : nullptr,
|
||||
(type == SelectType::write) ? &descriptorSet : nullptr,
|
||||
nullptr,
|
||||
(timeout >= 0) ? &selectTimeout : nullptr);
|
||||
|
||||
while (count == -1 && WSAGetLastError() == WSAEINTR)
|
||||
count = ::select(0,
|
||||
(type == SelectType::read) ? &descriptorSet : nullptr,
|
||||
(type == SelectType::write) ? &descriptorSet : nullptr,
|
||||
nullptr,
|
||||
(timeout >= 0) ? &selectTimeout : nullptr);
|
||||
|
||||
if (count == -1)
|
||||
throw std::system_error(WSAGetLastError(), std::system_category(), "Failed to select socket");
|
||||
else if (count == 0)
|
||||
throw ResponseError("Request timed out");
|
||||
#else
|
||||
auto count = ::select(endpoint + 1,
|
||||
(type == SelectType::read) ? &descriptorSet : nullptr,
|
||||
(type == SelectType::write) ? &descriptorSet : nullptr,
|
||||
nullptr,
|
||||
(timeout >= 0) ? &selectTimeout : nullptr);
|
||||
|
||||
while (count == -1 && errno == EINTR)
|
||||
count = ::select(endpoint + 1,
|
||||
(type == SelectType::read) ? &descriptorSet : nullptr,
|
||||
(type == SelectType::write) ? &descriptorSet : nullptr,
|
||||
nullptr,
|
||||
(timeout >= 0) ? &selectTimeout : nullptr);
|
||||
|
||||
if (count == -1)
|
||||
throw std::system_error(errno, std::system_category(), "Failed to select socket");
|
||||
else if (count == 0)
|
||||
throw ResponseError("Request timed out");
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
void close() noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
closesocket(endpoint);
|
||||
#else
|
||||
::close(endpoint);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
#if defined(__unix__) && !defined(__APPLE__)
|
||||
static constexpr int noSignal = MSG_NOSIGNAL;
|
||||
#else
|
||||
static constexpr int noSignal = 0;
|
||||
#endif // defined(__unix__) && !defined(__APPLE__)
|
||||
|
||||
Type endpoint = invalid;
|
||||
};
|
||||
}
|
||||
|
||||
struct Response final
|
||||
{
|
||||
enum Status
|
||||
{
|
||||
Continue = 100,
|
||||
SwitchingProtocol = 101,
|
||||
Processing = 102,
|
||||
EarlyHints = 103,
|
||||
|
||||
Ok = 200,
|
||||
Created = 201,
|
||||
Accepted = 202,
|
||||
NonAuthoritativeInformation = 203,
|
||||
NoContent = 204,
|
||||
ResetContent = 205,
|
||||
PartialContent = 206,
|
||||
MultiStatus = 207,
|
||||
AlreadyReported = 208,
|
||||
ImUsed = 226,
|
||||
|
||||
MultipleChoice = 300,
|
||||
MovedPermanently = 301,
|
||||
Found = 302,
|
||||
SeeOther = 303,
|
||||
NotModified = 304,
|
||||
UseProxy = 305,
|
||||
TemporaryRedirect = 307,
|
||||
PermanentRedirect = 308,
|
||||
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
PaymentRequired = 402,
|
||||
Forbidden = 403,
|
||||
NotFound = 404,
|
||||
MethodNotAllowed = 405,
|
||||
NotAcceptable = 406,
|
||||
ProxyAuthenticationRequired = 407,
|
||||
RequestTimeout = 408,
|
||||
Conflict = 409,
|
||||
Gone = 410,
|
||||
LengthRequired = 411,
|
||||
PreconditionFailed = 412,
|
||||
PayloadTooLarge = 413,
|
||||
UriTooLong = 414,
|
||||
UnsupportedMediaType = 415,
|
||||
RangeNotSatisfiable = 416,
|
||||
ExpectationFailed = 417,
|
||||
MisdirectedRequest = 421,
|
||||
UnprocessableEntity = 422,
|
||||
Locked = 423,
|
||||
FailedDependency = 424,
|
||||
TooEarly = 425,
|
||||
UpgradeRequired = 426,
|
||||
PreconditionRequired = 428,
|
||||
TooManyRequests = 429,
|
||||
RequestHeaderFieldsTooLarge = 431,
|
||||
UnavailableForLegalReasons = 451,
|
||||
|
||||
InternalServerError = 500,
|
||||
NotImplemented = 501,
|
||||
BadGateway = 502,
|
||||
ServiceUnavailable = 503,
|
||||
GatewayTimeout = 504,
|
||||
HttpVersionNotSupported = 505,
|
||||
VariantAlsoNegotiates = 506,
|
||||
InsufficientStorage = 507,
|
||||
LoopDetected = 508,
|
||||
NotExtended = 510,
|
||||
NetworkAuthenticationRequired = 511
|
||||
};
|
||||
|
||||
int status = 0;
|
||||
std::string description;
|
||||
std::vector<std::string> headers;
|
||||
std::vector<std::uint8_t> body;
|
||||
};
|
||||
|
||||
class Request final
|
||||
{
|
||||
public:
|
||||
explicit Request(const std::string& url,
|
||||
const InternetProtocol protocol = InternetProtocol::V4) :
|
||||
internetProtocol{ protocol }
|
||||
{
|
||||
const auto schemeEndPosition = url.find("://");
|
||||
|
||||
if (schemeEndPosition != std::string::npos)
|
||||
{
|
||||
scheme = url.substr(0, schemeEndPosition);
|
||||
path = url.substr(schemeEndPosition + 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
scheme = "http";
|
||||
path = url;
|
||||
}
|
||||
|
||||
const auto fragmentPosition = path.find('#');
|
||||
|
||||
// remove the fragment part
|
||||
if (fragmentPosition != std::string::npos)
|
||||
path.resize(fragmentPosition);
|
||||
|
||||
const auto pathPosition = path.find('/');
|
||||
|
||||
if (pathPosition == std::string::npos)
|
||||
{
|
||||
domain = path;
|
||||
path = "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
domain = path.substr(0, pathPosition);
|
||||
path = path.substr(pathPosition);
|
||||
}
|
||||
|
||||
const auto portPosition = domain.find(':');
|
||||
|
||||
if (portPosition != std::string::npos)
|
||||
{
|
||||
port = domain.substr(portPosition + 1);
|
||||
domain.resize(portPosition);
|
||||
}
|
||||
else
|
||||
port = "80";
|
||||
}
|
||||
|
||||
Response send(const std::string& method = "GET",
|
||||
const std::string& body = "",
|
||||
const std::vector<std::string>& headers = {},
|
||||
const std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 })
|
||||
{
|
||||
return send(method,
|
||||
std::vector<uint8_t>(body.begin(), body.end()),
|
||||
headers,
|
||||
timeout);
|
||||
}
|
||||
|
||||
Response send(const std::string& method,
|
||||
const std::vector<uint8_t>& body,
|
||||
const std::vector<std::string>& headers,
|
||||
const std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 })
|
||||
{
|
||||
const auto stopTime = std::chrono::steady_clock::now() + timeout;
|
||||
|
||||
if (scheme != "http")
|
||||
throw RequestError("Only HTTP scheme is supported");
|
||||
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = getAddressFamily(internetProtocol);
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
addrinfo* info;
|
||||
if (getaddrinfo(domain.c_str(), port.c_str(), &hints, &info) != 0)
|
||||
throw std::system_error(getLastError(), std::system_category(), "Failed to get address info of " + domain);
|
||||
|
||||
std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> addressInfo(info, freeaddrinfo);
|
||||
|
||||
// RFC 7230, 3.1.1. Request Line
|
||||
std::string headerData = method + " " + path + " HTTP/1.1\r\n";
|
||||
|
||||
for (const auto& header : headers)
|
||||
headerData += header + "\r\n";
|
||||
|
||||
// RFC 7230, 3.2. Header Fields
|
||||
headerData += "Host: " + domain + "\r\n"
|
||||
"Content-Length: " + std::to_string(body.size()) + "\r\n"
|
||||
"\r\n";
|
||||
|
||||
std::vector<uint8_t> requestData(headerData.begin(), headerData.end());
|
||||
requestData.insert(requestData.end(), body.begin(), body.end());
|
||||
|
||||
Socket socket(internetProtocol);
|
||||
|
||||
// take the first address from the list
|
||||
socket.connect(addressInfo->ai_addr, static_cast<socklen_t>(addressInfo->ai_addrlen),
|
||||
(timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
|
||||
|
||||
auto remaining = requestData.size();
|
||||
auto sendData = requestData.data();
|
||||
|
||||
// send the request
|
||||
while (remaining > 0)
|
||||
{
|
||||
const auto size = socket.send(sendData, remaining,
|
||||
(timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
|
||||
remaining -= size;
|
||||
sendData += size;
|
||||
}
|
||||
|
||||
std::array<std::uint8_t, 4096> tempBuffer;
|
||||
constexpr std::array<std::uint8_t, 2> crlf = { '\r', '\n' };
|
||||
Response response;
|
||||
std::vector<std::uint8_t> responseData;
|
||||
enum class State
|
||||
{
|
||||
statusLine,
|
||||
headers,
|
||||
body
|
||||
} state = State::statusLine;
|
||||
bool contentLengthReceived = false;
|
||||
std::size_t contentLength = 0;
|
||||
bool chunkedResponse = false;
|
||||
std::size_t expectedChunkSize = 0;
|
||||
bool removeCrlfAfterChunk = false;
|
||||
|
||||
// read the response
|
||||
for (;;)
|
||||
{
|
||||
const auto size = socket.recv(tempBuffer.data(), tempBuffer.size(),
|
||||
(timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
// download complete
|
||||
return response;
|
||||
}
|
||||
else if (size < 0)
|
||||
{
|
||||
throw ResponseError("Socket error");
|
||||
}
|
||||
|
||||
responseData.insert(responseData.end(), tempBuffer.begin(), tempBuffer.begin() + size);
|
||||
|
||||
if (state != State::body)
|
||||
for (;;)
|
||||
{
|
||||
// RFC 7230, 3. Message Format
|
||||
const auto i = std::search(responseData.begin(), responseData.end(), crlf.begin(), crlf.end());
|
||||
|
||||
// didn't find a newline
|
||||
if (i == responseData.end()) break;
|
||||
|
||||
const std::string line(responseData.begin(), i);
|
||||
responseData.erase(responseData.begin(), i + 2);
|
||||
|
||||
// empty line indicates the end of the header section
|
||||
if (line.empty())
|
||||
{
|
||||
state = State::body;
|
||||
break;
|
||||
}
|
||||
else if (state == State::statusLine) // RFC 7230, 3.1.2. Status Line
|
||||
{
|
||||
state = State::headers;
|
||||
std::size_t partNum = 0;
|
||||
|
||||
// tokenize the status line
|
||||
for (auto beginIterator = line.begin(); beginIterator != line.end();)
|
||||
{
|
||||
const auto endIterator = std::find(beginIterator, line.end(), ' ');
|
||||
const std::string part{ beginIterator, endIterator };
|
||||
|
||||
switch (++partNum)
|
||||
{
|
||||
case 2: response.status = std::stoi(part); break;
|
||||
case 3: response.description = part; break;
|
||||
}
|
||||
|
||||
if (endIterator == line.end()) break;
|
||||
beginIterator = endIterator + 1;
|
||||
}
|
||||
}
|
||||
else if (state == State::headers) // RFC 7230, 3.2. Header Fields
|
||||
{
|
||||
response.headers.push_back(line);
|
||||
|
||||
const auto loumnPosition = line.find(':');
|
||||
|
||||
if (loumnPosition == std::string::npos)
|
||||
throw ResponseError("Invalid header: " + line);
|
||||
|
||||
const auto headerName = line.substr(0, loumnPosition);
|
||||
auto headerValue = line.substr(loumnPosition + 1);
|
||||
|
||||
// RFC 7230, Appendix B. Collected ABNF
|
||||
auto isNotWhiteSpace = [](char c) {
|
||||
return c != ' ' && c != '\t';
|
||||
};
|
||||
|
||||
// ltrim
|
||||
headerValue.erase(headerValue.begin(), std::find_if(headerValue.begin(), headerValue.end(), isNotWhiteSpace));
|
||||
|
||||
// rtrim
|
||||
headerValue.erase(std::find_if(headerValue.rbegin(), headerValue.rend(), isNotWhiteSpace).base(), headerValue.end());
|
||||
|
||||
if (headerName == "Content-Length")
|
||||
{
|
||||
contentLength = std::stoul(headerValue);
|
||||
contentLengthReceived = true;
|
||||
response.body.reserve(contentLength);
|
||||
}
|
||||
else if (headerName == "Transfer-Encoding")
|
||||
{
|
||||
if (headerValue == "chunked")
|
||||
chunkedResponse = true;
|
||||
else
|
||||
throw ResponseError("Unsupported transfer encoding: " + headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == State::body)
|
||||
{
|
||||
if (method == "HEAD") // Body must be ignored for HEAD requests
|
||||
{
|
||||
return response;
|
||||
}
|
||||
else if (chunkedResponse) // Content-Length must be ignored if Transfer-Encoding is received
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (expectedChunkSize > 0)
|
||||
{
|
||||
const auto toWrite = (std::min)(expectedChunkSize, responseData.size());
|
||||
response.body.insert(response.body.end(), responseData.begin(), responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
|
||||
responseData.erase(responseData.begin(), responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
|
||||
expectedChunkSize -= toWrite;
|
||||
|
||||
if (expectedChunkSize == 0) removeCrlfAfterChunk = true;
|
||||
if (responseData.empty()) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (removeCrlfAfterChunk)
|
||||
{
|
||||
if (responseData.size() < 2) break;
|
||||
|
||||
if (!std::equal(crlf.begin(), crlf.end(), responseData.begin()))
|
||||
throw ResponseError("Invalid chunk");
|
||||
|
||||
removeCrlfAfterChunk = false;
|
||||
responseData.erase(responseData.begin(), responseData.begin() + 2);
|
||||
}
|
||||
|
||||
const auto i = std::search(responseData.begin(), responseData.end(), crlf.begin(), crlf.end());
|
||||
|
||||
if (i == responseData.end()) break;
|
||||
|
||||
const std::string line(responseData.begin(), i);
|
||||
responseData.erase(responseData.begin(), i + 2);
|
||||
|
||||
expectedChunkSize = std::stoul(line, nullptr, 16);
|
||||
if (expectedChunkSize == 0)
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response.body.insert(response.body.end(), responseData.begin(), responseData.end());
|
||||
responseData.clear();
|
||||
|
||||
// got the whole content
|
||||
if (contentLengthReceived && response.body.size() >= contentLength)
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::int64_t getRemainingMilliseconds(const std::chrono::steady_clock::time_point time)
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const auto remainingTime = std::chrono::duration_cast<std::chrono::milliseconds>(time - now);
|
||||
return (remainingTime.count() > 0) ? remainingTime.count() : 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
WinSock winSock;
|
||||
#endif // _WIN32
|
||||
InternetProtocol internetProtocol;
|
||||
std::string scheme;
|
||||
std::string domain;
|
||||
std::string port;
|
||||
std::string path;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTPREQUEST_HPP
|
@ -1,92 +0,0 @@
|
||||
#pragma once
|
||||
#include "api/http_request.hpp"
|
||||
|
||||
namespace big::remote
|
||||
{
|
||||
inline std::string get_etag_from_headers(std::vector<std::string> headers)
|
||||
{
|
||||
std::string remote_etag = "";
|
||||
|
||||
for (auto& header : headers)
|
||||
{
|
||||
if (header.rfind("ETag", 0) == 0)
|
||||
{
|
||||
remote_etag = header;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return remote_etag;
|
||||
}
|
||||
|
||||
|
||||
inline bool update_binary(const std::string_view file_url, const std::filesystem::path& file_location, const std::filesystem::path& etag_location)
|
||||
{
|
||||
std::string local_etag = "";
|
||||
std::string remote_etag = "";
|
||||
|
||||
try {
|
||||
|
||||
{
|
||||
std::ifstream file_etag_ifstream(etag_location, std::ios::binary);
|
||||
std::stringstream file_etag_stringstream;
|
||||
file_etag_stringstream << file_etag_ifstream.rdbuf();
|
||||
local_etag = file_etag_stringstream.str();
|
||||
}
|
||||
|
||||
if (!local_etag.empty())
|
||||
{
|
||||
http::Request req(file_url.data());
|
||||
http::Response res = req.send("HEAD", "", {}, 20s);
|
||||
|
||||
if (res.status == http::Response::Status::Ok)
|
||||
{
|
||||
remote_etag = get_etag_from_headers(res.headers);
|
||||
|
||||
if (remote_etag == local_etag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::exception(std::to_string(res.status).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << "Update Error (HEAD): " << e.what();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
http::Request req(file_url.data());
|
||||
http::Response res = req.send("GET", "", {}, 20s);
|
||||
|
||||
if (res.status == http::Response::Status::Ok)
|
||||
{
|
||||
std::ofstream file_ofstream(file_location, std::ios::binary | std::ios::trunc);
|
||||
std::ostream_iterator<std::uint8_t> file_out_iter(file_ofstream);
|
||||
std::copy(res.body.begin(), res.body.end(), file_out_iter);
|
||||
|
||||
remote_etag = get_etag_from_headers(res.headers);
|
||||
|
||||
std::ofstream file_etag_ofstream(etag_location, std::ios::binary | std::ios::trunc);
|
||||
file_etag_ofstream << remote_etag;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::exception(std::to_string(res.status).c_str());
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG(WARNING) << "Update Error (GET): " << e.what();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
#include "backend.hpp"
|
||||
#include "script.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "api/api.hpp"
|
||||
#include "looped/looped.hpp"
|
||||
#include "services/context_menu/context_menu_service.hpp"
|
||||
|
||||
@ -15,13 +14,6 @@ namespace big
|
||||
looped::system_update_pointers();
|
||||
looped::system_desync_kick_protection();
|
||||
|
||||
if (g_local_player != nullptr && !api::util::signed_in())
|
||||
{
|
||||
g_thread_pool->push([]
|
||||
{
|
||||
looped::api_login_session();
|
||||
});
|
||||
}
|
||||
script::get_current()->yield();
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
#include "api/api.hpp"
|
||||
#include "backend/looped/looped.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void looped::api_login_session()
|
||||
{
|
||||
if (g_local_player == nullptr || api::util::signed_in())
|
||||
return;
|
||||
|
||||
api::auth::create_session();
|
||||
}
|
||||
}
|
@ -17,6 +17,18 @@ namespace big::gta_util
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline CVehicle* get_local_vehicle()
|
||||
{
|
||||
if (const auto ped = get_local_ped(); ped)
|
||||
{
|
||||
if (const auto veh = ped->m_vehicle; veh)
|
||||
{
|
||||
return veh;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline CPlayerInfo *get_local_playerinfo()
|
||||
{
|
||||
if (auto ped_factory = *g_pointers->m_ped_factory)
|
||||
|
@ -13,10 +13,12 @@ namespace big
|
||||
ImGui::SliderFloat("##Mass", &g_local_player->m_vehicle->m_handling_data->m_mass, 0.f, 50000.f);
|
||||
|
||||
ImGui::Text("Centre of mass");
|
||||
float fCenterOfMass[3];
|
||||
fCenterOfMass[0] = g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.x;
|
||||
fCenterOfMass[1] = g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.y;
|
||||
fCenterOfMass[2] = g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.z;
|
||||
float fCenterOfMass[3]
|
||||
{
|
||||
g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.x,
|
||||
g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.y,
|
||||
g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.z
|
||||
};
|
||||
if (ImGui::SliderFloat3("##centre_of_mass", fCenterOfMass, -10.f, 10.f))
|
||||
{
|
||||
g_local_player->m_vehicle->m_handling_data->m_centre_of_mass.x = fCenterOfMass[0];
|
||||
|
@ -21,6 +21,5 @@ namespace big
|
||||
namespace modal_handling
|
||||
{
|
||||
void modal_save_handling();
|
||||
void modal_update_handling();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "current_profile/current_profile_tabs.hpp"
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "services/vehicle/handling_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
@ -11,27 +11,17 @@ namespace big
|
||||
ImGui::Text("Please enter a vehicle.");
|
||||
return;
|
||||
}
|
||||
g_handling_service->backup_vehicle();
|
||||
|
||||
if (g_vehicle_service->get_active_profile(g_local_player->m_vehicle->m_handling_data->m_model_hash).empty())
|
||||
if (components::button("Save Profile"))
|
||||
{
|
||||
if (components::button("Save Profile"))
|
||||
{
|
||||
ImGui::OpenPopup("Save Handling");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (components::button("Update Profile"))
|
||||
{
|
||||
ImGui::OpenPopup("Update Handling");
|
||||
}
|
||||
ImGui::OpenPopup("Save Handling");
|
||||
}
|
||||
|
||||
modal_handling::modal_save_handling();
|
||||
modal_handling::modal_update_handling();
|
||||
ImGui::SameLine();
|
||||
if (components::button("Restore Handling"))
|
||||
g_vehicle_service->restore_vehicle();
|
||||
g_handling_service->restore_vehicle();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void view::handling_my_profiles()
|
||||
{
|
||||
if (g_local_player == nullptr || g_local_player->m_vehicle == nullptr || g_local_player->m_ped_task_flag & (int)ePedTask::TASK_FOOT)
|
||||
{
|
||||
ImGui::Text("Please enter a vehicle.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_vehicle_service->update_mine())
|
||||
ImGui::Text("Loading profiles...");
|
||||
else
|
||||
{
|
||||
if (g_vehicle_service->m_my_profiles.size() == 0)
|
||||
ImGui::Text("You have no profiles available for this vehicle.");
|
||||
for (auto& key : g_vehicle_service->m_my_profiles)
|
||||
{
|
||||
if (auto it = g_vehicle_service->m_handling_profiles.find(key); it != g_vehicle_service->m_handling_profiles.end())
|
||||
{
|
||||
auto& profile = it->second;
|
||||
|
||||
if (profile.share_code == g_vehicle_service->get_active_profile(profile.handling_hash))
|
||||
ImGui::TextColored({ 0.1254f,0.8039f,0.3137f,1.f }, "Active");
|
||||
|
||||
ImGui::BeginTable("table", 3, ImGuiTableFlags_SizingStretchProp);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Name:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(profile.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Share Code: %s", profile.share_code.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Description:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped(profile.description.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
if (components::button("Load Profile"))
|
||||
g_vehicle_service->set_handling_profile(profile);
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "services/vehicle/handling_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
@ -10,48 +10,24 @@ namespace big
|
||||
ImGui::Text("Please enter a vehicle.");
|
||||
return;
|
||||
}
|
||||
g_handling_service->backup_vehicle();
|
||||
|
||||
if (!g_vehicle_service->load_saved_profiles())
|
||||
ImGui::Text("Loading profiles...");
|
||||
else
|
||||
if (components::button("Reload Profiles"))
|
||||
{
|
||||
if (g_vehicle_service->m_saved_profiles.size() == 0)
|
||||
ImGui::Text("You have no saved profiles available for this vehicle.");
|
||||
for (auto& key : g_vehicle_service->m_saved_profiles)
|
||||
g_handling_service->load_files();
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::ListBoxHeader("##handling_profiles"))
|
||||
{
|
||||
for (const auto& [name, profile] : g_handling_service->profiles())
|
||||
{
|
||||
if (auto it = g_vehicle_service->m_handling_profiles.find(key); it != g_vehicle_service->m_handling_profiles.end())
|
||||
if (components::selectable(name, profile.get() == g_handling_service->active_profile()))
|
||||
{
|
||||
auto& profile = it->second;
|
||||
|
||||
if (profile.share_code == g_vehicle_service->get_active_profile(profile.handling_hash))
|
||||
ImGui::TextColored({ 0.1254f,0.8039f,0.3137f,1.f }, "Active");
|
||||
|
||||
ImGui::BeginTable("table", 3, ImGuiTableFlags_SizingStretchProp);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Name:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(profile.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Share Code: %s", profile.share_code.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Description:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped(profile.description.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
if (components::button("Load Profile"))
|
||||
g_vehicle_service->set_handling_profile(profile);
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::Separator();
|
||||
g_handling_service->apply_profile(profile.get());
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
#include "api/api.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "script.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void view::handling_search()
|
||||
{
|
||||
if (g_local_player == nullptr || g_local_player->m_vehicle == nullptr || g_local_player->m_ped_task_flag & (int)ePedTask::TASK_FOOT)
|
||||
{
|
||||
ImGui::Text("Please enter a vehicle.");
|
||||
return;
|
||||
}
|
||||
|
||||
static char search[13];
|
||||
components::input_text_with_hint("##search_share_code", "Search by share code", search, sizeof(search), ImGuiInputTextFlags_EnterReturnsTrue, []
|
||||
{
|
||||
g_thread_pool->push([&] { g_vehicle_service->get_by_share_code(search); });
|
||||
});
|
||||
ImGui::SameLine();
|
||||
if (components::button("Search"))
|
||||
g_thread_pool->push([&] { g_vehicle_service->get_by_share_code(search); });
|
||||
|
||||
switch (g_vehicle_service->m_search_status)
|
||||
{
|
||||
case SearchStatus::SEARCHING:
|
||||
ImGui::Text("Searching...");
|
||||
|
||||
break;
|
||||
case SearchStatus::NO_RESULT:
|
||||
ImGui::Text("No results found for %s", search);
|
||||
|
||||
break;
|
||||
case SearchStatus::FAILED:
|
||||
ImGui::Text("Search failed, host is down or response body is invalid...");
|
||||
|
||||
break;
|
||||
case SearchStatus::FOUND:
|
||||
if (auto it = g_vehicle_service->m_handling_profiles.find(search); it != g_vehicle_service->m_handling_profiles.end())
|
||||
{
|
||||
auto& profile = it->second;
|
||||
|
||||
if (profile.share_code == g_vehicle_service->get_active_profile(profile.handling_hash))
|
||||
ImGui::TextColored({ 0.1254f,0.8039f,0.3137f,1.f }, "Active");
|
||||
|
||||
ImGui::BeginTable("table", 3, ImGuiTableFlags_SizingStretchProp);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Name:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(profile.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Share Code: %s", profile.share_code.c_str());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Description:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped(profile.description.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
if (components::button("Load Profile"))
|
||||
g_vehicle_service->set_handling_profile(profile);
|
||||
ImGui::SameLine();
|
||||
if (components::button("Save Profile"))
|
||||
{
|
||||
g_thread_pool->push([&]
|
||||
{
|
||||
api::vehicle::handling::save_profile(profile.share_code);
|
||||
|
||||
g_vehicle_service->load_saved_profiles(true);
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
#include "../current_profile/current_profile_tabs.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "script.hpp"
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "services/vehicle/handling_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
@ -13,53 +12,13 @@ namespace big
|
||||
ImGui::SetNextWindowSize({ 520, 325 }, ImGuiCond_FirstUseEver);
|
||||
if (ImGui::BeginPopupModal("Save Handling"))
|
||||
{
|
||||
static char name[32], description[256] = "No description.";
|
||||
static char name[32];
|
||||
|
||||
switch (g_vehicle_service->publish_status())
|
||||
{
|
||||
case PublishStatus::SAVING:
|
||||
ImGui::Text("Saving...");
|
||||
|
||||
return ImGui::EndPopup();
|
||||
case PublishStatus::SAVED:
|
||||
strcpy(name, "");
|
||||
strcpy(description, "No description.");
|
||||
|
||||
g_vehicle_service->publish_status(PublishStatus::IDLE);
|
||||
g_vehicle_service->update_mine(true);
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
return ImGui::EndPopup();
|
||||
case PublishStatus::FAILED:
|
||||
ImGui::TextColored({ 255, 0, 0, 1 }, "Failed to save handling profile.");
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
QUEUE_JOB_BEGIN_CLAUSE()
|
||||
{
|
||||
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
|
||||
}QUEUE_JOB_END_CLAUSE
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::Text("Name:");
|
||||
ImGui::Text("Description:");
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::InputText("##modal_handling_name", name, sizeof(name));
|
||||
ImGui::InputTextMultiline("##modal_handling_description", description, sizeof(description));
|
||||
|
||||
ImGui::EndGroup();
|
||||
components::input_text_with_hint("Name", "Name", name, sizeof(name));
|
||||
|
||||
if (components::button("Cancel"))
|
||||
{
|
||||
strcpy(name, "");
|
||||
strcpy(description, "No description.");
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
@ -68,8 +27,9 @@ namespace big
|
||||
{
|
||||
g_thread_pool->push([&]
|
||||
{
|
||||
g_vehicle_service->publish_profile(name, description);
|
||||
g_handling_service->save_profile(name);
|
||||
});
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
|
@ -1,93 +0,0 @@
|
||||
#include "../current_profile/current_profile_tabs.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "script.hpp"
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
void modal_handling::modal_update_handling()
|
||||
{
|
||||
ImGui::SetNextWindowSize({ 520, 325 }, ImGuiCond_FirstUseEver);
|
||||
if (ImGui::BeginPopupModal("Update Handling"))
|
||||
{
|
||||
auto share_code = g_vehicle_service->get_active_profile(g_local_player->m_vehicle->m_handling_data->m_model_hash);
|
||||
if (share_code.empty())
|
||||
{
|
||||
// should be impossible to trigger but still
|
||||
|
||||
return ImGui::EndPopup();
|
||||
}
|
||||
auto it = g_vehicle_service->m_handling_profiles.find(share_code);
|
||||
if (it == g_vehicle_service->m_handling_profiles.end())
|
||||
return ImGui::EndPopup();
|
||||
auto profile = it->second;
|
||||
|
||||
static char name[32], description[256] = "No description.";
|
||||
|
||||
strcpy(name, profile.name.c_str());
|
||||
strcpy(description, profile.description.c_str());
|
||||
|
||||
switch (g_vehicle_service->publish_status())
|
||||
{
|
||||
case PublishStatus::SAVING:
|
||||
ImGui::Text("Saving...");
|
||||
|
||||
return ImGui::EndPopup();
|
||||
case PublishStatus::SAVED:
|
||||
strcpy(name, "");
|
||||
strcpy(description, "No description.");
|
||||
|
||||
g_vehicle_service->publish_status(PublishStatus::IDLE);
|
||||
g_vehicle_service->update_mine(true);
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
return ImGui::EndPopup();
|
||||
case PublishStatus::FAILED:
|
||||
ImGui::TextColored({ 255, 0, 0, 1 }, "Failed to update handling profile.");
|
||||
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
QUEUE_JOB_BEGIN_CLAUSE()
|
||||
{
|
||||
PAD::DISABLE_ALL_CONTROL_ACTIONS(0);
|
||||
}QUEUE_JOB_END_CLAUSE
|
||||
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::Text("Name:");
|
||||
ImGui::Text("Description:");
|
||||
|
||||
ImGui::EndGroup();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::InputText("##modal_handling_name", name, sizeof(name));
|
||||
ImGui::InputTextMultiline("##modal_handling_description", description, sizeof(description));
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
if (components::button("Cancel"))
|
||||
{
|
||||
strcpy(name, "");
|
||||
strcpy(description, "No description.");
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (components::button("Update"))
|
||||
{
|
||||
g_thread_pool->push([&]
|
||||
{
|
||||
g_vehicle_service->publish_profile(name, description, profile.share_code);
|
||||
});
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "services/notifications/notification_service.hpp"
|
||||
#include "services/model_preview/model_preview_service.hpp"
|
||||
#include "services/vehicle/vehicle_service.hpp"
|
||||
#include "services/vehicle/handling_service.hpp"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
{
|
||||
@ -82,7 +82,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
auto player_service_instance = std::make_unique<player_service>();
|
||||
auto gta_data_service_instance = std::make_unique<gta_data_service>();
|
||||
auto model_preview_service_instance = std::make_unique<model_preview_service>();
|
||||
auto vehicle_service_instance = std::make_unique<vehicle_service>();
|
||||
auto handling_service_instance = std::make_unique<handling_service>();
|
||||
auto gui_service_instance = std::make_unique<gui_service>();
|
||||
LOG(INFO) << "Registered service instances...";
|
||||
|
||||
@ -135,7 +135,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID)
|
||||
LOG(INFO) << "Gui Service reset.";
|
||||
gta_data_service_instance.reset();
|
||||
LOG(INFO) << "GTA Data Service reset.";
|
||||
vehicle_service_instance.reset();
|
||||
handling_service_instance.reset();
|
||||
LOG(INFO) << "Vehicle Service reset.";
|
||||
model_preview_service_instance.reset();
|
||||
LOG(INFO) << "Model Preview Service reset.";
|
||||
|
@ -63,9 +63,7 @@ namespace big
|
||||
{tabs::VEHICLE, { "Vehicle", view::vehicle, {
|
||||
{ tabs::HANDLING, {"Handling", view::handling_current_profile, {
|
||||
{ tabs::HANDLING_CURRENT_PROFILE, {"Current Profile", view::handling_current_profile } },
|
||||
{ tabs::HANDLING_MY_PROFILES, {"My Profiles", view::handling_my_profiles } },
|
||||
{ tabs::HANDLING_SAVED_PROFILE, {"Saved Profiles", view::handling_saved_profiles } },
|
||||
{ tabs::HANDLING_SEARCH, {"Search Handling", view::handling_search } },
|
||||
}}},
|
||||
{ tabs::LSC, { "LS Customs", view::lsc }},
|
||||
{ tabs::SPAWN_VEHICLE, { "Spawn Vehicle", view::spawn_vehicle }},
|
||||
|
21
BigBaseV2/src/services/vehicle/handling_profile.cpp
Normal file
21
BigBaseV2/src/services/vehicle/handling_profile.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "handling_profile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
handling_profile::handling_profile(CVehicle* vehicle)
|
||||
{
|
||||
m_gravity = vehicle->m_gravity;
|
||||
m_handling_data = *vehicle->m_handling_data;
|
||||
}
|
||||
|
||||
void handling_profile::apply_to(CVehicle* vehicle, bool restore_hash) const
|
||||
{
|
||||
const auto hash = vehicle->m_handling_data->m_model_hash;
|
||||
|
||||
vehicle->m_gravity = m_gravity;
|
||||
*vehicle->m_handling_data = m_handling_data;
|
||||
|
||||
if (restore_hash)
|
||||
vehicle->m_handling_data->m_model_hash = hash;
|
||||
}
|
||||
}
|
19
BigBaseV2/src/services/vehicle/handling_profile.hpp
Normal file
19
BigBaseV2/src/services/vehicle/handling_profile.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "vehicle/CVehicle.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
class handling_profile final
|
||||
{
|
||||
public:
|
||||
handling_profile(CVehicle* vehicle);
|
||||
handling_profile() = default;
|
||||
|
||||
void apply_to(CVehicle* vehicle, bool restore_hash = true) const;
|
||||
|
||||
private:
|
||||
float m_gravity;
|
||||
CHandlingData m_handling_data;
|
||||
|
||||
};
|
||||
}
|
108
BigBaseV2/src/services/vehicle/handling_service.cpp
Normal file
108
BigBaseV2/src/services/vehicle/handling_service.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "gta_util.hpp"
|
||||
#include "handling_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
handling_service::handling_service() :
|
||||
m_profiles_folder(g_file_manager->get_project_folder("./handling_profiles"))
|
||||
{
|
||||
g_handling_service = this;
|
||||
|
||||
load_files();
|
||||
}
|
||||
|
||||
handling_service::~handling_service()
|
||||
{
|
||||
g_handling_service = nullptr;
|
||||
}
|
||||
|
||||
std::size_t handling_service::load_files()
|
||||
{
|
||||
std::size_t files_loaded{};
|
||||
|
||||
for (const auto& item : std::filesystem::directory_iterator(m_profiles_folder.get_path()))
|
||||
{
|
||||
if (!item.is_regular_file())
|
||||
continue;
|
||||
if (auto file_path = item.path(); file_path.extension() == ".bin")
|
||||
{
|
||||
auto profile = std::make_unique<handling_profile>();
|
||||
auto profile_file = std::ifstream(file_path, std::ios::binary);
|
||||
profile_file.read(reinterpret_cast<char*>(profile.get()), sizeof(handling_profile));
|
||||
profile_file.close();
|
||||
|
||||
m_handling_profiles.emplace(file_path.stem().string(), std::move(profile));
|
||||
|
||||
++files_loaded;
|
||||
}
|
||||
}
|
||||
|
||||
return files_loaded;
|
||||
}
|
||||
|
||||
handling_profiles& handling_service::profiles()
|
||||
{
|
||||
return m_handling_profiles;
|
||||
}
|
||||
|
||||
handling_profile* handling_service::active_profile()
|
||||
{
|
||||
return m_active_profile;
|
||||
}
|
||||
|
||||
void handling_service::apply_profile(handling_profile* profile)
|
||||
{
|
||||
if (const auto vehicle = gta_util::get_local_vehicle(); vehicle)
|
||||
{
|
||||
profile->apply_to(vehicle);
|
||||
m_active_profile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
bool handling_service::save_profile(std::string name)
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
|
||||
name += ".bin";
|
||||
const auto save = m_profiles_folder.get_file(name);
|
||||
|
||||
auto profile = std::make_unique<handling_profile>(vehicle);
|
||||
|
||||
auto save_file = std::ofstream(save.get_path(), std::ios::binary);
|
||||
save_file.write(reinterpret_cast<const char*>(profile.get()), sizeof(handling_profile));
|
||||
save_file.close();
|
||||
|
||||
m_handling_profiles.emplace(name, std::move(profile));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handling_service::backup_vehicle()
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
|
||||
const auto hash = vehicle->m_handling_data->m_model_hash;
|
||||
if (const auto& it = m_vehicle_backups.find(hash); it != m_vehicle_backups.end())
|
||||
return false;
|
||||
|
||||
m_vehicle_backups.emplace(hash, std::make_unique<handling_profile>(vehicle));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void handling_service::restore_vehicle() const
|
||||
{
|
||||
const auto vehicle = gta_util::get_local_vehicle();
|
||||
if (!vehicle)
|
||||
return;
|
||||
|
||||
if (const auto& it = m_vehicle_backups.find(vehicle->m_handling_data->m_model_hash); it != m_vehicle_backups.end())
|
||||
{
|
||||
it->second->apply_to(vehicle);
|
||||
}
|
||||
}
|
||||
}
|
40
BigBaseV2/src/services/vehicle/handling_service.hpp
Normal file
40
BigBaseV2/src/services/vehicle/handling_service.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "handling_profile.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
using handling_profiles = std::map<std::string, std::unique_ptr<handling_profile>>;
|
||||
class handling_service final
|
||||
{
|
||||
public:
|
||||
handling_service();
|
||||
~handling_service();
|
||||
|
||||
handling_service(const handling_service&) = delete;
|
||||
handling_service(handling_service&&) noexcept = delete;
|
||||
handling_service& operator=(const handling_service&) = delete;
|
||||
handling_service& operator=(handling_service&&) noexcept = delete;
|
||||
|
||||
std::size_t load_files();
|
||||
handling_profiles& profiles();
|
||||
|
||||
handling_profile* active_profile();
|
||||
void apply_profile(handling_profile* profile);
|
||||
bool save_profile(std::string name);
|
||||
|
||||
bool backup_vehicle();
|
||||
void restore_vehicle() const;
|
||||
|
||||
private:
|
||||
const folder m_profiles_folder;
|
||||
|
||||
handling_profile* m_active_profile;
|
||||
handling_profiles m_handling_profiles;
|
||||
|
||||
// contains the handling profiles of a vehicles before they're been modified
|
||||
std::unordered_map<std::uint32_t, std::unique_ptr<handling_profile>> m_vehicle_backups;
|
||||
|
||||
};
|
||||
|
||||
inline handling_service* g_handling_service{};
|
||||
}
|
@ -1,307 +0,0 @@
|
||||
#include "api/api.hpp"
|
||||
#include "thread_pool.hpp"
|
||||
#include "vehicle_service.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
bool safe_to_modify()
|
||||
{
|
||||
return !(g_local_player == nullptr || g_local_player->m_vehicle == nullptr);
|
||||
}
|
||||
|
||||
vehicle_service::vehicle_service()
|
||||
{
|
||||
g_vehicle_service = this;
|
||||
}
|
||||
|
||||
vehicle_service::~vehicle_service()
|
||||
{
|
||||
g_vehicle_service = nullptr;
|
||||
}
|
||||
|
||||
int vehicle_service::attempt_save()
|
||||
{
|
||||
if (g_local_player == nullptr || g_local_player->m_ped_task_flag & (int)ePedTask::TASK_FOOT || g_local_player->m_vehicle == nullptr)
|
||||
return -1;
|
||||
if (m_handling_backup.find(g_local_player->m_vehicle->m_handling_data->m_model_hash) != m_handling_backup.end())
|
||||
return 0;
|
||||
|
||||
CHandlingData handling = *g_local_player->m_vehicle->m_handling_data;
|
||||
|
||||
m_handling_backup.emplace(g_local_player->m_vehicle->m_handling_data->m_model_hash, handling);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string vehicle_service::get_active_profile(std::uint32_t hash)
|
||||
{
|
||||
if (auto it = this->m_active_profiles.find(hash); it != this->m_active_profiles.end())
|
||||
return it->second;
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
bool vehicle_service::get_by_share_code(const char* share_code)
|
||||
{
|
||||
static std::string up_to_date = "";
|
||||
|
||||
if (m_search_status == SearchStatus::SEARCHING)
|
||||
return false;
|
||||
|
||||
if (!safe_to_modify())
|
||||
return false;
|
||||
|
||||
if (up_to_date == share_code)
|
||||
return true;
|
||||
m_search_status = SearchStatus::SEARCHING;
|
||||
|
||||
nlohmann::json json;
|
||||
if (api::vehicle::handling::get_by_share_code(std::string(share_code), json))
|
||||
{
|
||||
if (json["data"].is_null())
|
||||
m_search_status = SearchStatus::NO_RESULT;
|
||||
else
|
||||
{
|
||||
auto& data = json["data"];
|
||||
|
||||
HandlingProfile profile = HandlingProfile(data, g_local_player->m_vehicle->m_handling_data);
|
||||
|
||||
if (auto it = m_handling_profiles.find(data["share_code"]); it != m_handling_profiles.end())
|
||||
it->second = profile;
|
||||
else m_handling_profiles.emplace(data["share_code"], profile);
|
||||
|
||||
up_to_date = data["share_code"];
|
||||
m_search_status = SearchStatus::FOUND;
|
||||
}
|
||||
}
|
||||
else m_search_status = SearchStatus::FAILED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vehicle_service::handling_data_to_json(CHandlingData& handling_data, nlohmann::json& out)
|
||||
{
|
||||
out = {
|
||||
{ "centre_of_mass",
|
||||
{
|
||||
{ "x", handling_data.m_centre_of_mass.x },
|
||||
{ "y", handling_data.m_centre_of_mass.y },
|
||||
{ "z", handling_data.m_centre_of_mass.z }
|
||||
}
|
||||
},
|
||||
{ "inertia_mult",
|
||||
{
|
||||
{ "x", handling_data.m_inertia_mult.x },
|
||||
{ "y", handling_data.m_inertia_mult.y },
|
||||
{ "z", handling_data.m_inertia_mult.z }
|
||||
}
|
||||
},
|
||||
{ "mass", handling_data.m_mass },
|
||||
{ "downforce_mult", handling_data.m_downforce_multiplier },
|
||||
{ "buoyancy", handling_data.m_buoyancy },
|
||||
{ "drive_bias_rear", handling_data.m_drive_bias_rear },
|
||||
{ "drive_bias_front", handling_data.m_drive_bias_front },
|
||||
{ "acceleration_mult", handling_data.m_acceleration },
|
||||
{ "initial_drive_gears", handling_data.m_initial_drive_gears },
|
||||
{ "upshift", handling_data.m_upshift },
|
||||
{ "downshift", handling_data.m_downshift },
|
||||
{ "drive_inertia", handling_data.m_drive_inertia },
|
||||
{ "initial_drive_force", handling_data.m_initial_drive_force },
|
||||
{ "drive_max_flat_vel", handling_data.m_drive_max_flat_velocity },
|
||||
{ "brake_force", handling_data.m_brake_force },
|
||||
{ "brake_bias_front", handling_data.m_brake_bias_front },
|
||||
{ "brake_bias_rear", handling_data.m_brake_bias_rear },
|
||||
{ "handbrake_force", handling_data.m_handbrake_force },
|
||||
{ "steering_lock", handling_data.m_steering_lock },
|
||||
{ "steering_lock_ratio", handling_data.m_steering_lock_ratio },
|
||||
{ "traction_curve_max", handling_data.m_traction_curve_max },
|
||||
{ "traction_curve_lateral", handling_data.m_traction_curve_lateral },
|
||||
{ "traction_curve_min", handling_data.m_traction_curve_min },
|
||||
{ "traction_curve_ratio", handling_data.m_traction_curve_ratio },
|
||||
{ "traction_bias_front", handling_data.m_traction_bias_front },
|
||||
{ "traction_bias_rear", handling_data.m_traction_bias_rear },
|
||||
{ "traction_loss_mult", handling_data.m_traction_loss_mult },
|
||||
{ "curve_lateral", handling_data.m_curve_lateral },
|
||||
{ "curve_lateral_ratio", handling_data.m_curve_lateral_ratio },
|
||||
{ "traction_spring_delta_max", handling_data.m_traction_spring_delta_max },
|
||||
{ "traction_spring_delta_max_ratio", handling_data.m_traction_spring_delta_max_ratio },
|
||||
{ "low_speed_traction_loss_mult", handling_data.m_low_speed_traction_loss_mult },
|
||||
{ "camber_stiffness", handling_data.m_camber_stiffness },
|
||||
{ "suspension_force", handling_data.m_suspension_force },
|
||||
{ "suspension_comp_damp", handling_data.m_suspension_comp_damp },
|
||||
{ "suspension_rebound_damp", handling_data.m_suspension_rebound_damp },
|
||||
{ "suspension_upper_limit", handling_data.m_suspension_upper_limit },
|
||||
{ "suspension_lower_limit", handling_data.m_suspension_lower_limit },
|
||||
{ "suspension_raise", handling_data.m_suspension_raise },
|
||||
{ "suspension_bias_front", handling_data.m_suspension_bias_front },
|
||||
{ "suspension_bias_rear", handling_data.m_suspension_bias_rear },
|
||||
{ "anti_rollbar_force", handling_data.m_anti_rollbar_force },
|
||||
{ "anti_rollbar_bias_front", handling_data.m_anti_rollbar_bias_front },
|
||||
{ "anti_rollbar_bias_rear", handling_data.m_anti_rollbar_bias_rear },
|
||||
{ "roll_centre_height_front", handling_data.m_roll_centre_height_front },
|
||||
{ "roll_centre_height_rear", handling_data.m_roll_centre_height_rear }
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vehicle_service::load_saved_profiles(bool force_update)
|
||||
{
|
||||
static bool busy = false;
|
||||
static uint32_t up_to_date = -1;
|
||||
|
||||
if (busy)
|
||||
return false;
|
||||
|
||||
if (!safe_to_modify())
|
||||
return false;
|
||||
|
||||
if (!force_update && up_to_date == g_local_player->m_vehicle->m_handling_data->m_model_hash)
|
||||
return true;
|
||||
|
||||
busy = true;
|
||||
|
||||
g_thread_pool->push([&]()
|
||||
{
|
||||
nlohmann::json json;
|
||||
if (!api::vehicle::handling::get_saved_handling(g_local_player->m_vehicle->m_handling_data->m_model_hash, json) || json == nullptr)
|
||||
{
|
||||
busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this->m_saved_profiles.clear();
|
||||
for (auto& el : json["data"])
|
||||
{
|
||||
LOG(INFO) << "Registered profile '" << el["name"].get<std::string>().c_str() << "' with share code " << el["share_code"].get<std::string>().c_str();
|
||||
|
||||
HandlingProfile profile = HandlingProfile(el, g_local_player->m_vehicle->m_handling_data);
|
||||
|
||||
if (auto it = this->m_handling_profiles.find(el["share_code"]); it != this->m_handling_profiles.end())
|
||||
it->second = profile;
|
||||
else this->m_handling_profiles.emplace(el["share_code"], profile);
|
||||
this->m_saved_profiles.push_back(el["share_code"]);
|
||||
}
|
||||
|
||||
busy = false;
|
||||
up_to_date = g_local_player->m_vehicle->m_handling_data->m_model_hash;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vehicle_service::publish_profile(const char* name, const char* description, std::string share_code)
|
||||
{
|
||||
if (this->m_publish_status == PublishStatus::SAVED)
|
||||
return true;
|
||||
if (this->m_publish_status == PublishStatus::SAVING)
|
||||
return false;
|
||||
|
||||
if (!safe_to_modify()) return false;
|
||||
|
||||
this->m_publish_status = PublishStatus::SAVING;
|
||||
|
||||
CHandlingData handling_data = *g_local_player->m_vehicle->m_handling_data;
|
||||
uint32_t hash = handling_data.m_model_hash;
|
||||
|
||||
nlohmann::json data;
|
||||
this->handling_data_to_json(handling_data, data);
|
||||
|
||||
nlohmann::json json;
|
||||
if (!share_code.empty() && api::vehicle::handling::update(hash, name, description, share_code, data))
|
||||
m_publish_status = PublishStatus::SAVED;
|
||||
else if (api::vehicle::handling::create_profile(hash, name, description, data, json))
|
||||
{
|
||||
this->set_active_profile(hash, json["data"]["share_code"]);
|
||||
|
||||
m_publish_status = PublishStatus::SAVED;
|
||||
}
|
||||
else m_publish_status = PublishStatus::FAILED;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PublishStatus vehicle_service::publish_status(PublishStatus new_status)
|
||||
{
|
||||
if (new_status != PublishStatus::NONE)
|
||||
this->m_publish_status = new_status;
|
||||
|
||||
return this->m_publish_status;
|
||||
}
|
||||
|
||||
bool vehicle_service::restore_vehicle()
|
||||
{
|
||||
if (auto it = m_handling_backup.find(g_local_player->m_vehicle->m_handling_data->m_model_hash); it != m_handling_backup.end())
|
||||
{
|
||||
if (safe_to_modify())
|
||||
*g_local_player->m_vehicle->m_handling_data = it->second;
|
||||
this->m_active_profiles.erase(g_local_player->m_vehicle->m_handling_data->m_model_hash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void vehicle_service::set_active_profile(std::uint32_t hash, std::string share_code)
|
||||
{
|
||||
if (const auto& it = this->m_active_profiles.find(hash); it != this->m_active_profiles.end())
|
||||
it->second = share_code;
|
||||
else
|
||||
this->m_active_profiles.emplace(hash, share_code);
|
||||
}
|
||||
|
||||
void vehicle_service::set_handling_profile(HandlingProfile& profile)
|
||||
{
|
||||
if (!safe_to_modify())
|
||||
return;
|
||||
*g_local_player->m_vehicle->m_handling_data = profile.data;
|
||||
|
||||
this->set_active_profile(profile.handling_hash, profile.share_code);
|
||||
}
|
||||
|
||||
bool vehicle_service::update_mine(bool force_update)
|
||||
{
|
||||
static bool busy = false;
|
||||
static uint32_t up_to_date = 0;
|
||||
|
||||
if (busy)
|
||||
return false;
|
||||
|
||||
if (!safe_to_modify())
|
||||
return false;
|
||||
|
||||
if (!force_update && up_to_date == g_local_player->m_vehicle->m_handling_data->m_model_hash)
|
||||
return true;
|
||||
|
||||
busy = true;
|
||||
|
||||
g_thread_pool->push([&] {
|
||||
nlohmann::json json;
|
||||
if (!api::vehicle::handling::get_my_handling(g_local_player->m_vehicle->m_handling_data->m_model_hash, json) || json == nullptr)
|
||||
{
|
||||
busy = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this->m_my_profiles.clear();
|
||||
for (auto& el : json["data"])
|
||||
{
|
||||
LOG(INFO) << "Registered profile '" << el["name"].get<std::string>().c_str() << "' with share code " << el["share_code"].get<std::string>().c_str();
|
||||
|
||||
HandlingProfile profile = HandlingProfile(el, g_local_player->m_vehicle->m_handling_data);
|
||||
|
||||
if (auto it = this->m_handling_profiles.find(el["share_code"]); it != this->m_handling_profiles.end())
|
||||
it->second = profile;
|
||||
else this->m_handling_profiles.emplace(el["share_code"], profile);
|
||||
this->m_my_profiles.push_back(el["share_code"]);
|
||||
}
|
||||
|
||||
busy = false;
|
||||
up_to_date = g_local_player->m_vehicle->m_handling_data->m_model_hash;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace big
|
||||
{
|
||||
enum class PublishStatus {
|
||||
NONE = -2,
|
||||
IDLE = -1,
|
||||
SAVING,
|
||||
SAVED,
|
||||
FAILED
|
||||
};
|
||||
|
||||
enum class SearchStatus {
|
||||
NONE = -2,
|
||||
IDLE = -1,
|
||||
SEARCHING,
|
||||
FOUND,
|
||||
NO_RESULT,
|
||||
FAILED
|
||||
};
|
||||
|
||||
class vehicle_service
|
||||
{
|
||||
private:
|
||||
|
||||
class HandlingProfile
|
||||
{
|
||||
public:
|
||||
HandlingProfile(nlohmann::json &in, CHandlingData *handling_data)
|
||||
{
|
||||
this->handling_hash = in["handling_hash"];
|
||||
this->share_code = in["share_code"];
|
||||
|
||||
this->name = in["name"];
|
||||
this->description = in["description"];
|
||||
|
||||
nlohmann::json& data = in["data"];
|
||||
|
||||
// Make sure we copy the values that we don't modify to prevent corrupting the handling
|
||||
this->data = *handling_data;
|
||||
|
||||
this->data.m_centre_of_mass.x = data["centre_of_mass"]["x"];
|
||||
this->data.m_centre_of_mass.y = data["centre_of_mass"]["y"];
|
||||
this->data.m_centre_of_mass.z = data["centre_of_mass"]["z"];
|
||||
|
||||
this->data.m_inertia_mult.x = data["inertia_mult"]["x"];
|
||||
this->data.m_inertia_mult.y = data["inertia_mult"]["y"];
|
||||
this->data.m_inertia_mult.z = data["inertia_mult"]["z"];
|
||||
|
||||
this->data.m_mass = data["mass"];
|
||||
this->data.m_downforce_multiplier = data["downforce_mult"];
|
||||
this->data.m_buoyancy = data["buoyancy"];
|
||||
|
||||
this->data.m_drive_bias_rear = data["drive_bias_rear"];
|
||||
this->data.m_drive_bias_front = data["drive_bias_front"];
|
||||
|
||||
this->data.m_acceleration = data["acceleration_mult"];
|
||||
|
||||
this->data.m_initial_drive_gears = data["initial_drive_gears"];
|
||||
this->data.m_upshift = data["upshift"];
|
||||
this->data.m_downshift = data["downshift"];
|
||||
|
||||
this->data.m_drive_inertia = data["drive_inertia"];
|
||||
|
||||
this->data.m_drive_max_flat_velocity = data["drive_max_flat_vel"];
|
||||
|
||||
this->data.m_brake_force = data["brake_force"];
|
||||
this->data.m_brake_bias_front = data["brake_bias_front"];
|
||||
this->data.m_brake_bias_rear = data["brake_bias_rear"];
|
||||
this->data.m_handbrake_force = data["handbrake_force"];
|
||||
|
||||
this->data.m_steering_lock = data["steering_lock"];
|
||||
this->data.m_steering_lock_ratio = data["steering_lock_ratio"];
|
||||
|
||||
this->data.m_traction_curve_max = data["traction_curve_max"];
|
||||
this->data.m_traction_curve_lateral = data["traction_curve_lateral"];
|
||||
this->data.m_traction_curve_min = data["traction_curve_min"];
|
||||
this->data.m_traction_curve_ratio = data["traction_curve_ratio"];
|
||||
|
||||
this->data.m_curve_lateral = data["curve_lateral"];
|
||||
this->data.m_curve_lateral_ratio = data["curve_lateral_ratio"];
|
||||
|
||||
this->data.m_traction_spring_delta_max = data["traction_spring_delta_max"];
|
||||
this->data.m_traction_spring_delta_max_ratio = data["traction_spring_delta_max_ratio"];
|
||||
|
||||
this->data.m_low_speed_traction_loss_mult = data["low_speed_traction_loss_mult"];
|
||||
this->data.m_camber_stiffness = data["camber_stiffness"];
|
||||
|
||||
this->data.m_suspension_force = data["suspension_force"];
|
||||
this->data.m_suspension_comp_damp = data["suspension_comp_damp"];
|
||||
this->data.m_suspension_rebound_damp = data["suspension_rebound_damp"];
|
||||
this->data.m_suspension_upper_limit = data["suspension_upper_limit"];
|
||||
this->data.m_suspension_lower_limit = data["suspension_lower_limit"];
|
||||
this->data.m_suspension_raise = data["suspension_raise"];
|
||||
this->data.m_suspension_bias_front = data["suspension_bias_front"];
|
||||
this->data.m_suspension_bias_rear = data["suspension_bias_rear"];
|
||||
|
||||
this->data.m_anti_rollbar_force = data["anti_rollbar_force"];
|
||||
this->data.m_anti_rollbar_bias_front = data["anti_rollbar_bias_front"];
|
||||
this->data.m_anti_rollbar_bias_rear = data["anti_rollbar_bias_rear"];
|
||||
|
||||
this->data.m_roll_centre_height_front = data["roll_centre_height_front"];
|
||||
this->data.m_roll_centre_height_rear = data["roll_centre_height_rear"];
|
||||
}
|
||||
|
||||
CHandlingData data;
|
||||
|
||||
uint32_t handling_hash;
|
||||
std::string share_code;
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
public:
|
||||
vehicle_service();
|
||||
~vehicle_service();
|
||||
|
||||
int attempt_save();
|
||||
std::string get_active_profile(std::uint32_t hash);
|
||||
bool get_by_share_code(const char* share_code);
|
||||
bool handling_data_to_json(CHandlingData& handling_data, nlohmann::json& out);
|
||||
bool load_saved_profiles(bool force_update = false);
|
||||
bool publish_profile(const char* name, const char* description, std::string share_code = "");
|
||||
PublishStatus publish_status(PublishStatus new_status = PublishStatus::NONE);
|
||||
bool restore_vehicle();
|
||||
void set_active_profile(std::uint32_t hash, std::string share_code);
|
||||
void set_handling_profile(HandlingProfile& profile);
|
||||
bool update_mine(bool force_update = false);
|
||||
|
||||
inline static std::unordered_map<std::uint32_t, std::string> m_active_profiles;
|
||||
inline static std::vector<std::string> m_my_profiles;
|
||||
inline static std::vector<std::string> m_saved_profiles;
|
||||
inline static std::unordered_map<std::string, HandlingProfile> m_handling_profiles;
|
||||
|
||||
SearchStatus m_search_status = SearchStatus::IDLE;
|
||||
private:
|
||||
PublishStatus m_publish_status = PublishStatus::IDLE;
|
||||
|
||||
inline static std::unordered_map<Hash, CHandlingData> m_handling_backup;
|
||||
};
|
||||
|
||||
inline vehicle_service* g_vehicle_service;
|
||||
}
|
@ -18,9 +18,7 @@ namespace big
|
||||
static void context_menu_settings();
|
||||
static void gui_settings();
|
||||
static void handling_current_profile();
|
||||
static void handling_my_profiles();
|
||||
static void handling_saved_profiles();
|
||||
static void handling_search();
|
||||
static void notification_settings();
|
||||
static void protection_settings();
|
||||
static void heading();
|
||||
|
Reference in New Issue
Block a user