1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-10-06 09:07:19 +02:00

Add configuration file to replace OPENRW_GAME_PATH env var

This will permit the storage of other user settings like language and
video settings.
This commit is contained in:
Daniel Evans 2016-05-20 02:09:22 +01:00
parent fe61c1a9f4
commit 2ee4a6e533
13 changed files with 220 additions and 22 deletions

View File

@ -24,6 +24,12 @@ option(ENABLE_PROFILING "Enable detailed profiling metrics")
# Build configuration
#
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions(-DRW_LINUX=1)
else ()
message(FATAL_ERROR "Unknown platform \"${CMAKE_SYSTEM_NAME}\". please update CMakeLists.txt.")
endif ()
# Make GLM use radians
add_definitions(-DGLM_FORCE_RADIANS)
@ -46,6 +52,9 @@ find_package(Bullet REQUIRED)
find_package(SFML 2 COMPONENTS system window audio graphics network REQUIRED)
find_package(MAD REQUIRED)
# External-internal dependencies
add_subdirectory(cmake/external)
#
# Components
#

View File

@ -32,14 +32,19 @@ $ cmake ../ -DCMAKE_BUILD_TYPE=Release
## Running
Once compiled, set the environment variable `OPENRW_GAME_PATH` to the directory containing "gta3.exe" and run the rwgame executable.
### rwgame
This is the game binary
This is the game binary. Before running the game a config file is needed, by default
the game will look for ``~/.config/OpenRW/openrw.ini``, which should look like:
```
[game]
path=/opt/games/Grand Theft Auto 3/ ; Game data path
```
Eventually the game will write this for you, but currently it must be done by
hand.
* Options:
* env: OPENRW\_GAME\_PATH, must be set to the folder containing "gta3.exe"
* -w **n**, -h **n** sets initial window size
* --newgame starts a new game automatically

5
cmake/external/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,5 @@
##### External files
set(EXTERNAL_PREFIX "${CMAKE_BINARY_DIR}/external")
include(inih.cmake)

22
cmake/external/inih.cmake vendored Normal file
View File

