feat(Handling): Added online profile getting

This commit is contained in:
Yimura 2021-08-17 16:56:41 +02:00
parent f22682b607
commit 6f5e12168b
15 changed files with 1463 additions and 28 deletions

125
BigBaseV2/src/api/api.hpp Normal file
View File

@ -0,0 +1,125 @@
#pragma once
#include "http_request.hpp"
namespace big::api
{
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()
{
if (g_local_player == nullptr) return false;
const std::string path = "/auth/create_session";
http::Request request(domain + path);
CPlayerInfo* player_info = g_local_player->m_player_info;
nlohmann::json body = {
{ "username", std::string(player_info->m_name) },
{ "rockstar_id", player_info->m_rockstar_id2 }
};
http::Response res = request.send("POST", body.dump(), {
"Content-Type: application/json"
});
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();
return true;
}
LOG(INFO) << "Failed to create a session.";
return false;
}
}
namespace vehicle
{
namespace handling
{
static bool create_profile(uint32_t handling_hash, const char* name, const char* description, nlohmann::json &handling_data)
{
const std::string path = "/vehicle/handling/create";
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"] = handling_data;
http::Response res = request.send("POST", json.dump(), {
util::authorization_header()
});
return util::parse_body(res, json);
}
static bool get_by_share_code(std::string share_code, nlohmann::json& out)
{
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)
{
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);
}
}
}
}

View File

@ -0,0 +1,761 @@
//
// 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) // disconnected
return response;
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)
{
// Content-Length must be ignored if Transfer-Encoding is received
if (chunkedResponse)
{
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

View File

@ -17,6 +17,11 @@ namespace big
looped::system_update_pointers(); looped::system_update_pointers();
}QUEUE_JOB_END_CLAUSE }QUEUE_JOB_END_CLAUSE
g_fiber_pool->queue_job([]
{
looped::api_login_session();
});
QUEUE_JOB_BEGIN_CLAUSE() QUEUE_JOB_BEGIN_CLAUSE()
{ {
looped::protections_replay_interface(); looped::protections_replay_interface();
@ -59,6 +64,7 @@ namespace big
QUEUE_JOB_BEGIN_CLAUSE() QUEUE_JOB_BEGIN_CLAUSE()
{ {
looped::vehicle_god_mode();
looped::vehicle_horn_boost(); looped::vehicle_horn_boost();
looped::vehicle_speedo_meter(); looped::vehicle_speedo_meter();
}QUEUE_JOB_END_CLAUSE }QUEUE_JOB_END_CLAUSE

View File

@ -0,0 +1,13 @@
#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();
}
}

View File

