refactor: 重构日志系统并优化智能指针命名

重构日志系统,移除Registry日志级别,增加带类别的日志宏
将makeUnique重命名为unique以保持一致性
更新所有相关文件和文档中的日志调用
优化代码格式和头文件包含
This commit is contained in:
ChestnutYueyue 2026-02-23 02:15:30 +08:00
parent e600730105
commit 037b8599c7
28 changed files with 14620 additions and 894 deletions

View File

@ -1,13 +1,13 @@
#pragma once #pragma once
#include <extra2d/asset/asset.h>
#include <extra2d/asset/asset_handle.h>
#include <extra2d/asset/asset_types.h>
#include <extra2d/core/types.h>
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
#include <deque> #include <deque>
#include <extra2d/asset/asset.h>
#include <extra2d/asset/asset_handle.h>
#include <extra2d/asset/asset_types.h>
#include <extra2d/core/types.h>
#include <list> #include <list>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -15,6 +15,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
namespace extra2d { namespace extra2d {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -27,26 +28,25 @@ namespace extra2d {
* 访LRU缓存管理 * 访LRU缓存管理
*/ */
struct CacheEntry { struct CacheEntry {
Ref<Asset> asset; Ref<Asset> asset;
std::chrono::steady_clock::time_point lastAccess; std::chrono::steady_clock::time_point lastAccess;
size_t accessCount = 0; size_t accessCount = 0;
/** /**
* @brief * @brief
* @param a * @param a
*/ */
explicit CacheEntry(Ref<Asset> a) explicit CacheEntry(Ref<Asset> a)
: asset(std::move(a)), : asset(std::move(a)), lastAccess(std::chrono::steady_clock::now()),
lastAccess(std::chrono::steady_clock::now()), accessCount(1) {}
accessCount(1) {}
/** /**
* @brief 访 * @brief 访
*/ */
void touch() { void touch() {
lastAccess = std::chrono::steady_clock::now(); lastAccess = std::chrono::steady_clock::now();
++accessCount; ++accessCount;
} }
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -67,197 +67,191 @@ struct CacheEntry {
*/ */
class AssetCache { class AssetCache {
public: public:
/** /**
* @brief * @brief
* @param limit 0 * @param limit 0
*/ */
explicit AssetCache(size_t limit = 0); explicit AssetCache(size_t limit = 0);
~AssetCache() = default; ~AssetCache() = default;
AssetCache(const AssetCache&) = delete; AssetCache(const AssetCache &) = delete;
AssetCache& operator=(const AssetCache&) = delete; AssetCache &operator=(const AssetCache &) = delete;
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 资源管理 // 资源管理
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** /**
* @brief * @brief
* @tparam T * @tparam T
* @param asset * @param asset
* @return * @return
*/ */
template<typename T> template <typename T> AssetHandle<T> add(Ref<T> asset) {
AssetHandle<T> add(Ref<T> asset) { static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
if (!asset) { if (!asset) {
return AssetHandle<T>(); return AssetHandle<T>();
}
std::unique_lock<std::shared_mutex> lock(mutex_);
AssetID id = asset->id();
size_t memSize = asset->memSize();
bytes_ += memSize;
auto it = lruList_.insert(lruList_.end(), id);
entries_[id] = CacheEntryData{
ptr::makeUnique<CacheEntry>(std::static_pointer_cast<Asset>(asset)),
it
};
++stats_.count;
if (limit_ > 0 && bytes_ > limit_) {
evict();
}
return AssetHandle<T>(id, Weak<T>(asset));
} }
/** std::unique_lock<std::shared_mutex> lock(mutex_);
* @brief
* @tparam T
* @param id ID
* @return
*/
template<typename T>
AssetHandle<T> get(const AssetID& id) {
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
std::unique_lock<std::shared_mutex> lock(mutex_); AssetID id = asset->id();
auto it = entries_.find(id); size_t memSize = asset->memSize();
if (it == entries_.end()) { bytes_ += memSize;
++stats_.misses;
return AssetHandle<T>();
}
it->second.entry->touch(); auto it = lruList_.insert(lruList_.end(), id);
lruList_.erase(it->second.lruIterator); entries_[id] = CacheEntryData{
it->second.lruIterator = lruList_.insert(lruList_.end(), id); ptr::unique<CacheEntry>(std::static_pointer_cast<Asset>(asset)), it};
++stats_.hits; ++stats_.count;
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset); if (limit_ > 0 && bytes_ > limit_) {
return AssetHandle<T>(id, Weak<T>(typedAsset)); evict();
} }
/** return AssetHandle<T>(id, Weak<T>(asset));
* @brief }
* @param id ID
* @return true
*/
bool has(const AssetID& id) const;
/** /**
* @brief * @brief
* @param id ID * @tparam T
* @return true * @param id ID
*/ * @return
bool remove(const AssetID& id); */
template <typename T> AssetHandle<T> get(const AssetID &id) {
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
// ------------------------------------------------------------------------- std::unique_lock<std::shared_mutex> lock(mutex_);
// 缓存管理
// -------------------------------------------------------------------------
/** auto it = entries_.find(id);
* @brief if (it == entries_.end()) {
* @param limit 0 ++stats_.misses;
*/ return AssetHandle<T>();
void setLimit(size_t limit); }
/** it->second.entry->touch();
* @brief
* @return
*/
size_t limit() const { return limit_; }
/** lruList_.erase(it->second.lruIterator);
* @brief 使 it->second.lruIterator = lruList_.insert(lruList_.end(), id);
* @return 使
*/
size_t bytes() const { return bytes_; }
/** ++stats_.hits;
* @brief
* @return
*/
size_t count() const;
/** auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
* @brief 使 return AssetHandle<T>(id, Weak<T>(typedAsset));
* @return 使 }
*/
size_t size() const { return bytes_; }
/** /**
* @brief * @brief
*/ * @param id ID
void hit() { ++stats_.hits; } * @return true
*/
bool has(const AssetID &id) const;
/** /**
* @brief * @brief
*/ * @param id ID
void miss() { ++stats_.misses; } * @return true
*/
bool remove(const AssetID &id);
/** // -------------------------------------------------------------------------
* @brief // 缓存管理
* @return // -------------------------------------------------------------------------
*/
size_t purge();
/** /**
* @brief * @brief
*/ * @param limit 0
void clear(); */
void setLimit(size_t limit);
/** /**
* @brief * @brief
* @return * @return
*/ */
CacheStats stats() const; size_t limit() const { return limit_; }
/** /**
* @brief * @brief 使
*/ * @return 使
void resetStats(); */
size_t bytes() const { return bytes_; }
/**
* @brief
* @return
*/
size_t count() const;
/**
* @brief 使
* @return 使
*/
size_t size() const { return bytes_; }
/**
* @brief
*/
void hit() { ++stats_.hits; }
/**
* @brief
*/
void miss() { ++stats_.misses; }
/**
* @brief
* @return
*/
size_t purge();
/**
* @brief
*/
void clear();
/**
* @brief
* @return
*/
CacheStats stats() const;
/**
* @brief
*/
void resetStats();
private: private:
/** /**
* @brief LRU迭代器 * @brief LRU迭代器
*/ */
struct CacheEntryData { struct CacheEntryData {
Ref<CacheEntry> entry; Ref<CacheEntry> entry;
std::list<AssetID>::iterator lruIterator; std::list<AssetID>::iterator lruIterator;
}; };
/** /**
* @brief LRU策略 * @brief LRU策略
*/ */
void evict(); void evict();
/** /**
* @brief * @brief
* @param entry * @param entry
* @return true * @return true
*/ */
bool canEvict(const CacheEntry& entry) const; bool canEvict(const CacheEntry &entry) const;
mutable std::shared_mutex mutex_; mutable std::shared_mutex mutex_;
std::unordered_map<AssetID, CacheEntryData> entries_; std::unordered_map<AssetID, CacheEntryData> entries_;
std::list<AssetID> lruList_; std::list<AssetID> lruList_;
size_t limit_ = 0; size_t limit_ = 0;
size_t bytes_ = 0; size_t bytes_ = 0;
mutable CacheStats stats_; mutable CacheStats stats_;
}; };
} } // namespace extra2d

View File

@ -20,41 +20,41 @@ namespace extra2d {
*/ */
class AssetLoaderBase { class AssetLoaderBase {
public: public:
virtual ~AssetLoaderBase() = default; virtual ~AssetLoaderBase() = default;
/** /**
* @brief Asset * @brief Asset
* @param path * @param path
* @return * @return
*/ */
virtual Ref<Asset> loadBase(const std::string& path) = 0; virtual Ref<Asset> loadBase(const std::string &path) = 0;
/** /**
* @brief * @brief
* @param data * @param data
* @param size * @param size
* @return * @return
*/ */
virtual Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) = 0; virtual Ref<Asset> loadFromMemoryBase(const u8 *data, size_t size) = 0;
/** /**
* @brief * @brief
* @param path * @param path
* @return true * @return true
*/ */
virtual bool canLoad(const std::string& path) const = 0; virtual bool canLoad(const std::string &path) const = 0;
/** /**
* @brief * @brief
* @return * @return
*/ */
virtual AssetType type() const = 0; virtual AssetType type() const = 0;
/** /**
* @brief * @brief
* @return * @return
*/ */
virtual std::vector<std::string> extensions() const = 0; virtual std::vector<std::string> extensions() const = 0;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -69,36 +69,32 @@ public:
* *
* @tparam T * @tparam T
*/ */
template<typename T> template <typename T> class AssetLoader : public AssetLoaderBase {
class AssetLoader : public AssetLoaderBase { static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
static_assert(std::is_base_of_v<Asset, T>,
"T must derive from Asset");
public: public:
virtual ~AssetLoader() = default; virtual ~AssetLoader() = default;
/** /**
* @brief * @brief
* @param path * @param path
* @return nullptr * @return nullptr
*/ */
virtual Ref<T> load(const std::string& path) = 0; virtual Ref<T> load(const std::string &path) = 0;
/** /**
* @brief * @brief
* @param data * @param data
* @param size * @param size
* @return nullptr * @return nullptr
*/ */
virtual Ref<T> loadFromMemory(const u8* data, size_t size) = 0; virtual Ref<T> loadFromMemory(const u8 *data, size_t size) = 0;
Ref<Asset> loadBase(const std::string& path) override { Ref<Asset> loadBase(const std::string &path) override { return load(path); }
return load(path);
}
Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) override { Ref<Asset> loadFromMemoryBase(const u8 *data, size_t size) override {
return loadFromMemory(data, size); return loadFromMemory(data, size);
} }
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -113,29 +109,29 @@ public:
*/ */
class TextureLoader : public AssetLoader<TextureAsset> { class TextureLoader : public AssetLoader<TextureAsset> {
public: public:
TextureLoader(); TextureLoader();
~TextureLoader() override; ~TextureLoader() override;
Ref<TextureAsset> load(const std::string& path) override; Ref<TextureAsset> load(const std::string &path) override;
Ref<TextureAsset> loadFromMemory(const u8* data, size_t size) override; Ref<TextureAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string& path) const override; bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Texture; } AssetType type() const override { return AssetType::Texture; }
std::vector<std::string> extensions() const override; std::vector<std::string> extensions() const override;
/** /**
* @brief * @brief
* @param channels 1-40 * @param channels 1-40
*/ */
void setDesiredChannels(int channels); void setDesiredChannels(int channels);
/** /**
* @brief * @brief
* @return * @return
*/ */
int desiredChannels() const { return desiredChannels_; } int desiredChannels() const { return desiredChannels_; }
private: private:
int desiredChannels_ = 4; int desiredChannels_ = 4;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -149,11 +145,11 @@ private:
*/ */
class FontLoader : public AssetLoader<FontAsset> { class FontLoader : public AssetLoader<FontAsset> {
public: public:
Ref<FontAsset> load(const std::string& path) override; Ref<FontAsset> load(const std::string &path) override;
Ref<FontAsset> loadFromMemory(const u8* data, size_t size) override; Ref<FontAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string& path) const override; bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Font; } AssetType type() const override { return AssetType::Font; }
std::vector<std::string> extensions() const override; std::vector<std::string> extensions() const override;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -169,38 +165,39 @@ public:
*/ */
class ShaderLoader : public AssetLoader<ShaderAsset> { class ShaderLoader : public AssetLoader<ShaderAsset> {
public: public:
Ref<ShaderAsset> load(const std::string& path) override; Ref<ShaderAsset> load(const std::string &path) override;
Ref<ShaderAsset> loadFromMemory(const u8* data, size_t size) override; Ref<ShaderAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string& path) const override; bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Shader; } AssetType type() const override { return AssetType::Shader; }
std::vector<std::string> extensions() const override; std::vector<std::string> extensions() const override;
/** /**
* @brief * @brief
* @param marker "[VERTEX]" * @param marker "[VERTEX]"
*/ */
void setVertexMarker(const std::string& marker) { vertexMarker_ = marker; } void setVertexMarker(const std::string &marker) { vertexMarker_ = marker; }
/** /**
* @brief * @brief
* @param marker "[FRAGMENT]" * @param marker "[FRAGMENT]"
*/ */
void setFragmentMarker(const std::string& marker) { fragmentMarker_ = marker; } void setFragmentMarker(const std::string &marker) {
fragmentMarker_ = marker;
}
private: private:
std::string vertexMarker_ = "[VERTEX]"; std::string vertexMarker_ = "[VERTEX]";
std::string fragmentMarker_ = "[FRAGMENT]"; std::string fragmentMarker_ = "[FRAGMENT]";
/** /**
* @brief * @brief
* @param content * @param content
* @param vertex * @param vertex
* @param fragment * @param fragment
* @return true * @return true
*/ */
bool parseCombined(const std::string& content, bool parseCombined(const std::string &content, std::string &vertex,
std::string& vertex, std::string &fragment);
std::string& fragment);
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -215,20 +212,20 @@ private:
*/ */
class AudioLoader : public AssetLoader<AudioAsset> { class AudioLoader : public AssetLoader<AudioAsset> {
public: public:
Ref<AudioAsset> load(const std::string& path) override; Ref<AudioAsset> load(const std::string &path) override;
Ref<AudioAsset> loadFromMemory(const u8* data, size_t size) override; Ref<AudioAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string& path) const override; bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Audio; } AssetType type() const override { return AssetType::Audio; }
std::vector<std::string> extensions() const override; std::vector<std::string> extensions() const override;
private: private:
/** /**
* @brief WAV * @brief WAV
* @param data * @param data
* @param size * @param size
* @return * @return
*/ */
Ref<AudioAsset> loadWav(const u8* data, size_t size); Ref<AudioAsset> loadWav(const u8 *data, size_t size);
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -242,11 +239,11 @@ private:
*/ */
class DataLoader : public AssetLoader<DataAsset> { class DataLoader : public AssetLoader<DataAsset> {
public: public:
Ref<DataAsset> load(const std::string& path) override; Ref<DataAsset> load(const std::string &path) override;
Ref<DataAsset> loadFromMemory(const u8* data, size_t size) override; Ref<DataAsset> loadFromMemory(const u8 *data, size_t size) override;
bool canLoad(const std::string& path) const override; bool canLoad(const std::string &path) const override;
AssetType type() const override { return AssetType::Data; } AssetType type() const override { return AssetType::Data; }
std::vector<std::string> extensions() const override; std::vector<std::string> extensions() const override;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -261,52 +258,52 @@ public:
*/ */
class AssetLoaderFactory { class AssetLoaderFactory {
public: public:
/** /**
* @brief * @brief
* @return * @return
*/ */
static Unique<TextureLoader> createTextureLoader() { static Unique<TextureLoader> createTextureLoader() {
return ptr::makeUnique<TextureLoader>(); return ptr::unique<TextureLoader>();
} }
/** /**
* @brief * @brief
* @return * @return
*/ */
static Unique<FontLoader> createFontLoader() { static Unique<FontLoader> createFontLoader() {
return ptr::makeUnique<FontLoader>(); return ptr::unique<FontLoader>();
} }
/** /**
* @brief * @brief
* @return * @return
*/ */
static Unique<ShaderLoader> createShaderLoader() { static Unique<ShaderLoader> createShaderLoader() {
return ptr::makeUnique<ShaderLoader>(); return ptr::unique<ShaderLoader>();
} }
/** /**
* @brief * @brief
* @return * @return
*/ */
static Unique<AudioLoader> createAudioLoader() { static Unique<AudioLoader> createAudioLoader() {
return ptr::makeUnique<AudioLoader>(); return ptr::unique<AudioLoader>();
} }
/** /**
* @brief * @brief
* @return * @return
*/ */
static Unique<DataLoader> createDataLoader() { static Unique<DataLoader> createDataLoader() {
return ptr::makeUnique<DataLoader>(); return ptr::unique<DataLoader>();
} }
/** /**
* @brief * @brief
* @param extension ".png" * @param extension ".png"
* @return AssetType::Unknown * @return AssetType::Unknown
*/ */
static AssetType getTypeByExtension(const std::string& extension); static AssetType getTypeByExtension(const std::string &extension);
}; };
} } // namespace extra2d

View File

@ -1,12 +1,10 @@
#pragma once #pragma once
#include <array>
#include <extra2d/core/module.h> #include <extra2d/core/module.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <array>
#include <typeindex> #include <typeindex>
#include <vector> #include <vector>
#include <memory>
#include <future>
namespace extra2d { namespace extra2d {
@ -18,153 +16,151 @@ class Application;
using TypeId = size_t; using TypeId = size_t;
namespace detail { namespace detail {
inline TypeId nextTypeId() { inline TypeId nextTypeId() {
static TypeId id = 0; static TypeId id = 0;
return ++id; return ++id;
}
template<typename T>
inline TypeId getTypeId() {
static TypeId id = nextTypeId();
return id;
}
} }
template <typename T> inline TypeId getTypeId() {
static TypeId id = nextTypeId();
return id;
}
} // namespace detail
/** /**
* @brief * @brief
* *
*/ */
class Registry { class Registry {
public: public:
static constexpr size_t MAX_MODULES = 64; static constexpr size_t MAX_MODULES = 64;
static Registry& instance(); static Registry &instance();
Registry(const Registry&) = delete; Registry(const Registry &) = delete;
Registry& operator=(const Registry&) = delete; Registry &operator=(const Registry &) = delete;
/** /**
* @brief * @brief
* @tparam T * @tparam T
* @tparam Args * @tparam Args
* @param args * @param args
* @return * @return
*/ */
template<typename T, typename... Args> template <typename T, typename... Args> T *use(Args &&...args) {
T* use(Args&&... args) { static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
TypeId typeId = detail::getTypeId<T>(); TypeId typeId = detail::getTypeId<T>();
// 数组查找O(n) 但 n 很小,缓存友好 // 数组查找O(n) 但 n 很小,缓存友好
for (size_t i = 0; i < moduleCount_; ++i) { for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId) { if (modules_[i].id == typeId) {
return static_cast<T*>(modules_[i].module.get()); return static_cast<T *>(modules_[i].module.get());
} }
}
// 添加新模块
if (moduleCount_ >= MAX_MODULES) {
return nullptr; // 模块数量超过上限
}
auto module = ptr::makeUnique<T>(std::forward<Args>(args)...);
T* ptr = module.get();
module->setApp(app_);
modules_[moduleCount_].id = typeId;
modules_[moduleCount_].module = std::move(module);
modules_[moduleCount_].valid = true;
++moduleCount_;
return ptr;
} }
/** // 添加新模块
* @brief if (moduleCount_ >= MAX_MODULES) {
* @tparam T return nullptr; // 模块数量超过上限
* @return nullptr
*/
template<typename T>
T* get() const {
TypeId typeId = detail::getTypeId<T>();
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId && modules_[i].valid) {
return static_cast<T*>(modules_[i].module.get());
}
}
return nullptr;
} }
/** auto module = ptr::unique<T>(std::forward<Args>(args)...);
* @brief T *ptr = module.get();
* @param typeIdx module->setApp(app_);
* @return
*/ modules_[moduleCount_].id = typeId;
Module* get(std::type_index typeIdx) const { modules_[moduleCount_].module = std::move(module);
// 这里仍然使用type_index作为后备方案 modules_[moduleCount_].valid = true;
for (size_t i = 0; i < moduleCount_; ++i) { ++moduleCount_;
if (modules_[i].valid &&
std::type_index(typeid(*modules_[i].module)) == typeIdx) { return ptr;
return modules_[i].module.get(); }
}
} /**
return nullptr; * @brief
* @tparam T
* @return nullptr
*/
template <typename T> T *get() const {
TypeId typeId = detail::getTypeId<T>();
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].id == typeId && modules_[i].valid) {
return static_cast<T *>(modules_[i].module.get());
}
} }
return nullptr;
}
/** /**
* @brief Application * @brief
*/ * @param typeIdx
void setApp(Application* app) { app_ = app; } * @return
*/
Module *get(std::type_index typeIdx) const {
for (size_t i = 0; i < moduleCount_; ++i) {
if (modules_[i].valid) {
Module *mod = modules_[i].module.get();
if (std::type_index(typeid(*mod)) == typeIdx) {
return mod;
}
}
}
return nullptr;
}
/** /**
* @brief * @brief Application
* @return true */
*/ void setApp(Application *app) { app_ = app; }
bool init();
/** /**
* @brief * @brief
*/ * @return true
void shutdown(); */
bool init();
/** /**
* @brief * @brief
*/ */
void clear(); void shutdown();
/** /**
* @brief * @brief
*/ */
size_t size() const { return moduleCount_; } void clear();
/**
* @brief
*/
size_t size() const { return moduleCount_; }
private: private:
Registry() = default; Registry() = default;
~Registry() = default; ~Registry() = default;
struct ModuleEntry { struct ModuleEntry {
TypeId id = 0; TypeId id = 0;
Unique<Module> module; Unique<Module> module;
bool valid = false; bool valid = false;
}; };
/** /**
* @brief * @brief
* @return * @return
*/ */
std::vector<Module*> sort(); std::vector<Module *> sort();
/** /**
* @brief * @brief
* *
* @return * @return
*/ */
std::vector<std::vector<Module*>> group(); std::vector<std::vector<Module *>> group();
std::array<ModuleEntry, MAX_MODULES> modules_; std::array<ModuleEntry, MAX_MODULES> modules_;
size_t moduleCount_ = 0; size_t moduleCount_ = 0;
Application* app_ = nullptr; Application *app_ = nullptr;
}; };
} // namespace extra2d } // namespace extra2d