@ -0,0 +1,22 @@
###### External Project: inih
include(ExternalProject)
SET(INIH_REPOSITORY https://github.com/benhoyt/inih.git)
SET(INIH_PREFIX_DIR ${EXTERNAL_PREFIX}/inih)
ExternalProject_Add(inih-repository
PREFIX ${INIH_PREFIX_DIR}
# Do nothing, we just need the files.
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
GIT_REPOSITORY ${INIH_REPOSITORY})
SET(INIH_SOURCES
${INIH_PREFIX_DIR}/src/inih-repository/ini.c)
set_source_files_properties(${INIH_SOURCES} PROPERTIES GENERATED TRUE)
add_library(inih
${INIH_SOURCES})
add_dependencies(inih inih-repository)
target_include_directories(inih INTERFACE SYSTEM ${INIH_PREFIX_DIR}/src/inih-repository/)

View File

@ -3,6 +3,9 @@ add_executable(rwgame
RWGame.cpp
GameConfig.hpp
GameConfig.cpp
State.cpp
loadingstate.cpp
@ -27,6 +30,7 @@ include_directories(
target_link_libraries( rwgame
rwengine
inih
${SFML_LIBRARIES}
${OPENGL_LIBRARIES}
${BULLET_LIBRARIES})

83
rwgame/GameConfig.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "GameConfig.hpp"
#include <rw/defines.hpp>
#include <cstring>
#include <ini.h>
const std::string kConfigDirectoryName("OpenRW");
GameConfig::GameConfig(const std::string& configName, const std::string& configPath)
: m_configName(configName)
, m_configPath(configPath)
, m_valid(false)
{
if (m_configPath.empty())
{
m_configPath = getDefaultConfigPath();
}
// Look up the path to use
auto configFile = getConfigFile();
if (ini_parse(configFile.c_str(), handler, this) < 0)
{
m_valid = false;
}
else
{
m_valid = true;
}
}
std::string GameConfig::getConfigFile()
{
return m_configPath + "/" + m_configName;
}
bool GameConfig::isValid()
{
return m_valid;
}
std::string GameConfig::getDefaultConfigPath()
{
#if RW_LINUX
char* config_home = getenv("XDG_CONFIG_HOME");
if (config_home != nullptr) {
return std::string(config_home) + "/" + kConfigDirectoryName;
}
char* home = getenv("HOME");
if (home != nullptr) {
return std::string(home) + "/.config/" + kConfigDirectoryName;
}
// Well now we're stuck.
RW_ERROR("No default config path found.");
return ".";
#else
#error Dont know how to find default config path
#endif
}
int GameConfig::handler(void* user,
const char* section,
const char* name,
const char* value)
{
auto self = static_cast<GameConfig*>(user);
#define MATCH(_s, _n) (strcmp(_s, section) == 0 && strcmp(_n, name) == 0)
if (MATCH("game", "path"))
{
self->m_gamePath = value;
}
else
{
RW_MESSAGE("Unhandled config entry [" << section << "] " << name << " = " << value);
return 0;
}
return 1;
#undef MATCH
}

43
rwgame/GameConfig.hpp Normal file
View File

@ -0,0 +1,43 @@
#ifndef RWGAME_GAMECONFIG_HPP
#define RWGAME_GAMECONFIG_HPP
#include <string>
class GameConfig
{
public:
/**
* @brief GameConfig Loads a game configuration
* @param configName The configuration filename to load
* @param configPath Where to look.
*/
GameConfig(const std::string& configName, const std::string& configPath = getDefaultConfigPath());
/**
* @brief getFilePath Returns the system file path for the configuration
*/
std::string getConfigFile();
/**
* @brief isValid
* @return True if the loaded configuration is valid
*/
bool isValid();
const std::string& getGameDataPath() const { return m_gamePath; }
private:
static std::string getDefaultConfigPath();
static int handler(void*, const char*, const char*, const char*);
/* Config State */
std::string m_configName;
std::string m_configPath;
bool m_valid;
/* Actual Configuration */
/// Path to the game data
std::string m_gamePath;
};
#endif

View File

@ -30,12 +30,18 @@ DebugDraw* debug;
StdOutReciever logPrinter;
RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
: state(nullptr), world(nullptr), renderer(nullptr), script(nullptr),
RWGame::RWGame(int argc, char* argv[])
: config("openrw.ini")
, state(nullptr), world(nullptr), renderer(nullptr), script(nullptr),
debugScript(false), inFocus(true),
showDebugStats(false), showDebugPaths(false), showDebugPhysics(false),
accum(0.f), timescale(1.f)
{
if (!config.isValid())
{
throw std::runtime_error("Invalid configuration file at: " + config.getConfigFile());
}
size_t w = GAME_WINDOW_WIDTH, h = GAME_WINDOW_HEIGHT;
bool fullscreen = false;
bool newgame = false;
@ -91,16 +97,15 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
window.create(sf::VideoMode(w, h), "", style, cs);
window.setMouseCursorVisible(false);
log.addReciever(&logPrinter);
log.info("Game", "Game directory: " + gamepath);
log.addReciever(&logPrinter);
log.info("Game", "Game directory: " + config.getGameDataPath());
if(! GameData::isValidGameDirectory(gamepath) )
if(! GameData::isValidGameDirectory(config.getGameDataPath()) )
{
std::string envname(ENV_GAME_PATH_NAME);
throw std::runtime_error("Invalid game directory path, is " +envname+ " set?");
throw std::runtime_error("Invalid game directory path: " + config.getGameDataPath());
}
data = new GameData(&log, &work, gamepath);
data = new GameData(&log, &work, config.getGameDataPath());
// Initalize all the archives.
data->loadIMG("/models/gta3");
@ -122,7 +127,7 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
debug->setDebugMode(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits);
debug->setShaderProgram(renderer->worldProg);
data->loadDynamicObjects(gamepath + "/data/object.dat");
data->loadDynamicObjects(config.getGameDataPath() + "/data/object.dat");
/// @TODO language choices.
data->loadGXT("english.gxt");

View File

@ -8,6 +8,8 @@
#include <script/ScriptMachine.hpp>
#include "game.hpp"
#include "GameConfig.hpp"
#include <SFML/Graphics.hpp>
class PlayerController;
@ -16,6 +18,7 @@ class HttpServer;
class RWGame
{
Logger log;
GameConfig config;
GameState* state;
GameData* data;
GameWorld* world;
@ -40,7 +43,7 @@ class RWGame
float timescale;
public:
RWGame(const std::string& gamepath, int argc, char* argv[]);
RWGame(int argc, char* argv[]);
~RWGame();
int run();

View File

@ -14,7 +14,6 @@ bool hitWorldRay(glm::vec3& hit, glm::vec3& normal, GameObject** object = nullpt
bool hitWorldRay(const glm::vec3& start, const glm::vec3& direction,
glm::vec3& hit, glm::vec3& normal, GameObject** object = nullptr);
#define ENV_GAME_PATH_NAME ("OPENRW_GAME_PATH")
#define GAME_TIMESTEP (1.f/30.f)
#define GAME_WINDOW_WIDTH 800
#define GAME_WINDOW_HEIGHT 600

View File

@ -1,15 +1,9 @@
#include "RWGame.hpp"
std::string getGamePath()
{
auto v = getenv(ENV_GAME_PATH_NAME);
return v ? v : "";
}
int main(int argc, char *argv[])
{
RWGame game( getGamePath(), argc, argv );
RWGame game( argc, argv );
return game.run();
}

View File

@ -12,6 +12,7 @@ set(TEST_SOURCES
"test_character.cpp"
"test_chase.cpp"
"test_cutscene.cpp"
"test_config.cpp"
"test_data.cpp"
"test_FileIndex.cpp"
"test_GameData.cpp"
@ -39,6 +40,9 @@ set(TEST_SOURCES
"test_weapon.cpp"
"test_worker.cpp"
"test_world.cpp"
# Hack in rwgame sources until there's a per-target test suite
"${CMAKE_SOURCE_DIR}/rwgame/GameConfig.cpp"
)
ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK)
@ -57,6 +61,7 @@ include_directories(SYSTEM
target_link_libraries(run_tests
rwengine
inih
${OPENGL_LIBRARIES}
${SFML_LIBRARIES}
${BULLET_LIBRARIES}

21
tests/test_config.cpp Normal file
View File

@ -0,0 +1,21 @@
#include <boost/test/unit_test.hpp>
#include <GameConfig.hpp>
#include <fstream>
BOOST_AUTO_TEST_SUITE(ConfigTests)
BOOST_AUTO_TEST_CASE(test_loading)
{
// Write out a temporary file
std::ofstream test_config("/tmp/openrw_test.ini");
test_config << "[game]\n"
<< "path=/dev/test" << std::endl;
GameConfig config("openrw_test.ini", "/tmp");
BOOST_CHECK(config.isValid());
BOOST_CHECK_EQUAL(config.getGameDataPath(), "/dev/test");
}
BOOST_AUTO_TEST_SUITE_END()