A modern C++ header-only library for interacting with Voices of the Void game state, providing type-safe access to game objects, player state, and environment variables.
- ๐ฎ Type-safe game state access through a single header
- ๐ Memory-safe field manipulation
- ๐ฏ Direct access to core game systems
- ๐งฐ Modern C++ wrapper around UE4/UE4SS objects
- ๐ก Object lifetime tracking utilities
- ๐ง Struct manipulation macros for clean field access of custom fields
- Map VotV specific functions with same method
- Prerequisites
- Version Compatibility
- Installation
- Usage
- Utility Classes
- API Reference
- Examples
- Contributing
- Credits
- CMake 3.18 or higher
- UE4SS C++ Dev Environment Setup
The library consists of three headers:
game.hpp
- Main game state and objectsObjectLifetimeTracker.hpp
- UObject lifetime managementStructUtil.hpp
- Field access macros and utilities
- Add the repository as a submodule:
git submodule add https://github.com/modestimpala/libvotv.git extern/libvotv
- Add to your CMakeLists.txt:
add_subdirectory(extern/libvotv)
target_link_libraries(your_project PRIVATE libvotv)
Since the library is header-only:
- Copy the three headers to your project
- Add their location to your include path
- Ensure UE4SS headers are available
#include <votv/game.hpp> // Single header for all game functionality
#include <votv/ObjectLifetimeTracker.hpp>
using namespace votv::game;
// Get existing game mode from UE4SS
GameMode* gameMode = /* get from UE4SS */;
// Validate object is still alive
auto& tracker = ObjectLifetimeTracker::Get();
if (!tracker.IsActorAlive(gameMode)) return;
// Access player
MainPlayer* player = gameMode->mainPlayer;
if (!tracker.IsActorAlive(player)) return;
// Check player state
if (player->inWater && player->air < 50.0f) {
// Handle low air situation
}
The ObjectLifetimeTracker
provides safe UObject lifetime tracking.
Initialize the ObjectLifetimeTracker as early as possible in your mod lifecycle:
auto on_unreal_init() -> void override
auto& tracker = ObjectLifetimeTracker::Get(); // Get instance early
// Rest of initialization...
}
Early initialization ensures no objects created during startup are missed by the tracking system.
Example:
#include <ObjectLifetimeTracker.hpp>
// Get singleton instance
auto& tracker = ObjectLifetimeTracker::Get();
// Track specific types
tracker.RegisterTrackedType(Car::StaticClass());
tracker.RegisterTrackedName(L"specialDoor");
// Check if objects are still valid
if (tracker.IsActorAlive(someActor)) {
// Safe to use
}
Features:
- Default tracking of GameMode and MainPlayer objects
- Registering custom types/names to track
- Thread-safe operation
- RAII-based resource management
- Protection against use-after-free
StructUtil.hpp
provides type-safe field access macros:
// Regular field access
FIELD(0x0600, MainPlayer*, mainPlayer); // Generates getters/setters
// Vector field with component access
VECTOR_INT_FIELD(0x02F8, timeZ); // Adds _x, _y, _z component access
// Enum field with type safety
ENUM_FIELD(0x01E1, GameModes::Type, gamemodeType);
// Bit field for flags
BIT_FIELD(0x0480, 0x01, someFlag);
// Array field access
ARRAY_FIELD(0x0208, RC::Unreal::TArray<float>, scores);
Features:
- Automatic getter/setter generation
- Type-safe field access
- Support for vectors, enums, arrays and bit fields
- Property syntax support through __declspec
- All methods marked noexcept where appropriate
- Proper reference handling (lvalue/rvalue)
All game functionality is contained in a single header under the votv::game
namespace.
namespace votv::game {
class GameMode;
class MainPlayer;
class DayNightCycle;
class Car;
class Door;
class KerfurOmega;
class GameInst;
class SaveSlot;
}
PowerInfo GetPowerInfo() const; // Get complete power system state
TimeInfo GetTimeInfo() const; // Get complete time info
WeatherInfo GetWeatherInfo() const; // Get complete weather info
// Game Instance
class GameInst : public RC::Unreal::UObject {
SaveSlot* save; // Active save
GameModes::Type gamemodeType; // Game mode Type (Sandbox, Story, etc)
bool opened; // Instance opened
int32_t startDay; // Starting day
...
};
// Save slot
class SaveSlot : public RC::Unreal::UObject {
float food; // Food level
float sleep; // Sleep level
int32_t Points; // Money
float health; // Current health
...
};
See header file for complete API.
#include <votv/game.hpp>
#include <votv/ObjectLifetimeTracker.hpp>
void SafeGameObjectAccess(votv::game::GameMode* gameMode) {
auto& tracker = ObjectLifetimeTracker::Get();
// Validate game mode
if (!tracker.IsActorAlive(gameMode)) return;
// Access and validate player
auto* player = gameMode->mainPlayer;
if (!tracker.IsActorAlive(player)) return;
// Safe to use objects
auto state = player->GetState();
auto power = gameMode->GetPowerInfo();
if (state.inWater && power.ratio < 0.2f) {
// Handle dangerous situation
}
}
#include <votv/game.hpp>
#include <votv/ObjectLifetimeTracker.hpp>
void SetupTracking() {
auto& tracker = ObjectLifetimeTracker::Get();
// Track all cars
tracker.RegisterTrackedType(Car::StaticClass());
// Track special objects
tracker.RegisterTrackedName(L"prop_important");
// Later cleanup if needed
tracker.UnregisterTrackedType(Car::StaticClass());
tracker.UnregisterTrackedName(L"prop_important");
}
#include <votv/game.hpp>
void HandleTimeChange(DayNightCycle* dnc) {
// Get complete time state
auto timeInfo = dnc->GetTimeInfo();
auto weather = dnc->GetWeatherInfo();
// Access raw day counter
int32_t currentDay = dnc->current_day();
// Modify time
dnc->set_timeZ(0, 0, currentDay + 1);
}
See UE4SS Dumpers
Make sure DumpOffsetsAndSizes and LoadAllAssetsBeforeGeneratingCXXHeaders are 1 then dump headers from main menu.
Find specific class, then map offsets:
// Dumped cremator.hpp
class Acremator_C : public Aactor_save_C
{
...
bool IsClosed; // 0x0351 (size: 0x1)
...
}
#include <votv/game.hpp>
#include <StructUtil.hpp>
// Your custom wrapper class (e.g., extra_game.hpp)
class Cremator : public RC::Unreal::AActor {
public:
FIELD(0x0351, bool, isClosed); // Maps to IsClosed from dump
};
And so on for any other custom fields needed.
- Fork the repository
- Create your feature branch (git checkout -b feature/extra-offsets)
- Commit your changes (git commit -m 'More offsets')
- Push to the branch (git push origin feature/extra-offsets)
- Open a Pull Request
- MrDrNose for VotV
- UE4SS and their Discord Community
- agersant (gbvsr-frame-meter)