View File

@ -3,7 +3,6 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string>
namespace extra2d { namespace extra2d {
@ -22,18 +21,18 @@ template <typename T> using Weak = std::weak_ptr<T>;
/// 智能指针工厂函数命名空间 /// 智能指针工厂函数命名空间
namespace ptr { namespace ptr {
/// 创建 Ref 的便捷函数 /// 创建 Ref 的便捷函数
template <typename T, typename... Args> inline Ref<T> make(Args &&...args) { template <typename T, typename... Args> inline Ref<T> make(Args &&...args) {
return std::make_shared<T>(std::forward<Args>(args)...); return std::make_shared<T>(std::forward<Args>(args)...);
}
/// 创建 Unique 的便捷函数
template <typename T, typename... Args>
inline Unique<T> makeUnique(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
} }
/// 创建 Unique 的便捷函数
template <typename T, typename... Args>
inline Unique<T> unique(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
} // namespace ptr
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// 函数别名 // 函数别名
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -36,11 +36,10 @@ enum class LogLevel : u8 {
Trace = 0, Trace = 0,
Debug = 1, Debug = 1,
Info = 2, Info = 2,
Registry = 3, Warn = 3,
Warn = 4, Error = 4,
Error = 5, Fatal = 5,
Fatal = 6, Off = 6
Off = 7
}; };
/** /**
@ -60,7 +59,6 @@ public:
virtual void trace(const char *fmt, ...) = 0; virtual void trace(const char *fmt, ...) = 0;
virtual void debug(const char *fmt, ...) = 0; virtual void debug(const char *fmt, ...) = 0;
virtual void info(const char *fmt, ...) = 0; virtual void info(const char *fmt, ...) = 0;
virtual void registry(const char *fmt, ...) = 0;
virtual void warn(const char *fmt, ...) = 0; virtual void warn(const char *fmt, ...) = 0;
virtual void error(const char *fmt, ...) = 0; virtual void error(const char *fmt, ...) = 0;
virtual void fatal(const char *fmt, ...) = 0; virtual void fatal(const char *fmt, ...) = 0;
@ -99,7 +97,6 @@ public:
void trace(const char *fmt, ...) override; void trace(const char *fmt, ...) override;
void debug(const char *fmt, ...) override; void debug(const char *fmt, ...) override;
void info(const char *fmt, ...) override; void info(const char *fmt, ...) override;
void registry(const char *fmt, ...) override;
void warn(const char *fmt, ...) override; void warn(const char *fmt, ...) override;
void error(const char *fmt, ...) override; void error(const char *fmt, ...) override;
void fatal(const char *fmt, ...) override; void fatal(const char *fmt, ...) override;
@ -109,11 +106,12 @@ public:
void colors(bool on) override; void colors(bool on) override;
bool colors() const override; bool colors() const override;
private:
void output(LogLevel lvl, const char *msg);
const char *levelString(LogLevel lvl); const char *levelString(LogLevel lvl);
std::string ansiColor(LogLevel lvl); std::string ansiColor(LogLevel lvl);
private:
void output(LogLevel lvl, const char *msg);
LogLevel level_ = LogLevel::Info; LogLevel level_ = LogLevel::Info;
bool colors_ = true; bool colors_ = true;
LogColor levelColors_[7]; LogColor levelColors_[7];
@ -130,8 +128,11 @@ namespace extra2d {
namespace detail { namespace detail {
template <typename T> std::string to_string(T &&value) { template <typename T> std::string to_string(T &&value) {
using D = std::decay_t<T>; using D = std::decay_t<T>;
using Raw = std::remove_reference_t<T>;
if constexpr (std::is_same_v<D, std::string>) if constexpr (std::is_same_v<D, std::string>)
return value; return value;
else if constexpr (std::is_array_v<Raw>)
return std::string(value);
else if constexpr (std::is_same_v<D, const char *>) else if constexpr (std::is_same_v<D, const char *>)
return value ? value : "(null)"; return value ? value : "(null)";
else if constexpr (std::is_same_v<D, bool>) else if constexpr (std::is_same_v<D, bool>)
@ -176,10 +177,36 @@ std::string format_str(const char *fmt, Args &&...args) {
log->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \ log->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
} while (0) } while (0)
#define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__) #define E2D_LOG_CAT(lvl, cat, ...) \
#define E2D_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__) do { \
#define E2D_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__) if (auto log = ::extra2d::ServiceLocator::instance() \
#define E2D_REGISTRY(...) E2D_LOG(::extra2d::LogLevel::Registry, __VA_ARGS__) .tryGet<::extra2d::ILogger>()) \
#define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__) if (log->enabled(lvl)) \
#define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__) log->log(lvl, ::extra2d::format_str("[{}] {}", cat, __VA_ARGS__)); \
#define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__) } while (0)
// 带类别的日志宏
#define E2D_TRACE(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Trace, cat, __VA_ARGS__)
#define E2D_DEBUG(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Debug, cat, __VA_ARGS__)
#define E2D_INFO(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Info, cat, __VA_ARGS__)
#define E2D_WARN(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Warn, cat, __VA_ARGS__)
#define E2D_ERROR(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Error, cat, __VA_ARGS__)
#define E2D_FATAL(cat, ...) \
E2D_LOG_CAT(::extra2d::LogLevel::Fatal, cat, __VA_ARGS__)
// 常用日志类别
#define CAT_APP "应用"
#define CAT_RENDER "渲染"
#define CAT_SCENE "场景"
#define CAT_INPUT "输入"
#define CAT_AUDIO "音频"
#define CAT_ASSET "资源"
#define CAT_SYSTEM "系统"
#define CAT_DATA "存档"
#define CAT_MODULES "模块系统"
#define CAT_SERVICES "服务系统"

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -202,7 +201,7 @@ public:
private: private:
class Impl; class Impl;
UniquePtr<Impl> impl_; Unique<Impl> impl_;
std::string filename_; std::string filename_;
std::string mountName_; std::string mountName_;
UserId defaultUserId_; UserId defaultUserId_;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,623 @@
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Before #including,
//
// #define STB_RECT_PACK_IMPLEMENTATION
//
// in the file that you want to have the implementation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
typedef int stbrp_coord;
#define STBRP__MAXVAL 0x7fffffff
// Mostly for internal use, but this is the maximum supported coordinate value.
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
context->extra[1].y = (1<<30);
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

View File

@ -18,13 +18,13 @@ bool Application::init() {
if (initialized_) if (initialized_)
return true; return true;
E2D_INFO("Application initializing: {}", name); E2D_INFO(CAT_APP, "Application initializing: {}", name);
ServiceLocator::instance().init(); ServiceLocator::instance().init();
window_ = new Window(); window_ = new Window();
if (!window_->create({.title = name, .width = 1280, .height = 720})) { if (!window_->create({.title = name, .width = 1280, .height = 720})) {
E2D_ERROR("Failed to create window"); E2D_ERROR(CAT_APP, "Failed to create window");
delete window_; delete window_;
window_ = nullptr; window_ = nullptr;
return false; return false;
@ -32,7 +32,7 @@ bool Application::init() {
auto &device = RenderDevice::instance(); auto &device = RenderDevice::instance();
if (!device.init(window_->sdlWindow())) { if (!device.init(window_->sdlWindow())) {
E2D_ERROR("Failed to initialize render device"); E2D_ERROR(CAT_APP, "Failed to initialize render device");
window_->destroy(); window_->destroy();
delete window_; delete window_;
window_ = nullptr; window_ = nullptr;
@ -51,7 +51,7 @@ void Application::shutdown() {
if (!initialized_) if (!initialized_)
return; return;
E2D_INFO("Application shutting down"); E2D_INFO(CAT_APP, "Application shutting down");
RenderDevice::instance().shutdown(); RenderDevice::instance().shutdown();

View File

@ -11,52 +11,53 @@ namespace extra2d {
class FontAsset::Impl { class FontAsset::Impl {
public: public:
stbtt_fontinfo info; stbtt_fontinfo info;
bool initialized = false; bool initialized = false;
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// FontAsset 实现 // FontAsset 实现
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
FontAsset::FontAsset() : impl_(ptr::makeUnique<Impl>()) {} FontAsset::FontAsset() : impl_(ptr::unique<Impl>()) {}
FontAsset::~FontAsset() = default; FontAsset::~FontAsset() = default;
bool FontAsset::loaded() const { bool FontAsset::loaded() const {
return state_.load(std::memory_order_acquire) == AssetState::Loaded && impl_->initialized; return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
impl_->initialized;
} }
float FontAsset::scaleForPixelHeight(float pixels) const { float FontAsset::scaleForPixelHeight(float pixels) const {
if (!impl_->initialized || data_.empty()) { if (!impl_->initialized || data_.empty()) {
return 0.0f; return 0.0f;
} }
return stbtt_ScaleForPixelHeight(&impl_->info, pixels); return stbtt_ScaleForPixelHeight(&impl_->info, pixels);
} }
bool FontAsset::setData(std::vector<u8> data) { bool FontAsset::setData(std::vector<u8> data) {
if (data.empty()) { if (data.empty()) {
return false; return false;
} }
data_ = std::move(data); data_ = std::move(data);
if (!stbtt_InitFont(&impl_->info, data_.data(), 0)) { if (!stbtt_InitFont(&impl_->info, data_.data(), 0)) {
data_.clear(); data_.clear();
impl_->initialized = false; impl_->initialized = false;
setState(AssetState::Failed); setState(AssetState::Failed);
return false; return false;
} }
impl_->initialized = true; impl_->initialized = true;
setState(AssetState::Loaded); setState(AssetState::Loaded);
return true; return true;
} }
void FontAsset::release() { void FontAsset::release() {
data_.clear(); data_.clear();
impl_->initialized = false; impl_->initialized = false;
setState(AssetState::Unloaded); setState(AssetState::Unloaded);
} }
} } // namespace extra2d

View File

@ -21,13 +21,13 @@ bool AudioEngine::initialize() {
// 初始化 SDL 音频子系统 // 初始化 SDL 音频子系统
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
E2D_LOG_ERROR("Failed to initialize SDL audio: {}", SDL_GetError()); E2D_ERROR(CAT_AUDIO, "Failed to initialize SDL audio: {}", SDL_GetError());
return false; return false;
} }
// 打开音频设备 // 打开音频设备
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) { if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) {
E2D_LOG_ERROR("Failed to open audio: {}", Mix_GetError()); E2D_ERROR(CAT_AUDIO, "Failed to open audio: {}", Mix_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
return false; return false;
} }
@ -36,7 +36,7 @@ bool AudioEngine::initialize() {
Mix_AllocateChannels(32); Mix_AllocateChannels(32);
initialized_ = true; initialized_ = true;
E2D_LOG_INFO("AudioEngine initialized successfully (SDL2_mixer)"); E2D_INFO(CAT_AUDIO, "AudioEngine initialized successfully (SDL2_mixer)");
return true; return true;
} }
@ -51,7 +51,7 @@ void AudioEngine::shutdown() {
SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_AUDIO);
initialized_ = false; initialized_ = false;
E2D_LOG_INFO("AudioEngine shutdown"); E2D_INFO(CAT_AUDIO, "AudioEngine shutdown");
} }
std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &filePath) { std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &filePath) {
@ -61,7 +61,7 @@ std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &filePath) {
std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &name, std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &name,
const std::string &filePath) { const std::string &filePath) {
if (!initialized_) { if (!initialized_) {
E2D_LOG_ERROR("AudioEngine not initialized"); E2D_ERROR(CAT_AUDIO, "AudioEngine not initialized");
return nullptr; return nullptr;
} }
@ -73,14 +73,14 @@ std::shared_ptr<Sound> AudioEngine::loadSound(const std::string &name,
Mix_Chunk *chunk = Mix_LoadWAV(filePath.c_str()); Mix_Chunk *chunk = Mix_LoadWAV(filePath.c_str());
if (!chunk) { if (!chunk) {
E2D_LOG_ERROR("Failed to load sound: {} ({})", filePath, Mix_GetError()); E2D_ERROR("Failed to load sound: {} ({})", filePath, Mix_GetError());
return nullptr; return nullptr;
} }
auto sound = std::shared_ptr<Sound>(new Sound(name, filePath, chunk)); auto sound = std::shared_ptr<Sound>(new Sound(name, filePath, chunk));
sounds_[name] = sound; sounds_[name] = sound;
E2D_LOG_DEBUG("Loaded sound: {}", filePath); E2D_DEBUG("Loaded sound: {}", filePath);
return sound; return sound;
} }
@ -96,14 +96,14 @@ void AudioEngine::unloadSound(const std::string &name) {
auto it = sounds_.find(name); auto it = sounds_.find(name);
if (it != sounds_.end()) { if (it != sounds_.end()) {
sounds_.erase(it); sounds_.erase(it);
E2D_LOG_DEBUG("Unloaded sound: {}", name); E2D_DEBUG(CAT_AUDIO, "Unloaded sound: {}", name);
} }
} }
void AudioEngine::unloadAllSounds() { void AudioEngine::unloadAllSounds() {
stopAll(); stopAll();
sounds_.clear(); sounds_.clear();
E2D_LOG_DEBUG("Unloaded all sounds"); E2D_DEBUG(CAT_AUDIO, "Unloaded all sounds");
} }
void AudioEngine::setMasterVolume(float volume) { void AudioEngine::setMasterVolume(float volume) {

View File

@ -21,7 +21,7 @@ Sound::~Sound() {
bool Sound::play() { bool Sound::play() {
if (!chunk_) { if (!chunk_) {
E2D_LOG_WARN("Sound::play() failed: chunk is null for {}", name_); E2D_WARN(CAT_AUDIO, "Sound::play() failed: chunk is null for {}", name_);
return false; return false;
} }
@ -29,7 +29,8 @@ bool Sound::play() {
int newChannel = Mix_PlayChannel(-1, chunk_, loops); int newChannel = Mix_PlayChannel(-1, chunk_, loops);
if (newChannel < 0) { if (newChannel < 0) {
E2D_LOG_WARN("Sound::play() failed: no free channel for {} ({})", name_, Mix_GetError()); E2D_WARN(CAT_AUDIO, "Sound::play() failed: no free channel for {} ({})", name_,
Mix_GetError());
return false; return false;
} }

View File

@ -12,8 +12,8 @@ Registry &Registry::instance() {
bool Registry::init() { bool Registry::init() {
auto levels = group(); auto levels = group();
E2D_REGISTRY("正在初始化 {} 个模块,共 {} 个层级...", moduleCount_, E2D_INFO(CAT_MODULES, "正在初始化 {} 个模块,共 {} 个层级...", moduleCount_,
levels.size()); levels.size());
for (size_t level = 0; level < levels.size(); ++level) { for (size_t level = 0; level < levels.size(); ++level) {
auto &modules = levels[level]; auto &modules = levels[level];
@ -30,19 +30,20 @@ bool Registry::init() {
// 如果只有一个模块或不支持并行,使用串行初始化 // 如果只有一个模块或不支持并行,使用串行初始化
if (modules.size() <= 1 || !hasParallelModules) { if (modules.size() <= 1 || !hasParallelModules) {
for (auto *module : modules) { for (auto *module : modules) {
E2D_REGISTRY("正在初始化模块: {} (层级 {})", module->name(), level); E2D_INFO(CAT_MODULES, "正在初始化模块: {} (层级 {})", module->name(),
level);
if (!module->init()) { if (!module->init()) {
E2D_ERROR("初始化模块失败: {}", module->name()); E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false; return false;
} }
E2D_REGISTRY("模块 {} 初始化成功", module->name()); E2D_INFO(CAT_MODULES, "模块 {} 初始化成功", module->name());
} }
} else { } else {
// 并行初始化当前层级的模块 // 并行初始化当前层级的模块
E2D_REGISTRY("正在并行初始化 {} 个模块 (层级 {})...", modules.size(), E2D_INFO(CAT_MODULES, "正在并行初始化 {} 个模块 (层级 {})...",
level); modules.size(), level);
std::vector<std::future<std::pair<Module *, bool>>> futures; std::vector<std::future<std::pair<Module *, bool>>> futures;
std::vector<Module *> serialModules; std::vector<Module *> serialModules;
@ -62,28 +63,28 @@ bool Registry::init() {
for (auto &future : futures) { for (auto &future : futures) {
auto [module, success] = future.get(); auto [module, success] = future.get();
if (!success) { if (!success) {
E2D_ERROR("初始化模块失败: {}", module->name()); E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false; return false;
} }
E2D_REGISTRY("模块 {} 初始化成功 (并行)", module->name()); E2D_INFO(CAT_MODULES, "模块 {} 初始化成功 (并行)", module->name());
} }
// 串行初始化不支持并行的模块 // 串行初始化不支持并行的模块
for (auto *module : serialModules) { for (auto *module : serialModules) {
E2D_REGISTRY("正在初始化模块: {} (串行, 层级 {})", module->name(), E2D_INFO(CAT_MODULES, "正在初始化模块: {} (串行, 层级 {})",
level); module->name(), level);
if (!module->init()) { if (!module->init()) {
E2D_ERROR("初始化模块失败: {}", module->name()); E2D_ERROR(CAT_MODULES, "初始化模块失败: {}", module->name());
return false; return false;
} }
E2D_REGISTRY("模块 {} 初始化成功", module->name()); E2D_INFO(CAT_MODULES, "模块 {} 初始化成功", module->name());
} }
} }
E2D_REGISTRY("层级 {} 初始化完成", level); E2D_INFO(CAT_MODULES, "层级 {} 初始化完成", level);
} }
E2D_REGISTRY("所有模块初始化完成"); E2D_INFO(CAT_MODULES, "所有模块初始化完成");
return true; return true;
} }

View File

@ -28,10 +28,10 @@ bool RenderDevice::init(SDL_Window *window, const RenderDeviceConfig &config) {
initialized_ = true; initialized_ = true;
E2D_INFO("RenderDevice initialized"); E2D_INFO(CAT_RENDER, "RenderDevice initialized");
E2D_INFO("OpenGL Version: {}", caps_.version); E2D_INFO(CAT_RENDER, "OpenGL Version: {}", caps_.version);
E2D_INFO("OpenGL Renderer: {}", caps_.renderer); E2D_INFO(CAT_RENDER, "OpenGL Renderer: {}", caps_.renderer);
E2D_INFO("GLSL Version: {}", caps_.glslVersion); E2D_INFO(CAT_RENDER, "GLSL Version: {}", caps_.glslVersion);
return true; return true;
} }
@ -61,12 +61,12 @@ bool RenderDevice::initGL(const RenderDeviceConfig &config) {
glContext_ = SDL_GL_CreateContext(window_); glContext_ = SDL_GL_CreateContext(window_);
if (!glContext_) { if (!glContext_) {
E2D_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError()); E2D_ERROR(CAT_RENDER, "SDL_GL_CreateContext failed: {}", SDL_GetError());
return false; return false;
} }
if (SDL_GL_MakeCurrent(window_, glContext_) != 0) { if (SDL_GL_MakeCurrent(window_, glContext_) != 0) {
E2D_ERROR("SDL_GL_MakeCurrent failed: {}", SDL_GetError()); E2D_ERROR(CAT_RENDER, "SDL_GL_MakeCurrent failed: {}", SDL_GetError());
SDL_GL_DeleteContext(glContext_); SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr; glContext_ = nullptr;
return false; return false;
@ -74,7 +74,7 @@ bool RenderDevice::initGL(const RenderDeviceConfig &config) {
if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) == if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) ==
0) { 0) {
E2D_ERROR("gladLoadGLLoader failed"); E2D_ERROR(CAT_RENDER, "gladLoadGLLoader failed");
SDL_GL_DeleteContext(glContext_); SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr; glContext_ = nullptr;
return false; return false;
@ -98,7 +98,7 @@ void RenderDevice::shutdown() {
currentProgram_ = 0; currentProgram_ = 0;
initialized_ = false; initialized_ = false;
E2D_INFO("RenderDevice shutdown"); E2D_INFO(CAT_RENDER, "RenderDevice shutdown");
} }
void RenderDevice::swapBuffers() { void RenderDevice::swapBuffers() {

View File

@ -15,7 +15,7 @@ void SceneManager::runWithScene(Ref<Scene> scene) {
} }
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
E2D_WARN("SceneManager: runWithScene should only be called once"); E2D_WARN(CAT_SCENE, "SceneManager: runWithScene should only be called once");
return; return;
} }
@ -178,18 +178,18 @@ void SceneManager::render(Renderer& renderer) {
clearColor = sceneStack_.top()->getBackgroundColor(); clearColor = sceneStack_.top()->getBackgroundColor();
} }
E2D_TRACE("SceneManager::render - beginFrame"); E2D_TRACE(CAT_SCENE, "SceneManager::render - beginFrame");
renderer.beginFrame(clearColor); renderer.beginFrame(clearColor);
if (!sceneStack_.empty()) { if (!sceneStack_.empty()) {
E2D_TRACE("SceneManager::render - rendering scene content"); E2D_TRACE(CAT_SCENE, "SceneManager::render - rendering scene content");
sceneStack_.top()->renderContent(renderer); sceneStack_.top()->renderContent(renderer);
} else { } else {
E2D_WARN("SceneManager::render - no scene to render"); E2D_WARN(CAT_SCENE, "SceneManager::render - no scene to render");
} }
renderer.endFrame(); renderer.endFrame();
E2D_TRACE("SceneManager::render - endFrame"); E2D_TRACE(CAT_SCENE, "SceneManager::render - endFrame");
} }
void SceneManager::end() { void SceneManager::end() {

View File

@ -8,7 +8,7 @@ namespace extra2d {
// AssetService 实现 // AssetService 实现
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
AssetService::AssetService() : cache_(ptr::makeUnique<AssetCache>()) {} AssetService::AssetService() : cache_(ptr::unique<AssetCache>()) {}
AssetService::~AssetService() { shutdown(); } AssetService::~AssetService() { shutdown(); }

View File

@ -1,267 +1,288 @@
#include <SDL.h>
#include <cstdarg> #include <cstdarg>
#include <cstdio> #include <cstdio>
#include <cstring>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <mutex> #include <mutex>
#include <SDL.h>
namespace extra2d { namespace extra2d {
class ConsoleLogger::Impl { class ConsoleLogger::Impl {
public: public:
std::mutex mutex_; std::mutex mutex_;
SDL_LogPriority sdlPriorities_[7]; SDL_LogPriority sdlPriorities_[7];
bool customOutputSet_ = false; bool customOutputSet_ = false;
}; };
static SDL_LogPriority toSDLPriority(LogLevel lvl) { static SDL_LogPriority toSDL(LogLevel lvl) {
switch (lvl) { switch (lvl) {
case LogLevel::Trace: return SDL_LOG_PRIORITY_VERBOSE; case LogLevel::Trace:
case LogLevel::Debug: return SDL_LOG_PRIORITY_DEBUG; return SDL_LOG_PRIORITY_VERBOSE;
case LogLevel::Info: return SDL_LOG_PRIORITY_INFO; case LogLevel::Debug:
case LogLevel::Registry: return SDL_LOG_PRIORITY_INFO; return SDL_LOG_PRIORITY_DEBUG;
case LogLevel::Warn: return SDL_LOG_PRIORITY_WARN; case LogLevel::Info:
case LogLevel::Error: return SDL_LOG_PRIORITY_ERROR; return SDL_LOG_PRIORITY_INFO;
case LogLevel::Fatal: return SDL_LOG_PRIORITY_CRITICAL; case LogLevel::Warn:
default: return SDL_LOG_PRIORITY_INFO; return SDL_LOG_PRIORITY_WARN;
} case LogLevel::Error:
return SDL_LOG_PRIORITY_ERROR;
case LogLevel::Fatal:
return SDL_LOG_PRIORITY_CRITICAL;
default:
return SDL_LOG_PRIORITY_INFO;
}
} }
static LogLevel fromSDLPriority(SDL_LogPriority priority) { static LogLevel fromSDL(SDL_LogPriority priority) {
switch (priority) { switch (priority) {
case SDL_LOG_PRIORITY_VERBOSE: return LogLevel::Trace; case SDL_LOG_PRIORITY_VERBOSE:
case SDL_LOG_PRIORITY_DEBUG: return LogLevel::Debug; return LogLevel::Trace;
case SDL_LOG_PRIORITY_INFO: return LogLevel::Info; case SDL_LOG_PRIORITY_DEBUG:
case SDL_LOG_PRIORITY_WARN: return LogLevel::Warn; return LogLevel::Debug;
case SDL_LOG_PRIORITY_ERROR: return LogLevel::Error; case SDL_LOG_PRIORITY_INFO:
case SDL_LOG_PRIORITY_CRITICAL: return LogLevel::Fatal; return LogLevel::Info;
default: return LogLevel::Info; case SDL_LOG_PRIORITY_WARN:
} return LogLevel::Warn;
case SDL_LOG_PRIORITY_ERROR:
return LogLevel::Error;
case SDL_LOG_PRIORITY_CRITICAL:
return LogLevel::Fatal;
default:
return LogLevel::Info;
}
} }
static ConsoleLogger* g_loggerInstance = nullptr; static ConsoleLogger *g_loggerInstance = nullptr;
static void SDLCALL sdlLogOutputFunction(void* userdata, int category, /**
SDL_LogPriority priority, * @brief
const char* message) { */
if (!g_loggerInstance) return; static bool parseCategory(const char *msg, std::string &cat,
const char *&content) {
cat = "应用";
content = msg;
ConsoleLogger* logger = static_cast<ConsoleLogger*>(g_loggerInstance); if (msg[0] == '[') {
LogLevel lvl = fromSDLPriority(priority); const char *end = strchr(msg + 1, ']');
if (end && end > msg + 1) {
if (!logger->enabled(lvl)) return; size_t len = end - msg - 1;
if (len > 0 && len < 20) {
const char* levelStr = logger->levelString(lvl); cat = std::string(msg + 1, len);
const char* categoryStr = ""; content = end + 1;
switch (category) { while (*content == ' ')
case SDL_LOG_CATEGORY_APPLICATION: categoryStr = "APP"; break; content++;
case SDL_LOG_CATEGORY_ERROR: categoryStr = "ERR"; break; return true;
case SDL_LOG_CATEGORY_ASSERT: categoryStr = "ASR"; break; }
case SDL_LOG_CATEGORY_SYSTEM: categoryStr = "SYS"; break;
case SDL_LOG_CATEGORY_AUDIO: categoryStr = "AUD"; break;
case SDL_LOG_CATEGORY_VIDEO: categoryStr = "VID"; break;
case SDL_LOG_CATEGORY_RENDER: categoryStr = "RDR"; break;
case SDL_LOG_CATEGORY_INPUT: categoryStr = "INP"; break;
case SDL_LOG_CATEGORY_TEST: categoryStr = "TST"; break;
default: categoryStr = "GEN"; break;
} }
}
return false;
}
Uint32 ticks = SDL_GetTicks(); static void SDLCALL logOutput(void *userdata, int category,
Uint32 seconds = ticks / 1000; SDL_LogPriority priority, const char *message) {
Uint32 minutes = seconds / 60; if (!g_loggerInstance)
Uint32 hours = minutes / 60; return;
if (logger->colors()) { ConsoleLogger *logger = static_cast<ConsoleLogger *>(g_loggerInstance);
std::string color = logger->ansiColor(lvl); LogLevel lvl = fromSDL(priority);
const char* reset = "\033[0m";
printf("%s[%02u:%02u:%02u.%03u] [%s][%s] %s%s\n", if (!logger->enabled(lvl))
color.c_str(), return;
hours % 24, minutes % 60, seconds % 60, ticks % 1000,
levelStr, categoryStr, message, reset); const char *levelStr = logger->levelString(lvl);
} else {
printf("[%02u:%02u:%02u.%03u] [%s][%s] %s\n", // 解析类别和内容
hours % 24, minutes % 60, seconds % 60, ticks % 1000, std::string cat;
levelStr, categoryStr, message); const char *content;
} parseCategory(message, cat, content);
Uint32 ticks = SDL_GetTicks();
Uint32 seconds = ticks / 1000;
Uint32 minutes = seconds / 60;
Uint32 hours = minutes / 60;
if (logger->colors()) {
std::string color = logger->ansiColor(lvl);
const char *reset = "\033[0m";
printf("%s[%02u:%02u:%02u.%03u] [%s][%s] %s%s\n", color.c_str(), hours % 24,
minutes % 60, seconds % 60, ticks % 1000, levelStr, cat.c_str(),
content, reset);
} else {
printf("[%02u:%02u:%02u.%03u] [%s][%s] %s\n", hours % 24, minutes % 60,
seconds % 60, ticks % 1000, levelStr, cat.c_str(), content);
}
} }
ConsoleLogger::ConsoleLogger() ConsoleLogger::ConsoleLogger()
: level_(LogLevel::Info), colors_(true), : level_(LogLevel::Info), colors_(true), impl_(std::make_unique<Impl>()) {
impl_(std::make_unique<Impl>()) { info_.name = "ConsoleLogger";
info_.name = "ConsoleLogger"; info_.priority = ServicePriority::Core;
info_.priority = ServicePriority::Core;
levelColors_[static_cast<int>(LogLevel::Trace)] = LogColor::Gray(); levelColors_[static_cast<int>(LogLevel::Trace)] = LogColor::Gray();
levelColors_[static_cast<int>(LogLevel::Debug)] = LogColor::Cyan(); levelColors_[static_cast<int>(LogLevel::Debug)] = LogColor::Cyan();
levelColors_[static_cast<int>(LogLevel::Info)] = LogColor::SkyLight(); levelColors_[static_cast<int>(LogLevel::Info)] = LogColor::SkyLight();
levelColors_[static_cast<int>(LogLevel::Registry)] = LogColor::IndigoLight(); levelColors_[static_cast<int>(LogLevel::Warn)] = LogColor::Yellow();
levelColors_[static_cast<int>(LogLevel::Warn)] = LogColor::Yellow(); levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red();
levelColors_[static_cast<int>(LogLevel::Error)] = LogColor::Red(); levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta();
levelColors_[static_cast<int>(LogLevel::Fatal)] = LogColor::Magenta();
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
impl_->sdlPriorities_[i] = toSDLPriority(static_cast<LogLevel>(i)); impl_->sdlPriorities_[i] = toSDL(static_cast<LogLevel>(i));
} }
} }
ConsoleLogger::~ConsoleLogger() { ConsoleLogger::~ConsoleLogger() {
if (impl_->customOutputSet_) { if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr); SDL_LogSetOutputFunction(nullptr, nullptr);
} }
g_loggerInstance = nullptr; g_loggerInstance = nullptr;
} }
bool ConsoleLogger::init() { bool ConsoleLogger::init() {
g_loggerInstance = this; g_loggerInstance = this;
SDL_LogSetOutputFunction(sdlLogOutputFunction, this); SDL_LogSetOutputFunction(logOutput, this);
impl_->customOutputSet_ = true; impl_->customOutputSet_ = true;
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDLPriority(level_)); SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, toSDL(level_));
setState(ServiceState::Running); setState(ServiceState::Running);
return true; return true;
} }
void ConsoleLogger::shutdown() { void ConsoleLogger::shutdown() {
if (impl_->customOutputSet_) { if (impl_->customOutputSet_) {
SDL_LogSetOutputFunction(nullptr, nullptr); SDL_LogSetOutputFunction(nullptr, nullptr);
impl_->customOutputSet_ = false; impl_->customOutputSet_ = false;
} }
g_loggerInstance = nullptr; g_loggerInstance = nullptr;
setState(ServiceState::Stopped); setState(ServiceState::Stopped);
} }
void ConsoleLogger::level(LogLevel lvl) { void ConsoleLogger::level(LogLevel lvl) {
level_ = lvl; level_ = lvl;
SDL_LogPriority priority = toSDLPriority(lvl); SDL_LogPriority priority = toSDL(lvl);
SDL_LogSetAllPriority(priority); SDL_LogSetAllPriority(priority);
} }
LogLevel ConsoleLogger::level() const { LogLevel ConsoleLogger::level() const { return level_; }
return level_;
}
bool ConsoleLogger::enabled(LogLevel lvl) const { bool ConsoleLogger::enabled(LogLevel lvl) const {
return static_cast<int>(lvl) >= static_cast<int>(level_); return static_cast<int>(lvl) >= static_cast<int>(level_);
} }
void ConsoleLogger::log(LogLevel lvl, const char *fmt, ...) { void ConsoleLogger::log(LogLevel lvl, const char *fmt, ...) {
if (!enabled(lvl)) return; if (!enabled(lvl))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogPriority priority = toSDLPriority(lvl); SDL_LogPriority priority = toSDL(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", buffer); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", buffer);
} }
void ConsoleLogger::log(LogLevel lvl, const std::string &msg) { void ConsoleLogger::log(LogLevel lvl, const std::string &msg) {
if (!enabled(lvl)) return; if (!enabled(lvl))
return;
SDL_LogPriority priority = toSDLPriority(lvl); SDL_LogPriority priority = toSDL(lvl);
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str()); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, priority, "%s", msg.c_str());
} }
void ConsoleLogger::trace(const char *fmt, ...) { void ConsoleLogger::trace(const char *fmt, ...) {
if (!enabled(LogLevel::Trace)) return; if (!enabled(LogLevel::Trace))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogVerbose(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::debug(const char *fmt, ...) { void ConsoleLogger::debug(const char *fmt, ...) {
if (!enabled(LogLevel::Debug)) return; if (!enabled(LogLevel::Debug))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::info(const char *fmt, ...) { void ConsoleLogger::info(const char *fmt, ...) {
if (!enabled(LogLevel::Info)) return; if (!enabled(LogLevel::Info))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
}
void ConsoleLogger::registry(const char *fmt, ...) {
if (!enabled(LogLevel::Registry)) return;
char buffer[2048];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[REGISTRY] %s", buffer);
} }
void ConsoleLogger::warn(const char *fmt, ...) { void ConsoleLogger::warn(const char *fmt, ...) {
if (!enabled(LogLevel::Warn)) return; if (!enabled(LogLevel::Warn))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::error(const char *fmt, ...) { void ConsoleLogger::error(const char *fmt, ...) {
if (!enabled(LogLevel::Error)) return; if (!enabled(LogLevel::Error))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::fatal(const char *fmt, ...) { void ConsoleLogger::fatal(const char *fmt, ...) {
if (!enabled(LogLevel::Fatal)) return; if (!enabled(LogLevel::Fatal))
return;
char buffer[2048]; char buffer[2048];
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args); vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args); va_end(args);
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer); SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", buffer);
} }
void ConsoleLogger::levelColor(LogLevel lvl, const LogColor &c) { void ConsoleLogger::levelColor(LogLevel lvl, const LogColor &c) {
int idx = static_cast<int>(lvl); int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) { if (idx >= 0 && idx < 7) {
levelColors_[idx] = c; levelColors_[idx] = c;
} }
} }
LogColor ConsoleLogger::levelColor(LogLevel lvl) const { LogColor ConsoleLogger::levelColor(LogLevel lvl) const {
int idx = static_cast<int>(lvl); int idx = static_cast<int>(lvl);
if (idx >= 0 && idx < 7) { if (idx >= 0 && idx < 7) {
return levelColors_[idx]; return levelColors_[idx];
} }
return LogColor::White(); return LogColor::White();
} }
void ConsoleLogger::colors(bool on) { colors_ = on; } void ConsoleLogger::colors(bool on) { colors_ = on; }
@ -269,23 +290,29 @@ void ConsoleLogger::colors(bool on) { colors_ = on; }
bool ConsoleLogger::colors() const { return colors_; } bool ConsoleLogger::colors() const { return colors_; }
std::string ConsoleLogger::ansiColor(LogLevel lvl) { std::string ConsoleLogger::ansiColor(LogLevel lvl) {
const LogColor &c = levelColor(lvl); const LogColor &c = levelColor(lvl);
char buf[32]; char buf[32];
snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", c.r, c.g, c.b); snprintf(buf, sizeof(buf), "\033[38;2;%d;%d;%dm", c.r, c.g, c.b);
return std::string(buf); return std::string(buf);
} }
const char *ConsoleLogger::levelString(LogLevel lvl) { const char *ConsoleLogger::levelString(LogLevel lvl) {
switch (lvl) { switch (lvl) {
case LogLevel::Trace: return "TRACE"; case LogLevel::Trace:
case LogLevel::Debug: return "DEBUG"; return "TRACE";
case LogLevel::Info: return "INFO"; case LogLevel::Debug:
case LogLevel::Registry: return "REGISTRY"; return "DEBUG";
case LogLevel::Warn: return "WARN"; case LogLevel::Info:
case LogLevel::Error: return "ERROR"; return "INFO";
case LogLevel::Fatal: return "FATAL"; case LogLevel::Warn:
default: return "UNKNOWN"; return "WARN";
} case LogLevel::Error:
return "ERROR";
case LogLevel::Fatal:
return "FATAL";
default:
return "UNKNOWN";
}
} }
} // namespace extra2d } // namespace extra2d

View File

@ -1,5 +1,5 @@
#include <extra2d/utils/data.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <extra2d/utils/data.h>
#include <simpleini/SimpleIni.h> #include <simpleini/SimpleIni.h>
// Switch 平台特定头文件 // Switch 平台特定头文件
@ -15,7 +15,7 @@ public:
CSimpleIniA ini; CSimpleIniA ini;
}; };
DataStore::DataStore() : impl_(makeUnique<Impl>()) {} DataStore::DataStore() : impl_(ptr::unique<Impl>()) {}
DataStore::~DataStore() { DataStore::~DataStore() {
// 如果在事务中,尝试提交 // 如果在事务中,尝试提交
@ -48,7 +48,7 @@ bool DataStore::save(const std::string &filename) {
const std::string &targetFile = filename.empty() ? filename_ : filename; const std::string &targetFile = filename.empty() ? filename_ : filename;
if (targetFile.empty()) { if (targetFile.empty()) {
E2D_LOG_ERROR("DataStore::save: 没有指定文件名"); E2D_ERROR(CAT_APP, "DataStore::save: 没有指定文件名");
return false; return false;
} }
@ -58,7 +58,7 @@ bool DataStore::save(const std::string &filename) {
bool DataStore::internalSave(const std::string &filename) { bool DataStore::internalSave(const std::string &filename) {
SI_Error rc = impl_->ini.SaveFile(filename.c_str()); SI_Error rc = impl_->ini.SaveFile(filename.c_str());
if (rc < 0) { if (rc < 0) {
E2D_LOG_ERROR("DataStore::save: 保存文件失败: {}", filename); E2D_ERROR(CAT_DATA, "DataStore::save: 保存文件失败: {}", filename);
return false; return false;
} }
dirty_ = false; dirty_ = false;
@ -87,7 +87,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
uid.uid[0] = currentUid.uid[0]; uid.uid[0] = currentUid.uid[0];
uid.uid[1] = currentUid.uid[1]; uid.uid[1] = currentUid.uid[1];
if (uid.uid[0] == 0 && uid.uid[1] == 0) { if (uid.uid[0] == 0 && uid.uid[1] == 0) {
E2D_LOG_ERROR("DataStore::mountSaveData: 无法获取当前用户ID"); E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 无法获取当前用户ID");
return false; return false;
} }
} }
@ -98,7 +98,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
rc = fsdevMountSaveData(mountName.c_str(), applicationId, uid); rc = fsdevMountSaveData(mountName.c_str(), applicationId, uid);
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::mountSaveData: 挂载失败: 0x{:X}", rc); E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 挂载失败: 0x{:X}", rc);
return false; return false;
} }
@ -106,7 +106,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
saveDataMounted_ = true; saveDataMounted_ = true;
defaultUserId_ = UserId{uid.uid[0], uid.uid[1]}; defaultUserId_ = UserId{uid.uid[0], uid.uid[1]};
E2D_LOG_INFO("DataStore::mountSaveData: 成功挂载存档: {}", mountName); E2D_INFO(CAT_DATA, "DataStore::mountSaveData: 成功挂载存档: {}", mountName);
return true; return true;
} }
@ -124,22 +124,22 @@ void DataStore::unmountSaveData(const std::string &mountName) {
saveDataMounted_ = false; saveDataMounted_ = false;
mountName_.clear(); mountName_.clear();
E2D_LOG_INFO("DataStore::unmountSaveData: 已卸载存档"); E2D_INFO("DataStore::unmountSaveData: 已卸载存档");
} }
bool DataStore::commitSaveData(const std::string &mountName) { bool DataStore::commitSaveData(const std::string &mountName) {
if (!saveDataMounted_) { if (!saveDataMounted_) {
E2D_LOG_WARN("DataStore::commitSaveData: 存档未挂载"); E2D_WARN(CAT_DATA, "DataStore::commitSaveData: 存档未挂载");
return false; return false;
} }
Result rc = fsdevCommitDevice(mountName.c_str()); Result rc = fsdevCommitDevice(mountName.c_str());
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc); E2D_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc);
return false; return false;
} }
E2D_LOG_DEBUG("DataStore::commitSaveData: 提交成功"); E2D_DEBUG("DataStore::commitSaveData: 提交成功");
return true; return true;
} }
@ -155,8 +155,9 @@ UserId DataStore::getCurrentUserId() {
Result rc = accountInitialize(AccountServiceType_Application); Result rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
E2D_LOG_ERROR("DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}", E2D_ERROR(CAT_DATA,
rc); "DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}",
rc);
return result; return result;
} }
@ -167,10 +168,10 @@ UserId DataStore::getCurrentUserId() {
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
result.uid[0] = uid.uid[0]; result.uid[0] = uid.uid[0];
result.uid[1] = uid.uid[1]; result.uid[1] = uid.uid[1];
E2D_LOG_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}", E2D_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}",
result.uid[1], result.uid[0]); result.uid[1], result.uid[0]);
} else { } else {
E2D_LOG_ERROR("DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc); E2D_ERROR(CAT_DATA, "DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc);
} }
return result; return result;
@ -185,7 +186,7 @@ bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
(void)type; (void)type;
(void)userId; (void)userId;
(void)mountName; (void)mountName;
E2D_LOG_WARN("DataStore::mountSaveData: 非 Switch 平台,存档功能不可用"); E2D_WARN(CAT_DATA, "非 Switch 平台,存档功能不可用");
return false; return false;
} }
@ -203,9 +204,7 @@ std::string DataStore::getSaveDataPath(const std::string &path) const {
return path; return path;
} }
UserId DataStore::getCurrentUserId() { UserId DataStore::getCurrentUserId() { return UserId(); }
return UserId();
}
#endif #endif
@ -329,19 +328,19 @@ void DataStore::clear() {
void DataStore::beginTransaction() { void DataStore::beginTransaction() {
if (inTransaction_) { if (inTransaction_) {
E2D_LOG_WARN("DataStore::beginTransaction: 已经处于事务中"); E2D_WARN(CAT_DATA, "DataStore::beginTransaction: 已经处于事务中");
return; return;
} }
inTransaction_ = true; inTransaction_ = true;
dirty_ = false; dirty_ = false;
E2D_LOG_DEBUG("DataStore::beginTransaction: 事务开始"); E2D_DEBUG(CAT_DATA, "DataStore::beginTransaction: 事务开始");
} }
bool DataStore::commit() { bool DataStore::commit() {
if (!inTransaction_) { if (!inTransaction_) {
E2D_LOG_WARN("DataStore::commit: 不在事务中"); E2D_WARN(CAT_DATA, "DataStore::commit: 不在事务中");
return false; return false;
} }
@ -358,13 +357,14 @@ bool DataStore::commit() {
inTransaction_ = false; inTransaction_ = false;
E2D_LOG_DEBUG("DataStore::commit: 事务提交 {}", result ? "成功" : "失败"); E2D_DEBUG(CAT_DATA, "DataStore::commit: 事务提交 {}",
result ? "成功" : "失败");
return result; return result;
} }
void DataStore::rollback() { void DataStore::rollback() {
if (!inTransaction_) { if (!inTransaction_) {
E2D_LOG_WARN("DataStore::rollback: 不在事务中"); E2D_WARN(CAT_DATA, "DataStore::rollback: 不在事务中");
return; return;
} }
@ -373,7 +373,8 @@ void DataStore::rollback() {
impl_->ini.Reset(); impl_->ini.Reset();
SI_Error rc = impl_->ini.LoadFile(filename_.c_str()); SI_Error rc = impl_->ini.LoadFile(filename_.c_str());
if (rc < 0) { if (rc < 0) {
E2D_LOG_ERROR("DataStore::rollback: 重新加载文件失败: {}", filename_); E2D_ERROR(CAT_DATA, "DataStore::rollback: 重新加载文件失败: {}",
filename_);
} }
} else { } else {
// 如果没有文件名,清空数据 // 如果没有文件名,清空数据
@ -383,7 +384,7 @@ void DataStore::rollback() {
inTransaction_ = false; inTransaction_ = false;
dirty_ = false; dirty_ = false;
E2D_LOG_DEBUG("DataStore::rollback: 事务已回滚"); E2D_DEBUG(CAT_DATA, "DataStore::rollback: 事务已回滚");
} }
// ============================================================================ // ============================================================================
@ -402,7 +403,8 @@ std::vector<std::string> DataStore::getAllSections() const {
return sections; return sections;
} }
std::vector<std::string> DataStore::getAllKeys(const std::string &section) const { std::vector<std::string>
DataStore::getAllKeys(const std::string &section) const {
std::vector<std::string> keys; std::vector<std::string> keys;
CSimpleIniA::TNamesDepend keyList; CSimpleIniA::TNamesDepend keyList;
impl_->ini.GetAllKeys(section.c_str(), keyList); impl_->ini.GetAllKeys(section.c_str(), keyList);
@ -416,7 +418,7 @@ std::vector<std::string> DataStore::getAllKeys(const std::string &section) const
bool DataStore::loadFromSave(const std::string &path) { bool DataStore::loadFromSave(const std::string &path) {
if (!saveDataMounted_) { if (!saveDataMounted_) {
E2D_LOG_ERROR("DataStore::loadFromSave: 存档未挂载"); E2D_ERROR(CAT_DATA, "DataStore::loadFromSave: 存档未挂载");
return false; return false;
} }
@ -426,7 +428,7 @@ bool DataStore::loadFromSave(const std::string &path) {
bool DataStore::saveToSave(const std::string &path) { bool DataStore::saveToSave(const std::string &path) {
if (!saveDataMounted_) { if (!saveDataMounted_) {
E2D_LOG_ERROR("DataStore::saveToSave: 存档未挂载"); E2D_ERROR(CAT_DATA, "DataStore::saveToSave: 存档未挂载");
return false; return false;
} }

View File

@ -25,7 +25,7 @@ public:
* @brief 场景进入时调用 * @brief 场景进入时调用
*/ */
void onEnter() override { void onEnter() override {
E2D_LOG_INFO("HelloWorldScene::onEnter - 进入场景"); E2D_INFO("HelloWorldScene::onEnter - 进入场景");
// 设置背景颜色为深蓝色 // 设置背景颜色为深蓝色
setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f)); setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f));
@ -35,7 +35,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 48, true); font_ = resources.loadFont("assets/font.ttf", 48, true);
if (!font_) { if (!font_) {
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!"); E2D_ERROR("字体加载失败,文字渲染将不可用!");
return; return;
} }
@ -68,7 +68,7 @@ public:
// 使用手柄 START 按钮退出 (GamepadButton::Start) // 使用手柄 START 按钮退出 (GamepadButton::Start)
if (input.isButtonPressed(GamepadButton::Start)) { if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用 (START 按钮)"); E2D_INFO("退出应用 (START 按钮)");
Application::instance().quit(); Application::instance().quit();
} }
} }
@ -100,7 +100,7 @@ int main(int argc, char **argv)
// 初始化应用 // 初始化应用
if (!app.init(config)) { if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!"); E2D_ERROR("应用初始化失败!");
return -1; return -1;
} }
@ -203,10 +203,10 @@ auto texture = resources.loadTexture("assets/image.png");
使用宏进行日志输出: 使用宏进行日志输出:
```cpp ```cpp
E2D_LOG_DEBUG("调试信息"); E2D_DEBUG("调试信息");
E2D_LOG_INFO("普通信息"); E2D_INFO("普通信息");
E2D_LOG_WARN("警告信息"); E2D_WARN("警告信息");
E2D_LOG_ERROR("错误信息"); E2D_ERROR("错误信息");
``` ```
## 下一步 ## 下一步

View File

@ -286,7 +286,7 @@ button->setBackgroundColor(
// 设置点击回调 // 设置点击回调
button->setOnClick([]() { button->setOnClick([]() {
E2D_LOG_INFO("按钮被点击!"); E2D_INFO("按钮被点击!");
}); });
addChild(button); addChild(button);
@ -661,7 +661,7 @@ node->runAction(loop);
auto action = makePtr<MoveTo>(2.0f, Vec2(100, 100)); auto action = makePtr<MoveTo>(2.0f, Vec2(100, 100));
action->setProgressCallback([](float progress) { action->setProgressCallback([](float progress) {
// progress: 0.0 - 1.0 // progress: 0.0 - 1.0
E2D_LOG_INFO("动画进度: {}%", progress * 100); E2D_INFO("动画进度: {}%", progress * 100);
}); });
node->runAction(action); node->runAction(action);
``` ```

View File

@ -281,14 +281,14 @@ void ResLoader::init() {
// 加载图集纹理 // 加载图集纹理
atlasTexture_ = resources.loadTexture("assets/images/atlas.png"); atlasTexture_ = resources.loadTexture("assets/images/atlas.png");
if (!atlasTexture_) { if (!atlasTexture_) {
E2D_LOG_ERROR("无法加载图集纹理 atlas.png"); E2D_ERROR("无法加载图集纹理 atlas.png");
return; return;
} }
// 加载 JSON 文件 // 加载 JSON 文件
std::string jsonContent = resources.loadJsonFile("assets/images/atlas.json"); std::string jsonContent = resources.loadJsonFile("assets/images/atlas.json");
if (jsonContent.empty()) { if (jsonContent.empty()) {
E2D_LOG_ERROR("无法加载 atlas.json 文件"); E2D_ERROR("无法加载 atlas.json 文件");
return; return;
} }
@ -307,9 +307,9 @@ void ResLoader::init() {
imageMap_[name] = info; imageMap_[name] = info;
} }
E2D_LOG_INFO("成功加载 {} 个精灵帧", imageMap_.size()); E2D_INFO("成功加载 {} 个精灵帧", imageMap_.size());
} catch (const std::exception &e) { } catch (const std::exception &e) {
E2D_LOG_ERROR("解析 atlas.json 失败: {}", e.what()); E2D_ERROR("解析 atlas.json 失败: {}", e.what());
return; return;
} }
@ -320,14 +320,14 @@ void ResLoader::init() {
soundMap_[MusicType::Point] = resources.loadSound("assets/sound/point.wav"); soundMap_[MusicType::Point] = resources.loadSound("assets/sound/point.wav");
soundMap_[MusicType::Swoosh] = resources.loadSound("assets/sound/swoosh.wav"); soundMap_[MusicType::Swoosh] = resources.loadSound("assets/sound/swoosh.wav");
E2D_LOG_INFO("资源加载完成"); E2D_INFO("资源加载完成");
} }
extra2d::Ptr<extra2d::SpriteFrame> extra2d::Ptr<extra2d::SpriteFrame>
ResLoader::getKeyFrame(const std::string &name) { ResLoader::getKeyFrame(const std::string &name) {
auto it = imageMap_.find(name); auto it = imageMap_.find(name);
if (it == imageMap_.end()) { if (it == imageMap_.end()) {
E2D_LOG_WARN("找不到精灵帧: %s", name.c_str()); E2D_WARN("找不到精灵帧: %s", name.c_str());
return nullptr; return nullptr;
} }

View File

@ -132,7 +132,7 @@ public:
// 创建100个碰撞节点 // 创建100个碰撞节点
createPhysicsNodes(100); createPhysicsNodes(100);
E2D_LOG_INFO("空间索引已启用: {}", isSpatialIndexingEnabled()); E2D_INFO("空间索引已启用: {}", isSpatialIndexingEnabled());
} }
void onUpdate(float dt) override { void onUpdate(float dt) override {
@ -184,10 +184,10 @@ private:
if (currentStrategy == SpatialStrategy::QuadTree) { if (currentStrategy == SpatialStrategy::QuadTree) {
spatialManager.setStrategy(SpatialStrategy::SpatialHash); spatialManager.setStrategy(SpatialStrategy::SpatialHash);
E2D_LOG_INFO("切换到空间哈希策略"); E2D_INFO("切换到空间哈希策略");
} else { } else {
spatialManager.setStrategy(SpatialStrategy::QuadTree); spatialManager.setStrategy(SpatialStrategy::QuadTree);
E2D_LOG_INFO("切换到四叉树策略"); E2D_INFO("切换到四叉树策略");
} }
} }

View File

@ -70,7 +70,7 @@ button->setBorder(Colors::White, 2.0f);
// 设置点击回调 // 设置点击回调
button->setOnClick([]() { button->setOnClick([]() {
E2D_LOG_INFO("按钮被点击!"); E2D_INFO("按钮被点击!");
}); });
addChild(button); addChild(button);
@ -146,12 +146,12 @@ toggleBtn->toggle();
// 状态改变回调 // 状态改变回调
toggleBtn->setOnStateChange([](bool isOn) { toggleBtn->setOnStateChange([](bool isOn) {
E2D_LOG_INFO("切换按钮状态: {}", isOn ? "开启" : "关闭"); E2D_INFO("切换按钮状态: {}", isOn ? "开启" : "关闭");
}); });
// 点击回调(切换模式也会触发此回调) // 点击回调(切换模式也会触发此回调)
toggleBtn->setOnClick([]() { toggleBtn->setOnClick([]() {
E2D_LOG_INFO("按钮被点击"); E2D_INFO("按钮被点击");
}); });
``` ```
@ -384,7 +384,7 @@ checkBox->setTextColor(Colors::White);
// 状态改变回调 // 状态改变回调
checkBox->setOnStateChange([](bool checked) { checkBox->setOnStateChange([](bool checked) {
E2D_LOG_INFO("复选框状态: {}", checked); E2D_INFO("复选框状态: {}", checked);
}); });
addChild(checkBox); addChild(checkBox);
@ -441,7 +441,7 @@ group->addButton(radio3.get());
// 选择改变回调 // 选择改变回调
group->setOnSelectionChange([](RadioButton* selected) { group->setOnSelectionChange([](RadioButton* selected) {
if (selected) { if (selected) {
E2D_LOG_INFO("选中: {}", selected->getLabel()); E2D_INFO("选中: {}", selected->getLabel());
} }
}); });
@ -489,15 +489,15 @@ auto slider = Slider::create(0.0f, 100.0f, 50.0f);
// 值改变回调 // 值改变回调
slider->setOnValueChange([](float value) { slider->setOnValueChange([](float value) {
E2D_LOG_INFO("滑块值: {}", value); E2D_INFO("滑块值: {}", value);
}); });
// 拖动开始/结束回调 // 拖动开始/结束回调
slider->setOnDragStart([]() { slider->setOnDragStart([]() {
E2D_LOG_INFO("开始拖动"); E2D_INFO("开始拖动");
}); });
slider->setOnDragEnd([]() { slider->setOnDragEnd([]() {
E2D_LOG_INFO("结束拖动"); E2D_INFO("结束拖动");
}); });
addChild(slider); addChild(slider);
@ -643,7 +643,7 @@ public:
soundToggle_->setStateBackgroundImage(soundOff, soundOn); soundToggle_->setStateBackgroundImage(soundOff, soundOn);
soundToggle_->setOn(true); soundToggle_->setOn(true);
soundToggle_->setOnStateChange([](bool isOn) { soundToggle_->setOnStateChange([](bool isOn) {
E2D_LOG_INFO("音效: {}", isOn ? "开启" : "关闭"); E2D_INFO("音效: {}", isOn ? "开启" : "关闭");
AudioManager::instance().setEnabled(isOn); AudioManager::instance().setEnabled(isOn);
}); });
addChild(soundToggle_); addChild(soundToggle_);
@ -657,7 +657,7 @@ public:
volumeSlider_->setPosition(Vec2(350, 280)); volumeSlider_->setPosition(Vec2(350, 280));
volumeSlider_->setSize(200, 20); volumeSlider_->setSize(200, 20);
volumeSlider_->setOnValueChange([](float value) { volumeSlider_->setOnValueChange([](float value) {
E2D_LOG_INFO("音量: {:.0f}%", value * 100); E2D_INFO("音量: {:.0f}%", value * 100);
}); });
addChild(volumeSlider_); addChild(volumeSlider_);

View File

@ -86,7 +86,7 @@ sprite->runAction(move);
```cpp ```cpp
auto action = MoveTo::create(2.0f, Vec2(100, 100)); auto action = MoveTo::create(2.0f, Vec2(100, 100));
action->setCompletionCallback([]() { action->setCompletionCallback([]() {
E2D_LOG_INFO("动作完成!"); E2D_INFO("动作完成!");
}); });
sprite->runAction(action); sprite->runAction(action);
``` ```
@ -194,7 +194,7 @@ auto sequence = Sequence::create(
MoveTo::create(1.0f, Vec2(100, 100)), MoveTo::create(1.0f, Vec2(100, 100)),
DelayTime::create(0.5f), DelayTime::create(0.5f),
FadeOut::create(1.0f), FadeOut::create(1.0f),
CallFunc::create([]() { E2D_LOG_INFO("完成"); }), CallFunc::create([]() { E2D_INFO("完成"); }),
nullptr // 必须以 nullptr 结尾 nullptr // 必须以 nullptr 结尾
); );
sprite->runAction(sequence); sprite->runAction(sequence);
@ -373,12 +373,12 @@ spriteA->runAction(targeted);
```cpp ```cpp
// 无参数回调 // 无参数回调
auto callFunc = CallFunc::create([]() { auto callFunc = CallFunc::create([]() {
E2D_LOG_INFO("回调执行"); E2D_INFO("回调执行");
}); });
// 带节点参数的回调 // 带节点参数的回调
auto callFuncN = CallFuncN::create([](Node* node) { auto callFuncN = CallFuncN::create([](Node* node) {
E2D_LOG_INFO("节点: %p", node); E2D_INFO("节点: %p", node);
}); });
``` ```

