mirror of
https://gitea.windcorp.ru/Wind-Corporation/Progressia.git
synced 2025-08-28 10:36:50 +03:00
Added logging, refactored versioning; STB is now included
- Added logging - Rewrote versioning code - Refactored dependency management - STB (stb_image.h) is now included - All other dependencies now use find_package - Cross-compilation from Linux to Windows is now possible
This commit is contained in:
5
main/config.h.in
Normal file
5
main/config.h.in
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define _VERSION "@VERSION@"
|
||||
#define _BUILD_ID "@BUILD_ID@"
|
||||
#cmakedefine VULKAN_ERROR_CHECKING
|
@@ -12,6 +12,9 @@
|
||||
|
||||
#include "rendering.h"
|
||||
|
||||
#include "logging.h"
|
||||
using namespace progressia::main::logging;
|
||||
|
||||
namespace progressia {
|
||||
namespace main {
|
||||
|
||||
@@ -56,13 +59,13 @@ void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height,
|
||||
|
||||
void initialize(GraphicsInterface &gintp) {
|
||||
|
||||
std::cout << "game init begin" << std::endl;
|
||||
debug("game init begin");
|
||||
gint = &gintp;
|
||||
|
||||
texture1.reset(
|
||||
gint->newTexture(progressia::main::loadImage(u"../assets/texture.png")));
|
||||
texture2.reset(
|
||||
gint->newTexture(progressia::main::loadImage(u"../assets/texture2.png")));
|
||||
texture1.reset(gint->newTexture(
|
||||
progressia::main::loadImage(u"../assets/texture.png")));
|
||||
texture2.reset(gint->newTexture(
|
||||
progressia::main::loadImage(u"../assets/texture2.png")));
|
||||
|
||||
// Cube 1
|
||||
{
|
||||
@@ -121,7 +124,7 @@ void initialize(GraphicsInterface &gintp) {
|
||||
perspective.reset(gint->newView());
|
||||
light.reset(gint->newLight());
|
||||
|
||||
std::cout << "game init complete" << std::endl;
|
||||
debug("game init complete");
|
||||
}
|
||||
|
||||
void renderTick() {
|
||||
@@ -161,7 +164,7 @@ void renderTick() {
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
std::cout << "game shutdown begin" << std::endl;
|
||||
debug("game shutdown begin");
|
||||
|
||||
cube1.reset();
|
||||
cube2.reset();
|
||||
@@ -171,7 +174,7 @@ void shutdown() {
|
||||
light.reset();
|
||||
perspective.reset();
|
||||
|
||||
std::cout << "game shutdown complete" << std::endl;
|
||||
debug("game shutdown complete");
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
|
169
main/logging.cpp
Normal file
169
main/logging.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "logging.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
namespace progressia {
|
||||
namespace main {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class LogSinkBackend {
|
||||
private:
|
||||
bool writing = false;
|
||||
std::stringstream buffer;
|
||||
|
||||
void flush();
|
||||
|
||||
public:
|
||||
LogSinkBackend() {}
|
||||
|
||||
std::ostream &getOutput() { return buffer; }
|
||||
|
||||
void setWriting(bool writing) {
|
||||
if (this->writing == writing) {
|
||||
std::cerr << "Attempting to write two log messages at once"
|
||||
<< std::endl;
|
||||
// REPORT_ERROR
|
||||
exit(1);
|
||||
}
|
||||
|
||||
this->writing = writing;
|
||||
|
||||
if (!writing) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace {
|
||||
std::ofstream openLogFile() {
|
||||
// FIXME this is relative to bin, not root dir
|
||||
std::filesystem::create_directories("../run");
|
||||
std::filesystem::create_directories("../run/logs");
|
||||
return std::ofstream("../run/logs/latest.log");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::mutex logFileMutex;
|
||||
std::ofstream logFile = openLogFile();
|
||||
thread_local detail::LogSinkBackend theBackend;
|
||||
|
||||
std::ostream &detail::LogSink::getStream() const {
|
||||
if (!isCurrentSink) {
|
||||
std::cerr << "LogSink::getStream() while !isCurrentSink" << std::endl;
|
||||
// REPORT_ERROR
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return theBackend.getOutput();
|
||||
}
|
||||
|
||||
detail::LogSink::LogSink(bool isCurrentSink) : isCurrentSink(isCurrentSink) {
|
||||
if (isCurrentSink) {
|
||||
theBackend.setWriting(true);
|
||||
}
|
||||
}
|
||||
|
||||
detail::LogSink::~LogSink() {
|
||||
if (isCurrentSink) {
|
||||
theBackend.setWriting(false);
|
||||
}
|
||||
}
|
||||
|
||||
detail::LogSink::LogSink(LogSink &&moveFrom)
|
||||
: isCurrentSink(moveFrom.isCurrentSink) {
|
||||
moveFrom.isCurrentSink = false;
|
||||
}
|
||||
|
||||
void detail::LogSinkBackend::flush() {
|
||||
auto message = buffer.str();
|
||||
buffer.str("");
|
||||
buffer.clear();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logFileMutex);
|
||||
// TODO flush less often?
|
||||
logFile << message << std::endl;
|
||||
std::cout << message << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// FIXME This approach is horribly inefficient. It is also unsafe if any
|
||||
// other piece of code wants access to std::localtime.
|
||||
std::mutex getLocalTimeMutex;
|
||||
std::tm getLocalTimeAndDontExplodePlease() {
|
||||
std::lock_guard<std::mutex> lock(getLocalTimeMutex);
|
||||
|
||||
std::time_t t = std::time(nullptr);
|
||||
return *std::localtime(&t);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
detail::LogSink log(LogLevel level, const char *start) {
|
||||
|
||||
#ifdef NDEBUG
|
||||
if (level == LogLevel::DEBUG) {
|
||||
return detail::LogSink(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
detail::LogSink sink(true);
|
||||
auto tm = getLocalTimeAndDontExplodePlease();
|
||||
sink << std::put_time(&tm, "%T") << " ";
|
||||
|
||||
switch (level) {
|
||||
case LogLevel::DEBUG:
|
||||
sink << "DEBUG";
|
||||
break;
|
||||
case LogLevel::INFO:
|
||||
sink << "INFO ";
|
||||
break;
|
||||
case LogLevel::WARN:
|
||||
sink << "WARN ";
|
||||
break;
|
||||
case LogLevel::ERROR:
|
||||
sink << "ERROR";
|
||||
break;
|
||||
default:
|
||||
sink << "FATAL";
|
||||
break;
|
||||
}
|
||||
|
||||
sink << " ";
|
||||
|
||||
if (start != nullptr) {
|
||||
sink << start;
|
||||
}
|
||||
|
||||
return sink;
|
||||
}
|
||||
|
||||
namespace logging {
|
||||
|
||||
#ifdef NDEBUG
|
||||
detail::LogSink debug(const char *) { return detail::LogSink(false); }
|
||||
#else
|
||||
detail::LogSink debug(const char *start) { return log(LogLevel::DEBUG, start); }
|
||||
#endif
|
||||
|
||||
detail::LogSink info(const char *start) { return log(LogLevel::INFO, start); }
|
||||
|
||||
detail::LogSink warn(const char *start) { return log(LogLevel::WARN, start); }
|
||||
|
||||
detail::LogSink error(const char *start) { return log(LogLevel::ERROR, start); }
|
||||
|
||||
detail::LogSink fatal(const char *start) { return log(LogLevel::FATAL, start); }
|
||||
|
||||
} // namespace logging
|
||||
|
||||
} // namespace main
|
||||
} // namespace progressia
|
64
main/logging.h
Normal file
64
main/logging.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "boost/core/noncopyable.hpp"
|
||||
#include <ostream>
|
||||
|
||||
namespace progressia {
|
||||
namespace main {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class LogSink : private boost::noncopyable {
|
||||
private:
|
||||
bool isCurrentSink;
|
||||
|
||||
std::ostream &getStream() const;
|
||||
|
||||
template <typename T> void put(const T &x) const {
|
||||
if (isCurrentSink) {
|
||||
getStream() << x;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
LogSink(bool isCurrentSink);
|
||||
~LogSink();
|
||||
|
||||
LogSink(LogSink &&);
|
||||
|
||||
template <typename T>
|
||||
friend const LogSink &operator<<(const LogSink &sink, const T &x) {
|
||||
sink.put(x);
|
||||
return sink;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
enum class LogLevel {
|
||||
// Only interesting when investigating a problem.
|
||||
DEBUG,
|
||||
// A regular user may want to see this.
|
||||
INFO,
|
||||
// All users should read this; if message persists, something is wrong.
|
||||
WARN,
|
||||
// Something is definitely wrong, but we're not crashing (yet).
|
||||
ERROR,
|
||||
// This is why the game is about to crash.
|
||||
FATAL
|
||||
};
|
||||
|
||||
detail::LogSink log(LogLevel, const char *start = nullptr);
|
||||
|
||||
namespace logging {
|
||||
detail::LogSink debug(const char *start = nullptr);
|
||||
detail::LogSink info(const char *start = nullptr);
|
||||
detail::LogSink warn(const char *start = nullptr);
|
||||
detail::LogSink error(const char *start = nullptr);
|
||||
detail::LogSink fatal(const char *start = nullptr);
|
||||
} // namespace logging
|
||||
|
||||
void initializeLogging();
|
||||
void shutdownLogging();
|
||||
|
||||
} // namespace main
|
||||
} // namespace progressia
|
57
main/meta.h
57
main/meta.h
@@ -1,43 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace progressia {
|
||||
namespace main {
|
||||
namespace meta {
|
||||
|
||||
namespace detail {
|
||||
constexpr static uint32_t getVersionNumber(const char *versionStr) {
|
||||
uint32_t parts[] = {0, 0, 0};
|
||||
std::size_t partCount = sizeof(parts) / sizeof(parts[0]);
|
||||
std::size_t currentPart = 0;
|
||||
|
||||
for (const char *cp = versionStr; *cp != '\0'; cp++) {
|
||||
char c = *cp;
|
||||
if (c == '.') {
|
||||
currentPart++;
|
||||
} else if (currentPart < partCount && c >= '0' && c <= '9') {
|
||||
parts[currentPart] =
|
||||
parts[currentPart] * 10 + static_cast<uint32_t>(c - '0');
|
||||
}
|
||||
}
|
||||
|
||||
return (parts[0] << 16) | (parts[1] << 8) | (parts[2] << 0);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
constexpr const char *NAME = "Progressia";
|
||||
constexpr const char *VERSION = _VERSION;
|
||||
constexpr const char *BUILD_ID = _BUILD_ID;
|
||||
|
||||
#ifndef _MAJOR
|
||||
#warning Version number (_MAJOR _MINOR _PATCH _BUILD) not set, using 0.0.0+1
|
||||
#define _MAJOR 0
|
||||
#define _MINOR 0
|
||||
#define _PATCH 0
|
||||
#define _BUILD 1
|
||||
#endif
|
||||
|
||||
using VersionUnit = uint8_t;
|
||||
using VersionInt = uint32_t;
|
||||
|
||||
constexpr struct {
|
||||
|
||||
VersionUnit major, minor, patch, build;
|
||||
|
||||
VersionInt number;
|
||||
|
||||
bool isRelease;
|
||||
|
||||
} VERSION{_MAJOR,
|
||||
_MINOR,
|
||||
_PATCH,
|
||||
_BUILD,
|
||||
|
||||
(static_cast<VersionInt>(_MAJOR) << 24) |
|
||||
(static_cast<VersionInt>(_MINOR) << 16) |
|
||||
(static_cast<VersionInt>(_PATCH) << 8) |
|
||||
(static_cast<VersionInt>(_BUILD) << 0),
|
||||
|
||||
_BUILD == 0};
|
||||
constexpr uint32_t VERSION_NUMBER = detail::getVersionNumber(_VERSION);
|
||||
constexpr uint32_t VERSION_MAJOR = (VERSION_NUMBER & 0xFF0000) >> 16;
|
||||
constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8;
|
||||
constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0;
|
||||
|
||||
} // namespace meta
|
||||
} // namespace main
|
||||
|
@@ -8,6 +8,9 @@
|
||||
|
||||
#include "stb/stb_image.h"
|
||||
|
||||
#include "../logging.h"
|
||||
using namespace progressia::main::logging;
|
||||
|
||||
namespace progressia {
|
||||
namespace main {
|
||||
|
||||
@@ -22,8 +25,7 @@ Image loadImage(const std::filesystem::path &path) {
|
||||
std::ifstream file(path, std::ios::ate | std::ios::binary);
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cout << "Could not read a PNG image from file " << path
|
||||
<< std::endl;
|
||||
fatal() << "Could not access a PNG image in file " << path;
|
||||
// REPORT_ERROR
|
||||
exit(1);
|
||||
}
|
||||
@@ -45,8 +47,7 @@ Image loadImage(const std::filesystem::path &path) {
|
||||
&channelsInFile, STBI_rgb_alpha);
|
||||
|
||||
if (stbAllocatedData == NULL) {
|
||||
std::cout << "Could not load a PNG image from file " << path
|
||||
<< std::endl;
|
||||
fatal() << "Could not decode a PNG image from file " << path;
|
||||
// REPORT_ERROR
|
||||
exit(1);
|
||||
}
|
||||
|
Reference in New Issue
Block a user