@ -5,6 +5,8 @@ namespace big
{ {
class looped { class looped {
public: public:
static void api_login_session();
static void tunables_disable_phone(); static void tunables_disable_phone();
static void tunables_no_idle_kick(); static void tunables_no_idle_kick();
@ -30,6 +32,7 @@ namespace big
static void weapons_steal_vehicle_gun(); static void weapons_steal_vehicle_gun();
static void weapons_vehicle_gun(); static void weapons_vehicle_gun();
static void vehicle_god_mode();
static void vehicle_horn_boost(); static void vehicle_horn_boost();
static void vehicle_speedo_meter(); static void vehicle_speedo_meter();
}; };

View File

@ -1,4 +1,5 @@
#include "backend/looped/looped.hpp" #include "backend/looped/looped.hpp"
#include "pointers.hpp"
#include "natives.hpp" #include "natives.hpp"
#include "script_global.hpp" #include "script_global.hpp"
@ -8,6 +9,21 @@ namespace big
{ {
if (g.self.off_radar) if (g.self.off_radar)
{ {
/*Player playerId = PLAYER::PLAYER_ID();
int off_radar[] = {
(int)RemoteEvent::RemoteOffradar,
playerId,
NETWORK::GET_NETWORK_TIME(),
0,
0,
0,
*script_global(1630816).at(playerId, 597).at(508).as<int*>()
};
g_pointers->m_trigger_script_event(1, off_radar, 7, 1 << playerId);*/
//SCRIPT::TRIGGER_SCRIPT_EVENT(1, off_radar, 7, 1 << playerId);
*script_global(2426865).at(PLAYER::PLAYER_ID(), 449).at(209).as<bool*>() = true; *script_global(2426865).at(PLAYER::PLAYER_ID(), 449).at(209).as<bool*>() = true;
*script_global(2441237).at(70).as<int*>() = NETWORK::GET_NETWORK_TIME() + 1; *script_global(2441237).at(70).as<int*>() = NETWORK::GET_NETWORK_TIME() + 1;
} }

View File

@ -9,7 +9,7 @@ namespace big
{ {
if (!g.vehicle.horn_boost) return; if (!g.vehicle.horn_boost) return;
if (PAD::IS_DISABLED_CONTROL_PRESSED(0, (int)ControllerInputs::INPUT_VEH_HORN)) if (PAD::IS_CONTROL_PRESSED(0, (int)ControllerInputs::INPUT_VEH_HORN))
{ {
Vehicle veh = PED::GET_VEHICLE_PED_IS_IN(PLAYER::PLAYER_PED_ID(), false); Vehicle veh = PED::GET_VEHICLE_PED_IS_IN(PLAYER::PLAYER_PED_ID(), false);

View File

@ -5,16 +5,20 @@
class CPlayerInfo class CPlayerInfo
{ {
public: public:
char pad_0000[52]; //0x0000 char pad_0000[40]; //0x0000
uint32_t m_internal_ip; //0x0034 uint64_t m_rockstar_id; //0x0028
uint16_t m_internal_port; //0x0038 char pad_0030[8]; //0x0030
char pad_003A[2]; //0x003A uint32_t m_internal_ip; //0x0038
uint32_t m_relay_ip; //0x003C uint16_t m_internal_port; //0x003C
uint16_t m_relay_port; //0x0040 char pad_003E[2]; //0x003E
char pad_0042[2]; //0x0042 uint32_t m_relay_ip; //0x0040
uint32_t m_external_ip; //0x0044 uint16_t m_relay_port; //0x0044
uint16_t m_external_port; //0x0048 char pad_0046[2]; //0x0046
char pad_004A[90]; //0x004A uint32_t m_external_ip; //0x0048
uint16_t m_external_port; //0x004C
char pad_004E[66]; //0x004E
uint64_t m_rockstar_id2; //0x0090
char pad_0098[12]; //0x0098
char m_name[20]; //0x00A4 char m_name[20]; //0x00A4
char pad_00B8[184]; //0x00B8 char pad_00B8[184]; //0x00B8
float m_swim_speed; //0x0170 float m_swim_speed; //0x0170

View File

@ -6,13 +6,6 @@ namespace big
{ {
if (ImGui::BeginTabItem("General")) if (ImGui::BeginTabItem("General"))
{ {
if (ImGui::Button("Restore Handling Data"))
{
g_vehicle_service->restore_vehicle();
}
ImGui::Separator();
ImGui::Text("Gravity"); ImGui::Text("Gravity");
ImGui::SliderFloat("##Gravity", &g_local_player->m_vehicle->m_gravity, -50.f, 50.f); ImGui::SliderFloat("##Gravity", &g_local_player->m_vehicle->m_gravity, -50.f, 50.f);

View File

@ -18,4 +18,10 @@ namespace big
static void tab_traction(); static void tab_traction();
static void tab_transmission(); static void tab_transmission();
}; };
class modal_handling
{
public:
static void modal_save_handling();
};
} }

View File

@ -0,0 +1,75 @@
#include "../handling_tabs.hpp"
#include "fiber_pool.hpp"
#include "natives.hpp"
#include "script.hpp"
namespace big
{
void modal_handling::modal_save_handling()
{
ImGui::SetNextWindowSize({ 520, 325 }, ImGuiCond_Always);
if (ImGui::BeginPopupModal("Save Handling"))
{
static char name[32], description[256] = "No description.";
switch (g_vehicle_service->publish_status())
{
case PublishStatus::SAVING:
ImGui::Text("Saving...");
return ImGui::EndPopup();
case PublishStatus::SAVED:
strcpy(name, "");
strcpy(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();
if (ImGui::Button("Cancel"))
{
strcpy(name, "");
strcpy(description, "No description.");
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Save"))
{
g_fiber_pool->queue_job([&]
{
g_vehicle_service->publish_profile(name, description);
});
}
ImGui::EndPopup();
}
}
}

View File

@ -1,3 +1,5 @@
//#include "api/api.hpp"
#include "fiber_pool.hpp"
#include "main_tabs.hpp" #include "main_tabs.hpp"
#include "util/system.hpp" #include "util/system.hpp"

View File

@ -1,3 +1,4 @@
#include "fiber_pool.hpp"
#include "gui/window.hpp" #include "gui/window.hpp"
#include "handling/handling_tabs.hpp" #include "handling/handling_tabs.hpp"
#include "imgui.h" #include "imgui.h"
@ -18,16 +19,138 @@ namespace big
} }
g_vehicle_service->attempt_save(); g_vehicle_service->attempt_save();
ImGui::BeginTabBar("handling_tabbar"); ImGui::BeginTabBar("handling_profiles");
tab_handling::tab_general(); if (ImGui::BeginTabItem("Current Profile"))
tab_handling::tab_other(); {
tab_handling::tab_brakes(); if (ImGui::Button("Save Profile"))
tab_handling::tab_gearing(); {
tab_handling::tab_traction(); ImGui::OpenPopup("Save Handling");
tab_handling::tab_steering(); }
tab_handling::tab_suspension();
tab_handling::tab_rollbars(); modal_handling::modal_save_handling();
tab_handling::tab_roll_centre_height(); ImGui::SameLine();
if (ImGui::Button("Restore Handling"))
{
g_vehicle_service->restore_vehicle();
}
ImGui::Separator();
ImGui::BeginTabBar("handling_tabbar");
tab_handling::tab_general();
tab_handling::tab_other();
tab_handling::tab_brakes();
tab_handling::tab_gearing();
tab_handling::tab_traction();
tab_handling::tab_steering();
tab_handling::tab_suspension();
tab_handling::tab_rollbars();
tab_handling::tab_roll_centre_height();
ImGui::EndTabBar();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("My Profiles"))
{
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;
ImGui::BeginGroup();
ImGui::Text("Name:");
ImGui::Text("Description:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text(profile.name.c_str());
ImGui::TextWrapped(profile.description.c_str());
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text("Share Code: %s", profile.share_code.c_str());
if (ImGui::Button("Load Profile"))
{
*g_local_player->m_vehicle->m_handling = profile.data;
}
ImGui::EndGroup();
ImGui::Separator();
}
}
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Search"))
{
static char search[13];
ImGui::InputTextWithHint("##search_share_code", "Search by share code", search, sizeof(search));
ImGui::SameLine();
if (ImGui::Button("Search"))
g_fiber_pool->queue_job([&] { 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;
ImGui::BeginGroup();
ImGui::Text("Name:");
ImGui::Text("Description:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text(profile.name.c_str());
ImGui::TextWrapped(profile.description.c_str());
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text("Share Code: %s", profile.share_code.c_str());
if (ImGui::Button("Load Profile"))
{
*g_local_player->m_vehicle->m_handling = profile.data;
}
ImGui::EndGroup();
}
break;
}
ImGui::EndTabItem();
}
ImGui::EndTabBar(); ImGui::EndTabBar();
ImGui::End(); ImGui::End();

View File

@ -1,3 +1,5 @@
#include "api/api.hpp"
#include "fiber_pool.hpp"
#include "vehicle_service.hpp" #include "vehicle_service.hpp"
namespace big namespace big
@ -12,6 +14,17 @@ namespace big
g_vehicle_service = nullptr; g_vehicle_service = nullptr;
} }
bool vehicle_service::apply_from_cache(std::string id)
{
if (auto it = m_handling_profiles.find(id); it != m_handling_profiles.end())
{
*g_local_player->m_vehicle->m_handling = it->second.data;
return true;
}
return false;
}
int vehicle_service::attempt_save() int vehicle_service::attempt_save()
{ {
if (g_local_player == nullptr || g_local_player->m_in_vehicle == 0x10 || g_local_player->m_vehicle == nullptr) if (g_local_player == nullptr || g_local_player->m_in_vehicle == 0x10 || g_local_player->m_vehicle == nullptr)
@ -26,6 +39,134 @@ namespace big
return 1; return 1;
} }
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 (g_local_player == nullptr || g_local_player->m_vehicle == nullptr)
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);
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::publish_profile(const char* name, const char* description)
{
if (this->m_publish_status == PublishStatus::SAVED)
return true;
if (this->m_publish_status == PublishStatus::SAVING)
return false;
if (g_local_player == nullptr || g_local_player->m_vehicle == nullptr) return false;
this->m_publish_status = PublishStatus::SAVING;
CHandlingData handling_data = *g_local_player->m_vehicle->m_handling;
uint32_t hash = handling_data.m_model_hash;
nlohmann::json data = {
{ "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 }
};
if (api::vehicle::handling::create_profile(hash, name, description, data))
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() bool vehicle_service::restore_vehicle()
{ {
if (auto it = m_handling_backup.find(g_local_player->m_vehicle->m_handling->m_model_hash); it != m_handling_backup.end()) if (auto it = m_handling_backup.find(g_local_player->m_vehicle->m_handling->m_model_hash); it != m_handling_backup.end())
@ -37,4 +178,49 @@ namespace big
return false; return false;
} }
bool vehicle_service::update_mine(bool force_update)
{
static bool busy = false;
static uint32_t up_to_date = 0;
if (busy)
return false;
if (g_local_player == nullptr || g_local_player->m_vehicle == nullptr)
return false;
if (!force_update && up_to_date == g_local_player->m_vehicle->m_handling->m_model_hash)
return true;
busy = true;
g_fiber_pool->queue_job([&] {
nlohmann::json json;
if (!api::vehicle::handling::get_my_handling(g_local_player->m_vehicle->m_handling->m_model_hash, json) || json == nullptr)
{
busy = false;
return;
}
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);
if (auto it = m_handling_profiles.find(el["share_code"]); it != m_handling_profiles.end())
it->second = profile;
else m_handling_profiles.emplace(el["share_code"], profile);
m_my_profiles.push_back(el["share_code"]);
}
busy = false;
up_to_date = g_local_player->m_vehicle->m_handling->m_model_hash;
});
return false;
}
} }

View File

@ -3,15 +3,137 @@
namespace big 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 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: public:
vehicle_service(); vehicle_service();
~vehicle_service(); ~vehicle_service();
bool apply_from_cache(std::string id);
int attempt_save(); int attempt_save();
bool get_by_share_code(const char* share_code);
bool publish_profile(const char* name, const char* description);
PublishStatus publish_status(PublishStatus new_status = PublishStatus::NONE);
bool restore_vehicle(); bool restore_vehicle();
bool update_mine(bool force_update = false);
inline static std::vector<std::string> m_my_profiles;
inline static std::unordered_map<std::string, HandlingProfile> m_handling_profiles;
SearchStatus m_search_status = SearchStatus::IDLE;
private: private:
PublishStatus m_publish_status = PublishStatus::IDLE;
inline static std::unordered_map<Hash, CHandlingData> m_handling_backup; inline static std::unordered_map<Hash, CHandlingData> m_handling_backup;
}; };