View File

@ -52,7 +52,7 @@ private:
class CollisionDemoScene : public Scene { class CollisionDemoScene : public Scene {
public: public:
void onEnter() override { void onEnter() override {
E2D_LOG_INFO("CollisionDemoScene::onEnter - 碰撞检测演示"); E2D_INFO(CAT_SCENE, "CollisionDemoScene::onEnter - 碰撞检测演示");
// 设置背景色 // 设置背景色
setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f)); setBackgroundColor(Color(0.05f, 0.05f, 0.1f, 1.0f));
@ -74,7 +74,7 @@ public:
// 加载字体并创建UI // 加载字体并创建UI
loadFonts(); loadFonts();
E2D_LOG_INFO("创建了 {} 个碰撞框", boxes_.size() + 1); E2D_INFO("创建了 {} 个碰撞框", boxes_.size() + 1);
} }
void onUpdate(float dt) override { void onUpdate(float dt) override {
@ -106,7 +106,7 @@ public:
// 检查退出按键 // 检查退出按键
auto &input = Application::instance().input(); auto &input = Application::instance().input();
if (input.isButtonPressed(GamepadButton::Start)) { if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用"); E2D_INFO(CAT_APP, "退出应用");
Application::instance().quit(); Application::instance().quit();
} }
} }
@ -149,7 +149,8 @@ private:
addChild(fpsText_); addChild(fpsText_);
// 创建退出提示文本 // 创建退出提示文本
float screenHeight = static_cast<float>(Application::instance().getConfig().height); float screenHeight =
static_cast<float>(Application::instance().getConfig().height);
exitHintText_ = Text::create("按 + 键退出", infoFont_); exitHintText_ = Text::create("按 + 键退出", infoFont_);
exitHintText_->setPosition(50.0f, screenHeight - 50.0f); exitHintText_->setPosition(50.0f, screenHeight - 50.0f);
exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f)); exitHintText_->setTextColor(Color(0.8f, 0.8f, 0.8f, 1.0f));
@ -238,15 +239,10 @@ private:
// 程序入口 // 程序入口
// ============================================================================ // ============================================================================
int main(int argc, char **argv) int main(int argc, char **argv) {
{ E2D_INFO(CAT_APP, "========================");
// 初始化日志系统 E2D_INFO(CAT_APP, "Easy2D 碰撞检测演示");
Logger::init(); E2D_INFO(CAT_APP, "========================");
Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D 碰撞检测演示");
E2D_LOG_INFO("========================");
// 获取应用实例 // 获取应用实例
auto &app = Application::instance(); auto &app = Application::instance();
@ -261,19 +257,19 @@ int main(int argc, char **argv)
// 初始化应用 // 初始化应用
if (!app.init(config)) { if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!"); E2D_ERROR(CAT_APP, "应用初始化失败!");
return -1; return -1;
} }
// 进入场景 // 进入场景
app.enterScene(makePtr<CollisionDemoScene>()); app.enterScene(makePtr<CollisionDemoScene>());
E2D_LOG_INFO("开始主循环..."); E2D_INFO(CAT_APP, "开始主循环...");
// 运行应用 // 运行应用
app.run(); app.run();
E2D_LOG_INFO("应用结束"); E2D_INFO(CAT_APP, "应用结束");
return 0; return 0;
} }

View File

@ -16,7 +16,7 @@ public:
* @brief * @brief
*/ */
void onEnter() override { void onEnter() override {
E2D_LOG_INFO("HelloWorldScene::onEnter - 进入场景"); E2D_INFO("HelloWorldScene::onEnter - 进入场景");
// 设置背景颜色为深蓝色 // 设置背景颜色为深蓝色
setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f)); setBackgroundColor(Color(0.1f, 0.1f, 0.3f, 1.0f));
@ -26,7 +26,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 48, true); font_ = resources.loadFont("assets/font.ttf", 48, true);
if (!font_) { if (!font_) {
E2D_LOG_ERROR("字体加载失败,文字渲染将不可用!"); E2D_ERROR(CAT_ASSET, "字体加载失败,文字渲染将不可用!");
return; return;
} }
@ -75,7 +75,7 @@ public:
// 使用手柄 START 按钮退出 (GamepadButton::Start) // 使用手柄 START 按钮退出 (GamepadButton::Start)
if (input.isButtonPressed(GamepadButton::Start)) { if (input.isButtonPressed(GamepadButton::Start)) {
E2D_LOG_INFO("退出应用 (START 按钮)"); E2D_INFO("退出应用 (START 按钮)");
Application::instance().quit(); Application::instance().quit();
} }
} }
@ -89,13 +89,9 @@ private:
// ============================================================================ // ============================================================================
int main(int argc, char **argv) { int main(int argc, char **argv) {
// 初始化日志系统 E2D_INFO(CAT_APP, "========================");
Logger::init(); E2D_INFO(CAT_APP, "Easy2D Hello World Demo");
Logger::setLevel(LogLevel::Debug); E2D_INFO(CAT_APP, "========================");
E2D_LOG_INFO("========================");
E2D_LOG_INFO("Easy2D Hello World Demo");
E2D_LOG_INFO("========================");
// 获取应用实例 // 获取应用实例
auto &app = Application::instance(); auto &app = Application::instance();
@ -110,19 +106,19 @@ int main(int argc, char **argv) {
// 初始化应用 // 初始化应用
if (!app.init(config)) { if (!app.init(config)) {
E2D_LOG_ERROR("应用初始化失败!"); E2D_ERROR(CAT_APP, "应用初始化失败!");
return -1; return -1;
} }
// 进入 Hello World 场景 // 进入 Hello World 场景
app.enterScene(makePtr<HelloWorldScene>()); app.enterScene(makePtr<HelloWorldScene>());
E2D_LOG_INFO("开始主循环..."); E2D_INFO(CAT_APP, "开始主循环...");
// 运行应用 // 运行应用
app.run(); app.run();
E2D_LOG_INFO("应用结束"); E2D_INFO(CAT_APP, "应用结束");
return 0; return 0;
} }

View File

@ -240,7 +240,7 @@ public:
* @brief 场景进入时调用 * @brief 场景进入时调用
*/ */
void onEnter() override {{ void onEnter() override {{
E2D_LOG_INFO("MainScene::onEnter - 进入场景"); E2D_INFO("MainScene::onEnter - 进入场景");
// 设置背景颜色 // 设置背景颜色
setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f)); setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f));
@ -250,7 +250,7 @@ public:
font_ = resources.loadFont("assets/font.ttf", 32, true); font_ = resources.loadFont("assets/font.ttf", 32, true);
if (!font_) {{ if (!font_) {{
E2D_LOG_ERROR("字体加载失败!请确保 assets 目录下有 font.ttf 文件"); E2D_ERROR("字体加载失败!请确保 assets 目录下有 font.ttf 文件");
return; return;
}} }}
@ -281,7 +281,7 @@ public:
// 检查退出按键 // 检查退出按键
auto &input = Application::instance().input(); auto &input = Application::instance().input();
if (input.isButtonPressed(GamepadButton::Start)) {{ if (input.isButtonPressed(GamepadButton::Start)) {{
E2D_LOG_INFO("退出应用"); E2D_INFO("退出应用");
Application::instance().quit(); Application::instance().quit();
}} }}
}} }}
@ -299,9 +299,9 @@ int main(int argc, char **argv) {{
Logger::init(); Logger::init();
Logger::setLevel(LogLevel::Debug); Logger::setLevel(LogLevel::Debug);
E2D_LOG_INFO("========================"); E2D_INFO("========================");
E2D_LOG_INFO("{self.project_name}"); E2D_INFO("{self.project_name}");
E2D_LOG_INFO("========================"); E2D_INFO("========================");
// 获取应用实例 // 获取应用实例
auto &app = Application::instance(); auto &app = Application::instance();
@ -316,19 +316,19 @@ int main(int argc, char **argv) {{
// 初始化应用 // 初始化应用
if (!app.init(config)) {{ if (!app.init(config)) {{
E2D_LOG_ERROR("应用初始化失败!"); E2D_ERROR("应用初始化失败!");
return -1; return -1;
}} }}
// 进入主场景 // 进入主场景
app.enterScene(makePtr<MainScene>()); app.enterScene(makePtr<MainScene>());
E2D_LOG_INFO("开始主循环..."); E2D_INFO("开始主循环...");
// 运行应用 // 运行应用
app.run(); app.run();
E2D_LOG_INFO("应用结束"); E2D_INFO("应用结束");
return 0; return 0;
}} }}