From a335259edc02b2de608fc9c4e5dce19a01782331 Mon Sep 17 00:00:00 2001 From: EmsiaetKadosh Date: Sun, 23 Mar 2025 13:23:10 +0800 Subject: [PATCH] =?UTF-8?q?world=E5=88=9D=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 45 ++--- dev.txt | 1 + src/def.cpp | 31 +++- src/def.h | 94 +++++++---- src/game/Game.cpp | 39 ++++- src/game/Game.h | 33 +--- src/game/entity/Entity.cpp | 5 + src/game/entity/Entity.h | 75 ++++++--- src/game/entity/Player.h | 5 +- src/game/world/Block.h | 35 +++- src/game/world/Location.cpp | 12 ++ src/game/world/Location.h | 135 +++++++++++++-- src/game/world/World.h | 167 +++++++++++++++++-- src/hbp.h | 6 +- src/includes.h | 13 +- src/interact/InteractManager.h | 8 +- src/main.cpp | 295 +++++++++++++++++++++++++++++++-- src/render/Renderer.h | 116 +++++++++---- src/ui/Hud.cpp | 2 +- src/ui/Hud.h | 2 +- src/ui/Window.cpp | 18 +- src/ui/Window.h | 16 +- src/ui/xWindows.h | 4 +- src/utils/IText.h | 23 ++- src/utils/exception.h | 4 +- src/utils/gc.h | 4 +- src/utils/math.h | 107 +++++------- 27 files changed, 982 insertions(+), 313 deletions(-) create mode 100644 src/game/entity/Entity.cpp create mode 100644 src/game/world/Location.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b7130f9..a5a56aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,39 +21,22 @@ enable_testing() add_executable(${PROJECT_NAME} src/main.cpp - src/def.h - src/def.cpp - src/interact/InteractManager.h - src/interact/InteractManager.cpp - src/render/Renderer.h - src/render/Renderer.cpp - src/game/Game.h - src/game/Game.cpp - src/ui/Window.h - src/ui/Window.cpp - src/ui/Hud.h - src/ui/Hud.cpp - src/utils/IText.h - src/utils/IText.cpp - src/utils/exception.h - src/utils/exception.cpp - src/utils/File.h - src/utils/File.cpp - src/render/TextureManager.h - src/render/TextureManager.cpp - src/includes.h - src/utils/Chars.h - src/ui/xWindows.h - src/utils/TestCode.h - src/game/entity/Entity.h - src/game/entity/Player.h + src/def.cpp + src/utils/File.cpp + src/utils/exception.cpp + + src/interact/InteractManager.cpp + src/utils/IText.cpp + src/game/world/Location.cpp + src/render/Renderer.cpp + src/render/TextureManager.cpp + src/ui/Hud.cpp + src/ui/Window.cpp + + src/game/Game.cpp + src/game/entity/Entity.cpp src/game/entity/Player.cpp - src/game/entity/Damage.h - src/utils/math.h - src/game/world/World.h - src/game/world/Location.h - src/game/world/Block.h ) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) diff --git a/dev.txt b/dev.txt index ee08ef6..d87b6f1 100644 --- a/dev.txt +++ b/dev.txt @@ -2,3 +2,4 @@ Module: File(30%), including Log, Save, Config, Language Translator(30%), with File KeyBinding(20%) + World, Entity(10%), including gc problem diff --git a/src/def.cpp b/src/def.cpp index 7572e52..4879c33 100644 --- a/src/def.cpp +++ b/src/def.cpp @@ -4,8 +4,10 @@ #include "def.h" -#include "utils\Chars.h" +#include "utils\File.h" #include "utils\exception.h" +#include "utils\Chars.h" +#include "utils\gc.h" template template requires std::is_base_of_v && TypeName void ObjectHolder::set(const T& value) { @@ -28,20 +30,31 @@ void ObjectHolder::set(T&& value) { void requireNonnull(const void* value) noexcept(false) { if (!value) throw NullPointerException(L"value is null"); } void checkAllocation(const void* value) noexcept(false) { if (!value) throw BadAllocationException(L"bad allocation"); } -void printAllocate(void* value, const size_t size, const String& msg) { +void $LimitedUse::printAllocate(void* value, const size_t size, const String& msg) { const String str = L"alloc " + ptrtow(reinterpret_cast(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg; Logger.log(str); -#if __CARLBEKS_MEMORY__ > 2 - MainLogFile << str << std::endl; -#endif } -void printDeallocate(void* value, const size_t size, const String& msg) { +void $LimitedUse::printDeallocate(void* value, const size_t size, const String& msg) { const String str = L"dealloc " + ptrtow(reinterpret_cast(value)) + L" " + std::to_wstring(size) + String(L"B ") + msg; Logger.log(str); -#if __CARLBEKS_MEMORY__ > 2 - MainLogFile << str << std::endl; -#endif } +void $LimitedUse::printDeallocateWarning(void* value, const String& msg) { + const String str = L"dealloc " + ptrtow(reinterpret_cast(value)) + L": " + msg; + Logger.error(str); +} + + String ptrtow(const QWORD value) { return qwtowb16(value, 16); } + +namespace $LimitedUse { + Release::~Release() { + delete &gc; + Logger.put(L"--------- Last Check ---------\n"); + for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } + delete &Logger; + delete &memoryManager; + std::wcout << L"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; + } +} diff --git a/src/def.h b/src/def.h index 200018a..fef6df9 100644 --- a/src/def.h +++ b/src/def.h @@ -5,7 +5,7 @@ #pragma once #define __CARLBEKS_DEBUG__ -#define __CARLBEKS_MEMORY__ 1 +#define __CARLBEKS_MEMORY__ 3 #pragma warning(disable: 4819) @@ -29,10 +29,14 @@ using wchar = wchar_t; using QWORD = unsigned long long int; using String = std::wstring; using Thread = std::thread; -template, typename Alloc = std::allocator>> using Map = std::map; -template> using Set = std::set; -template> using List = std::list; -template using Function = std::function; +template , typename Alloc = std::allocator>> +using Map = std::map; +template , typename Allocator = std::allocator> +using Set = std::set; +template > +using List = std::list; +template +using Function = std::function; #define Success() { return 0; } #define Failed() { return 1; } @@ -65,61 +69,75 @@ template using Function = std::function; #pragma comment(lib, "Uxtheme.lib") #pragma comment(lib, "winmm.lib") -template +template concept Copyable = requires(const T& t) { T(t); }; -template +template concept NewCopyable = requires(const T& t) { delete new T(t); }; -template +template concept Moveable = requires(T&& t) { T(std::move(t)); }; -template +template concept NewMoveable = requires(T&& t) { delete new T(std::move(t)); }; -template +template concept NonreferenceType = !std::is_reference_v; -template +template concept NonpointerType = !std::is_pointer_v; -template +template concept ReferenceType = std::is_reference_v; -template +template concept PointerType = std::is_pointer_v; -template +template concept TypeName = NonreferenceType && NonpointerType; +namespace $LimitedUse { + struct Release { + Release() = default; + + ~Release(); + } inline gcRelease_LoggerRelease_memoryManagerRelease; +} // namespace $LimitedUse + struct MemoryManager { struct MemoryInfo { const String msg; std::size_t size; }; - Map allocated {}; + Map allocated{}; constexpr MemoryManager() noexcept = default; -} inline memoryManager; +} inline& [[carlbeks::releasedat("def.cpp")]] memoryManager = *new MemoryManager; void requireNonnull(const void* value) noexcept(false); void checkAllocation(const void* value) noexcept(false); inline String ptrtow(QWORD value); #if defined __CARLBEKS_DEBUG__ || defined __CARLBEKS_MEMORY__ -void printAllocate(void* value, std::size_t size, const String&); -void printDeallocate(void* value, std::size_t size, const String&); +namespace $LimitedUse { + void printAllocate(void* value, std::size_t size, const String&); + void printDeallocate(void* value, std::size_t size, const String&); + void printDeallocateWarning(void* value, const String& msg); +} + extern String atow(const char* chars); -template T* allocatedFor$(T* value, const String& msg = L"", std::size_t size = sizeof(T)) { +template +T* allocatedFor$(T* value, const String& msg = L"", std::size_t size = sizeof(T)) { requireNonnull(value); - const auto& k = memoryManager.allocated.emplace(value, MemoryManager::MemoryInfo{ L"[" + atow(typeid(T).name()) + L"] " + msg , size}).first; + const auto& k = memoryManager.allocated.emplace(value, MemoryManager::MemoryInfo{L"[" + atow(typeid(T).name()) + L"] " + msg, size}).first; #if __CARLBEKS_MEMORY__ > 1 - printAllocate(value, k->second.size, k->second.msg); + $LimitedUse::printAllocate(value, k->second.size, k->second.msg); #endif return value; } -template T* deallocating$(T* value) { +template +T* deallocating$(T* value, const String& stack) { #if __CARLBEKS_MEMORY__ > 1 const MemoryManager::MemoryInfo* info = nullptr; if (memoryManager.allocated.contains(value)) info = &memoryManager.allocated.at(value); - printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???"); + $LimitedUse::printDeallocate(value, info ? info->size : 0, info ? info->msg : L"???"); #endif - if (value) memoryManager.allocated.erase(value); + if (value) { if (!memoryManager.allocated.erase(value)) $LimitedUse::printDeallocateWarning(value, L"value not recorded" + stack); } return value; } @@ -128,13 +146,18 @@ template T* deallocating$(T* value) { #else #define allocatedFor(val, ...) allocatedFor$(val, L"" __VA_OPT__(,) __VA_ARGS__) #endif +#if __CARLBEKS_MEMORY__ > 2 +#define deallocating(val) deallocating$(val, L"\n From " __FUNCSIG__ "\n At " __FILE__ ":" _STL_STRINGIZE(__LINE__)) +#else #define deallocating(val) deallocating$(val) +#endif #else #define allocatedFor(val, ...) val #define deallocating(val) val #endif -template class ObjectHolder { +template +class ObjectHolder { Base* value; bool hasValue; char padding[7]{}; @@ -147,10 +170,10 @@ public: ObjectHolder(Base* value) : value(value), hasValue(false) {} - template requires (std::is_base_of_v || std::is_same_v) && TypeName + template requires (std::is_base_of_v || std::is_same_v) && TypeName ObjectHolder(const T& value) : value(allocatedFor(new T(value))), hasValue(true) {} - template requires (std::is_base_of_v || std::is_same_v) && TypeName + template requires (std::is_base_of_v || std::is_same_v) && TypeName ObjectHolder(T&& value) : value(allocatedFor(new T(std::forward(value)))), hasValue(true) {} ObjectHolder(const ObjectHolder& other) noexcept: value(other.value), hasValue(false) {} @@ -160,10 +183,10 @@ public: other.hasValue = false; } - template requires std::is_base_of_v && TypeName + template requires std::is_base_of_v && TypeName void set(const T& value); - template requires std::is_base_of_v && TypeName + template requires std::is_base_of_v && TypeName void set(T&& value); ~ObjectHolder() { @@ -207,14 +230,16 @@ public: bool operator!() const noexcept { return value == nullptr; } [[nodiscard]] bool isManager() const noexcept { return hasValue; } - template ObjectHolder referenceof(const T& other) { + template + ObjectHolder referenceof(const T& other) { ObjectHolder ret{}; ret.value = &other; return ret; } }; -template class SynchronizedHolder { +template +class SynchronizedHolder { mutable Base* newValue = nullptr; mutable Base* value = nullptr; mutable bool isOk = false; @@ -223,7 +248,8 @@ public: SynchronizedHolder() = default; ~SynchronizedHolder() { - if (newValue == value) { if (value) delete deallocating(value); } else { + if (newValue == value) { if (value) delete deallocating(value); } + else { if (newValue) delete deallocating(newValue); if (value) delete deallocating(value); } @@ -232,14 +258,14 @@ public: } - template requires std::is_base_of_v && TypeName + template requires std::is_base_of_v && TypeName void setNew(const Base& other) noexcept { isOk = false; if (newValue && newValue != value) deleteNew(); newValue = allocatedFor(new T(other)); } - template requires std::is_base_of_v && TypeName + template requires std::is_base_of_v && TypeName void setNew(T&& val) noexcept { isOk = false; if (newValue && newValue != value) deleteNew(); diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 87cc292..50efff4 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -4,12 +4,15 @@ #include "Game.h" +#include "entity\Entity.h" #include "world\World.h" #include "..\ui\xWindows.h" void Game::initialize() { worldManager = allocatedFor(new WorldManager); - game.setWindow(StartWindow::create()); + entityManager = allocatedFor(new EntityManager); + setWindow(StartWindow::create()); + windows.onResize(); } Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ allocatedFor(new FloatWindow()) } { @@ -20,8 +23,42 @@ Game::Game() : caption{ allocatedFor(new CaptionWindow()) }, floatWindow{ alloca Game::~Game() { setWindow(nullptr); delete deallocating(floatWindow); + gc.pack(); + gc.collect(); delete deallocating(caption); + gc.pack(); + gc.collect(); delete deallocating(worldManager); + gc.pack(); + gc.collect(); + delete deallocating(entityManager); + gc.pack(); + gc.collect(); +} + +void Game::render(const double tickDelta) const noexcept { + if (renderer.checkResizing()) return; + renderer.gameStartRender(); + if (worldManager->current) worldManager->current->render(tickDelta); + caption->render(tickDelta); + hud.render(tickDelta); + windows.render(tickDelta); + floatWindow->render(tickDelta); + renderer.gameEndRender(); + gc.pack(); +} + +void Game::tick() noexcept { + ++currentTick; + if (worldManager->current) worldManager->current->tick(); + floatWindow->clear(); + floatWindow->tick(); + caption->tick(); + hud.tick(); + windows.tick(); + floatWindow->update(); + tasks.runAll(); + gc.collect(); } inline Game game = Game(); diff --git a/src/game/Game.h b/src/game/Game.h index 1c6a3fc..51a5b59 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -10,6 +10,7 @@ #include "../ui/Window.h" class [[carlbeks::predecl, carlbeks::defineat("World.h")]] WorldManager; +class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] EntityManager; class Game final /* : public IRenderable, public ITickable */ { friend void gameThread(); @@ -23,24 +24,17 @@ public: TaskScheduler tasks; // 8 std::minstd_rand random; WorldManager* worldManager = nullptr; + EntityManager* entityManager = nullptr; void initialize(); Game(); ~Game(); - [[nodiscard]] QWORD getTick() const noexcept { return currentTick; } int closeWindow(Window* const window) noexcept { return windows.pop(window); } - - void render() const noexcept { - if (renderer.checkResizing()) return; - renderer.gameStartRender(); - caption->render(); - hud.render(); - windows.render(); - floatWindow->render(); - renderer.gameEndRender(); - gc.pack(); - } + [[nodiscard]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; } + [[nodiscard]] QWORD getTick() const noexcept { return currentTick; } + void tick() noexcept; + void render(double tickDelta) const noexcept; /** * 所有窗口都提交给Game保管,在适当时刻自动删除。 @@ -59,25 +53,12 @@ public: Success(); } - [[nodiscard]] FloatWindow& getFloatWindow() const noexcept { return *floatWindow; } - [[nodiscard]] Window* getWindow() const noexcept { - if (auto *const back = windows.back()) return dynamic_cast(back); + if (auto* const back = windows.back()) return dynamic_cast(back); Logger.error(L"Game::getWindow returns nullptr"); return nullptr; } - void tick() noexcept { - ++currentTick; - floatWindow->clear(); - floatWindow->tick(); - caption->tick(); - hud.tick(); - windows.tick(); - floatWindow->update(); - tasks.runAll(); - gc.collect(); - } void handleResize() { caption->onResize(); diff --git a/src/game/entity/Entity.cpp b/src/game/entity/Entity.cpp new file mode 100644 index 0000000..7d9d088 --- /dev/null +++ b/src/game/entity/Entity.cpp @@ -0,0 +1,5 @@ +// +// Created by EmsiaetKadosh on 25-3-22. +// + +#include "Entity.h" diff --git a/src/game/entity/Entity.h b/src/game/entity/Entity.h index 6f13447..2334e6a 100644 --- a/src/game/entity/Entity.h +++ b/src/game/entity/Entity.h @@ -5,11 +5,15 @@ #pragma once #include "..\..\utils\math.h" +#include "..\..\render\Renderer.h" #include "..\world\Location.h" #include "Damage.h" class Entity; -class World; +class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World; +class EntityManager; + +using EntityID = QWORD; interface IDamageable { protected: @@ -22,40 +26,65 @@ public: virtual void onDeath() = 0; }; -interface IMoveable { -protected: - double maxSpeed = 1.0; - Vector2D velocity; - - virtual ~IMoveable() = default; -public: - void setVelocity(const Vector2D& velocity) { this->velocity = velocity; } - [[nodiscard]] Vector2D getVelocity() const { return this->velocity; } -}; - interface IArtificialIntelligent { protected: virtual ~IArtificialIntelligent() = default; virtual void aiProcess() {} }; -class Entity { +class Entity : public IRenderable, public ITickable { friend class World; - Location location; - QWORD id = 0; + friend class EntityManager; + EntityID idEntity = 0; + World* world = nullptr; protected: - Entity(const Location& location) : location(location) {} - Entity(Location&& location) : location(std::move(location)) {} - virtual ~Entity() = default; + Location location; + Vector2D velocity; + double maxSpeed = 1.0; + + Entity(const Vector2D& location) : location(location) {} + ~Entity() override = default; public: - virtual void tick() {} - virtual void onRemove() {} + virtual void onRemove() = 0; + virtual void onEnterWorld(World* world, WorldTransportReason reason) {} + virtual void onExitWorld(World* world, WorldTransportReason reason) {} + + void setVelocity(const Vector2D& velocity) noexcept { this->velocity = velocity; } + [[nodiscard]] const Location& getLocation() const noexcept { return this->location; } + [[nodiscard]] const Location& getLocation(const double tickDelta) const noexcept { return Location(this->location.getPosition() + this->velocity * tickDelta, this->location.getWorld()); } + [[nodiscard]] Vector2D getVelocity() const noexcept { return this->velocity; } + [[nodiscard]] double getMaxSpeed() const noexcept { return this->maxSpeed; } }; -class Enemy : public Entity, public IDamageable, public IMoveable, public IArtificialIntelligent { +class Enemy : public Entity, public IDamageable, public IArtificialIntelligent { protected: - Enemy(const Location& location) : Entity(location) {} - Enemy(Location&& location) : Entity(std::move(location)) {} + Enemy(const Vector2D& location) : Entity(location) {} +}; + +class EntityManager { + friend class Game; + EntityID nextID = 0; + Map entities; + EntityManager() = default; + ~EntityManager() { for (auto& [id, entity] : entities) { entity->onRemove(); } } + +public: + int addEntity(Entity* entity) { + if (!entity) Failed(); + if (entity->idEntity) Failed(); + entity->idEntity = ++nextID; + entities.emplace(entity->idEntity, entity); + Success(); + } + + int removeEntity(Entity* entity) { + if (!entity) Failed(); + if (entity->idEntity) Failed(); + if (entity->getLocation().getWorld()) Failed(); // 确保必须已经从其他世界移除 + entities.erase(entity->idEntity); + entity->onRemove(); + Success(); + } }; diff --git a/src/game/entity/Player.h b/src/game/entity/Player.h index fe1708b..fc53a5a 100644 --- a/src/game/entity/Player.h +++ b/src/game/entity/Player.h @@ -6,8 +6,7 @@ #include "Entity.h" -class Player : public Entity, public IMoveable, public IDamageable { +class Player : public Entity, public IDamageable { public: - Player(const Location& location) : Entity(location) {} - Player(Location&& location) : Entity(std::move(location)) {} + Player(const Vector2D& location) : Entity(location) {} }; diff --git a/src/game/world/Block.h b/src/game/world/Block.h index edf5ad6..abfd9f1 100644 --- a/src/game/world/Block.h +++ b/src/game/world/Block.h @@ -4,9 +4,40 @@ #pragma once +#include "..\..\def.h" +#include "..\..\utils\gc.h" #include "Location.h" -class Block { -public: +class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World; +class Block : public IRenderable, public ITickable { + friend class Garbage; + friend class World; + World* world = nullptr; + BlockLocation location; + +public: + Block(const BlockLocation& location) : location(location) { this->location.setWorld(0); } + [[nodiscard]] const BlockLocation& getLocation() const { return location; } + virtual void onEnterWorld(World* world, WorldTransportReason reason) {} + virtual void onExitWorld(World* world, WorldTransportReason reason) {} + virtual void onRemove() { gc.submit(this); } + /** + * 渲染方块影。方块影会覆盖所有的方块。 + */ + virtual void renderShadow() const noexcept {} + /** + * 渲染方块本体。渲染范围不应当超过方块占据的范围。 + */ + void render(double tickDelta) const noexcept override = 0; +}; + +class PureBarrierBlock final : public Block { + PureBarrierBlock(const BlockLocation& location) : Block(location) {} + ~PureBarrierBlock() override {} + +public: + void render(double tickDelta) const noexcept override { renderer.fillWorldBlock(getLocation(), 0xffeeeeee); } + void tick() noexcept override {} + static PureBarrierBlock* create(const BlockLocation& location) { return allocatedFor(new PureBarrierBlock(location)); } }; diff --git a/src/game/world/Location.cpp b/src/game/world/Location.cpp new file mode 100644 index 0000000..53d7643 --- /dev/null +++ b/src/game/world/Location.cpp @@ -0,0 +1,12 @@ +// +// Created by EmsiaetKadosh on 25-3-23. +// + +#include "Location.h" + +inline const WorldTransportReason WorldTransportReason::Shutdown = *manager.create(L"game shutdown", true, true); +inline const WorldTransportReason WorldTransportReason::InitialGeneration = *manager.create(L"initial generation", true, true); +inline const WorldTransportReason WorldTransportReason::WorldCollapse = *manager.create(L"world collapse", true, true); +inline const WorldTransportReason WorldTransportReason::BlockBreak = *manager.create(L"block break", true, false); +inline const WorldTransportReason WorldTransportReason::BlockReplace = *manager.create(L"block replace", true, false); +inline const WorldTransportReason WorldTransportReason::EntityTeleport = *manager.create(L"entity teleport", false, true); diff --git a/src/game/world/Location.h b/src/game/world/Location.h index f271071..1093218 100644 --- a/src/game/world/Location.h +++ b/src/game/world/Location.h @@ -9,34 +9,135 @@ using WorldID = QWORD; class Location; class BlockLocation; +class [[carlbeks::predecl, carlbeks::defineat("World.h")]] World; -class Location { +/** + * 当一个方块/实体被移入/移出世界时,需要有一个理由。 + * 如果方块移出时附带内置的Shutdown理由,那么方块在移出时会被立刻自动submit至gc。 + * Shutdown理由的含义是,世界管理器下的所有世界内的所有方块,都会被依次附带Shutdown理由移出世界。 + * 此过程中不可以进行其他的方块WorldTransport行为(因为正在遍历方块),也不需要手动清除其他的关联方块。 + * 禁止在处理附带Shutdown理由的移出世界事件时提交自身给gc。但是方块自身的其他垃圾可能需要手动清理。 + * + * 该类视同enum类。元素只允许引用访问,不允许复制、移动。 + */ +class WorldTransportReason final { + friend class World; + struct LessReason; + + class Reason final { + friend struct LessReason; + friend class WorldTransportReason; + const String description; + const bool isBlockReason, isEntityReason; + ~Reason() = default; + + public: + Reason(const String& description, const bool isBlockReason, const bool isEntityReason) : description(description), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {} + Reason(String&& description, const bool isBlockReason, const bool isEntityReason) : description(std::move(description)), isBlockReason(isBlockReason), isEntityReason(isEntityReason) {} + Reason(const Reason&) = delete; + Reason(Reason&&) = delete; + Reason& operator=(const Reason&) = delete; + Reason& operator=(Reason&&) = delete; + }; + + struct LessReason { + using is_transparent = String; + [[nodiscard]] bool operator()(const Reason* const lhs, const Reason* const rhs) const { return lhs->description < rhs->description; } + [[nodiscard]] bool operator()(const String& str, const Reason* const rhs) const { return str < rhs->description; } + [[nodiscard]] bool operator()(const Reason* const lhs, const String& str) const { return lhs->description < str; } + }; + + struct Manager { + Set reasons; + ~Manager() { for (Reason* r : reasons) delete deallocating(r); } + + Reason* create(const String& description, const bool isBlockReason, const bool isEntityReason) { + if (const Set::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*(); + Reason* r = allocatedFor(new Reason(description, isBlockReason, isEntityReason)); + reasons.emplace(r); + return r; + } + + Reason* create(String&& description, const bool isBlockReason, const bool isEntityReason) { + if (const Set::const_iterator iter = reasons.find(description); iter != reasons.end()) return iter.operator*(); + Reason* r = allocatedFor(new Reason(std::move(description), isBlockReason, isEntityReason)); + reasons.emplace(r); + return r; + } + } inline static manager; + + static const WorldTransportReason Shutdown; + const Reason* reason; + WorldTransportReason(const Reason& reason) : reason(&reason) {} + +public: + static const WorldTransportReason InitialGeneration; + static const WorldTransportReason WorldCollapse; + static const WorldTransportReason BlockBreak; + static const WorldTransportReason BlockReplace; + static const WorldTransportReason EntityTeleport; + + WorldTransportReason() = delete; + WorldTransportReason(const WorldTransportReason&) = default; + WorldTransportReason(WorldTransportReason&&) = default; + WorldTransportReason& operator=(const WorldTransportReason&) = default; + WorldTransportReason& operator=(WorldTransportReason&&) = default; + ~WorldTransportReason() = default; + [[nodiscard]] bool operator==(const WorldTransportReason& other) const noexcept { return reason == other.reason; } + [[nodiscard]] bool operator!=(const WorldTransportReason& other) const noexcept { return reason != other.reason; } + [[nodiscard]] bool isBlockReason() const noexcept { return reason->isBlockReason; } + [[nodiscard]] bool isEntityReason() const noexcept { return reason->isEntityReason; } + + static WorldTransportReason registerReason(const String& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(description, isBlockReason, isEntityReason); } + static WorldTransportReason registerReason(String&& description, const bool isBlockReason, const bool isEntityReason) { return *manager.create(std::move(description), isBlockReason, isEntityReason); } +}; + +class [[carlbeks::TriviallyCopyable]] Location { Vector2D position; WorldID idWorld; public: - Location(const Vector2D& position, const WorldID idWorld) : position(position), idWorld(idWorld) {} - Location(Vector2D&& position, const WorldID idWorld) : position(position), idWorld(idWorld) {} - [[nodiscard]] Vector2D getPosition() const { return position; } - [[nodiscard]] WorldID getWorld() const { return idWorld; } - [[nodiscard]] double getX() const { return position.getX(); } - [[nodiscard]] double getY() const { return position.getY(); } - [[nodiscard]] BlockLocation getBlockLocation() const; + Location(const Vector2D& position) noexcept : position(position), idWorld(0) {} + Location(const Vector2D& position, const WorldID idWorld) noexcept : position(position), idWorld(idWorld) {} + Location(const Location& other) noexcept = default; + Location(Location&& other) noexcept = default; + [[nodiscard]] Vector2D getPosition() const noexcept { return position; } + [[nodiscard]] WorldID getWorld() const noexcept { return idWorld; } + [[nodiscard]] double getX() const noexcept { return position.getX(); } + [[nodiscard]] double getY() const noexcept { return position.getY(); } + [[nodiscard]] BlockLocation getBlockLocation() const noexcept; + + void setPosition(const Vector2D& vector) noexcept { position = vector; } + void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; } }; -class BlockLocation { +class [[carlbeks::TriviallyCopyable]] BlockLocation { long long x, y; WorldID idWorld; public: - BlockLocation(const long long x, const long long y, const WorldID idWorld) : x(x), y(y), idWorld(idWorld) {} - BlockLocation(const Vector2D& position, const WorldID idWorld) : x(static_cast(std::floor(position.getX()))), y(static_cast(std::floor(position.getY()))), idWorld(idWorld) {} - [[nodiscard]] Vector2D getPosition() const { return Vector2D(static_cast(x), static_cast(y)); } - [[nodiscard]] WorldID getWorld() const { return idWorld; } - [[nodiscard]] long long getX() const { return x; } - [[nodiscard]] long long getY() const { return y; } - [[nodiscard]] Location toLocation() const { return Location({ static_cast(x), static_cast(y) }, idWorld); } + BlockLocation(const long long x, const long long y) noexcept : x(x), y(y), idWorld(0) {} + BlockLocation(const Vector2D& position) noexcept : x(static_cast(position.getX())), y(static_cast(position.getY())), idWorld(0) {} + BlockLocation(const long long x, const long long y, const WorldID idWorld) noexcept : x(x), y(y), idWorld(idWorld) {} + BlockLocation(const Vector2D& position, const WorldID idWorld) noexcept : x(static_cast(std::floor(position.getX()))), y(static_cast(std::floor(position.getY()))), idWorld(idWorld) {} + BlockLocation(const BlockLocation& other) noexcept = default; + BlockLocation(BlockLocation&& other) noexcept = default; + [[nodiscard]] Vector2D getPosition() const noexcept { return Vector2D(static_cast(x), static_cast(y)); } + [[nodiscard]] WorldID getWorld() const noexcept { return idWorld; } + [[nodiscard]] long long getX() const noexcept { return x; } + [[nodiscard]] long long getY() const noexcept { return y; } + [[nodiscard]] Location toLocation() const noexcept { return Location({ static_cast(x), static_cast(y) }, idWorld); } + + void setPosition(const long long x, const long long y) noexcept { this->x = x, this->y = y; } + void setWorld(const WorldID idWorld) noexcept { this->idWorld = idWorld; } + + struct Less { + bool operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept; + }; }; -inline BlockLocation Location::getBlockLocation() const { return BlockLocation(position, idWorld); } +inline bool BlockLocation::Less::operator()(const BlockLocation& lhs, const BlockLocation& rhs) const noexcept { return lhs.y < rhs.y || (lhs.y == rhs.y && lhs.x < rhs.x); } + + +inline BlockLocation Location::getBlockLocation() const noexcept { return BlockLocation(position, idWorld); } diff --git a/src/game/world/World.h b/src/game/world/World.h index 740f2c7..8c55398 100644 --- a/src/game/world/World.h +++ b/src/game/world/World.h @@ -6,34 +6,175 @@ #include "..\..\def.h" #include "..\entity\Entity.h" +#include "Block.h" -class World { - QWORD idEntity = 0; +class World; +class WorldManager; + +class World : public IRenderable, public ITickable { + friend class WorldManager; + friend class Garbage; + WorldID idWorld; Map entities; + Map blocks; using IterEntity = Map::const_iterator; + using IterBlock = Map::const_iterator; -public: +protected: World() = default; - int addEntity(Entity* entity) { + ~World() override { + Logger.debug(L"~World() called"); + for (auto& [location, block] : blocks) { + block->onExitWorld(this, WorldTransportReason::Shutdown); + block->location.setWorld(0); + block->world = nullptr; + block->onRemove(); + } + for (auto& [id, entity] : entities) { + entity->onExitWorld(this, WorldTransportReason::Shutdown); + entity->location.setWorld(0); + entity->world = nullptr; + // entity->onRemove() 交给EntityManager调用 + } + } + +public: + void render(const double tickDelta) const noexcept override { + for (const auto& [location, block] : blocks) block->render(tickDelta); + for (const auto& [id, entity] : entities) entity->render(tickDelta); + for (const auto& [location, block] : blocks) block->renderShadow(); + } + + virtual int addEntity(Entity* entity, const WorldTransportReason reason) { if (!entity) Failed(); - if (entity->id) Failed(); - entities.emplace(++idEntity, entity); + if (!entity->idEntity) Failed(); + if (entity->location.getWorld()) Failed(); + if (entity->world) Failed(); + if (!reason.isEntityReason()) Failed(); + entity->onEnterWorld(this, reason); + entity->location.setWorld(idWorld); + entity->world = this; + entities.emplace(entity->idEntity, entity); Success(); } - int removeEntity(Entity* entity) { + virtual int removeEntity(Entity* entity, const WorldTransportReason reason) { if (!entity) Failed(); - if (!entity->id) Failed(); - const IterEntity iter = entities.find(entity->id); - if (iter != entities.end()) Failed(); - entities.erase(iter); - entity->onRemove(); - entity->id = 0; + if (!entity->idEntity) Failed(); + if (!entities.erase(entity->idEntity)) Failed(); + if (!reason.isEntityReason()) Failed(); + entity->onExitWorld(this, reason); + entity->location.setWorld(0); + entity->world = nullptr; Success(); } + + virtual int addBlock(Block* block, const WorldTransportReason reason) { + if (!block) Failed(); + if (block->getLocation().getWorld()) Failed(); + if (block->world) Failed(); + if (blocks.contains(block->getLocation())) Failed(); + if (!reason.isBlockReason()) Failed(); + blocks.emplace(block->getLocation(), block); + block->onEnterWorld(this, reason); + block->location.setWorld(idWorld); + block->world = this; + Success(); + } + + virtual int removeBlock(Block* block, const WorldTransportReason reason) { + if (!block) Failed(); + if (block->getLocation().getWorld() != idWorld) Failed(); + if (!blocks.erase(block->getLocation())) Failed(); + if (!reason.isBlockReason()) Failed(); + block->onExitWorld(this, reason); + block->location.setWorld(0); + block->world = nullptr; + Success(); + } + + virtual int removeBlockAt(const BlockLocation& location, const WorldTransportReason reason) { + if (!reason.isBlockReason()) Failed(); + if (location.getWorld() != idWorld) Failed(); + const IterBlock it = blocks.find(location); + if (it == blocks.cend()) Failed(); + Block* block = it->second; + blocks.erase(it); + block->onExitWorld(this, reason); + block->location.setWorld(0); + block->world = nullptr; + Success(); + } + + virtual void onRemove() { + // Entity不需要再此处删除,交给EntityManager管理 + for (auto& [id, entity] : entities) { + entity->onExitWorld(this, WorldTransportReason::WorldCollapse); + entity->location.setWorld(0); + entity->world = nullptr; + } + for (auto& [location, block] : blocks) { + block->onExitWorld(this, WorldTransportReason::WorldCollapse); + block->location.setWorld(0); + block->world = nullptr; + block->onRemove(); + } + gc.submit(this); + } }; class WorldManager { + friend class Game; + WorldID nextID = 0; + Map worlds; + World* current = nullptr; + WorldManager() = default; + ~WorldManager() { + Logger.debug(L"~WorldManager() called"); + for (auto& [id, world] : worlds) world->onRemove(); + } + public: + int addWorld(World* world) { + if (!world) Failed(); + if (world->idWorld) Failed(); + world->idWorld = ++nextID; + worlds.emplace(world->idWorld, world); + Success(); + } + + int removeWorld(World* world) { + if (!world) Failed(); + if (!world->idWorld) Failed(); + world->idWorld = 0; + worlds.erase(world->idWorld); + world->onRemove(); + Success(); + } + + int setWorld(World* world) { + if (!world) Failed(); + if (world->idWorld) Failed(); + current = world; + Success(); + } +}; + +class StartWorld final : public World { + StartWorld() = default; + ~StartWorld() override { Logger.debug(L"~StartWorld() called"); } + +public: + void tick() noexcept override {} + + static StartWorld* create() { + StartWorld* world = allocatedFor(new StartWorld); + world->addBlock(PureBarrierBlock::create(BlockLocation(0, 0)), WorldTransportReason::InitialGeneration); + world->addBlock(PureBarrierBlock::create(BlockLocation(1, 0)), WorldTransportReason::InitialGeneration); + world->addBlock(PureBarrierBlock::create(BlockLocation(2, 0)), WorldTransportReason::InitialGeneration); + world->addBlock(PureBarrierBlock::create(BlockLocation(3, 0)), WorldTransportReason::InitialGeneration); + world->addBlock(PureBarrierBlock::create(BlockLocation(4, 0)), WorldTransportReason::InitialGeneration); + return world; + } }; diff --git a/src/hbp.h b/src/hbp.h index 0a29fe6..15fb22e 100644 --- a/src/hbp.h +++ b/src/hbp.h @@ -16,10 +16,8 @@ inline HRESULT RemoveDefaultCaption(const HWND hWnd, const MARGINS* p) noexcept inline bool ShowConsoleIO() noexcept { AllocConsole(); - FILE* pCout; - freopen_s(&pCout, "CONOUT$", "w", stdout); - FILE* pCin; - freopen_s(&pCin, "CONOUT$", "r+", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "r+", stdin); return true; } diff --git a/src/includes.h b/src/includes.h index 30d42df..13dbeca 100644 --- a/src/includes.h +++ b/src/includes.h @@ -8,20 +8,21 @@ // Any include #include "def.h" -#include "utils\math.h" +#include "utils\math.h" // required by Location.h #include "utils\File.h" #include "utils\exception.h" +#include "utils\Chars.h" // utils serialized -#include "utils\gc.h" -#include "utils\utils.h" -#include "hbp.h" -#include "utils\Chars.h" +#include "utils\gc.h" // required by utils.h +#include "utils\utils.h" // required by Task.h #include "utils\Task.h" +#include "hbp.h" // game basic #include "interact\InteractManager.h" #include "utils\IText.h" +#include "game\world\Location.h" // required by Renderer.h #include "render\Renderer.h" #include "game\Animation.h" #include "render\TextureManager.h" @@ -33,7 +34,7 @@ // game extension #include "ui\xWindows.h" -#include "game\world\Location.h" #include "game\entity\Entity.h" +#include "game\world\Block.h" #include "game\world\World.h" #include "game\entity\Player.h" diff --git a/src/interact/InteractManager.h b/src/interact/InteractManager.h index 22b8edb..ddc2c7f 100644 --- a/src/interact/InteractManager.h +++ b/src/interact/InteractManager.h @@ -206,6 +206,7 @@ class InteractSettings { int marginWidth = 40; int fontHeight = 96; int floatWindowMargin = 16; + double mapScale = 32.0; // 1格表现为32像素 }; struct Constants { @@ -215,12 +216,15 @@ class InteractSettings { */ double uiScale = 1; double screenScale = 1; + double smoothCamera = 0.5; // 相机平滑度。为0,始终瞬时设置相机的位置;为1,相机不动。 + long long msPerTick = 50; + long long msPerRender = 16; unsigned int floatWindowBackground = 0xdd000000; }; public: - Options options; - Options actual; + Options options; // 用户设置值 + Options actual; // 适应Scale后的实际值 Constants constants; InteractSettings& setUiScale(const double scale) noexcept { diff --git a/src/main.cpp b/src/main.cpp index 10a14f3..5c1d9a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,271 @@ #include "includes.h" -#include "utils/TestCode.h" +#include "utils\TestCode.h" + +long __stdcall UnhandledExceptionHandler(PEXCEPTION_POINTERS exception) { + isRunning = false; + Logger.error(L"Unhandled exception handler called"); + const auto ContextRecord = exception->ContextRecord; + const auto ExceptionRecord = exception->ExceptionRecord; + Logger.print( + std::hex, + L"Register parameter home addresses:" + L"\n P1Home", ContextRecord->P1Home, + L"\n P2Home", ContextRecord->P2Home, + L"\n P3Home", ContextRecord->P3Home, + L"\n P4Home", ContextRecord->P4Home, + L"\n P5Home", ContextRecord->P5Home, + L"\n P6Home", ContextRecord->P6Home); + Logger.print( + L"Control Flags:" + L"\n ContextFlags", ContextRecord->ContextFlags, + L"\n MxCsr", ContextRecord->MxCsr); + Logger.print( + L"Segment Registers and processor flags:" + L"\n SegCs", ContextRecord->SegCs, + L"\n SegDs", ContextRecord->SegDs, + L"\n SegEs", ContextRecord->SegEs, + L"\n SegFs", ContextRecord->SegFs, + L"\n SegGs", ContextRecord->SegGs, + L"\n SegSs", ContextRecord->SegSs, + L"\n EFlags", ContextRecord->EFlags); + Logger.print( + L"Debug registers:" + L"\n Dr0", ContextRecord->Dr0, + L"\n Dr1", ContextRecord->Dr1, + L"\n Dr2", ContextRecord->Dr2, + L"\n Dr3", ContextRecord->Dr3, + L"\n Dr6", ContextRecord->Dr6, + L"\n Dr7", ContextRecord->Dr7); + Logger.print( + L"Integer registers:" + L"\n Rax", ContextRecord->Rax, + L"\n Rcx", ContextRecord->Rcx, + L"\n Rdx", ContextRecord->Rdx, + L"\n Rbx", ContextRecord->Rbx, + L"\n Rsp", ContextRecord->Rsp, + L"\n Rbp", ContextRecord->Rbp, + L"\n Rsi", ContextRecord->Rsi, + L"\n Rdi", ContextRecord->Rdi, + L"\n R8", ContextRecord->R8, + L"\n R9", ContextRecord->R9, + L"\n R10", ContextRecord->R10, + L"\n R11", ContextRecord->R11, + L"\n R12", ContextRecord->R12, + L"\n R13", ContextRecord->R13, + L"\n R14", ContextRecord->R14, + L"\n R15", ContextRecord->R15); + Logger.print( + L"Program Counter:" + L"\n Rip", ContextRecord->Rip); + Logger.print( + L"Floating point state:" + L"\n XMM_SAVE_AREA32:" + L"\n ControlWord", ContextRecord->FltSave.ControlWord, + L"\n StatusWord", ContextRecord->FltSave.StatusWord, + L"\n TagWord", ContextRecord->FltSave.TagWord, + L"\n Reserved1", ContextRecord->FltSave.Reserved1, + L"\n ErrorOpcode", ContextRecord->FltSave.ErrorOpcode, + L"\n ErrorOffset", ContextRecord->FltSave.ErrorOffset, + L"\n ErrorSelector", ContextRecord->FltSave.ErrorSelector, + L"\n Reserved2", ContextRecord->FltSave.Reserved2, + L"\n DataOffset", ContextRecord->FltSave.DataOffset, + L"\n DataSelector", ContextRecord->FltSave.DataSelector, + L"\n Reserved3", ContextRecord->FltSave.Reserved3, + L"\n MxCsr", ContextRecord->FltSave.MxCsr, + L"\n MxCsr_Mask", ContextRecord->FltSave.MxCsr_Mask); + Logger.print( + L"\n FloatRegisters[0]", + ContextRecord->FltSave.FloatRegisters[0].High, + ContextRecord->FltSave.FloatRegisters[0].Low, + L"\n FloatRegisters[1]", + ContextRecord->FltSave.FloatRegisters[1].High, + ContextRecord->FltSave.FloatRegisters[1].Low, + L"\n FloatRegisters[2]", + ContextRecord->FltSave.FloatRegisters[2].High, + ContextRecord->FltSave.FloatRegisters[2].Low, + L"\n FloatRegisters[3]", + ContextRecord->FltSave.FloatRegisters[3].High, + ContextRecord->FltSave.FloatRegisters[3].Low, + L"\n FloatRegisters[4]", + ContextRecord->FltSave.FloatRegisters[4].High, + ContextRecord->FltSave.FloatRegisters[4].Low, + L"\n FloatRegisters[5]", + ContextRecord->FltSave.FloatRegisters[5].High, + ContextRecord->FltSave.FloatRegisters[5].Low, + L"\n FloatRegisters[6]", + ContextRecord->FltSave.FloatRegisters[6].High, + ContextRecord->FltSave.FloatRegisters[6].Low, + L"\n FloatRegisters[7]", + ContextRecord->FltSave.FloatRegisters[7].High, + ContextRecord->FltSave.FloatRegisters[7].Low); + Logger.print( + L"\n XmmRegisters[]" + L"\n Xmm0", + ContextRecord->Xmm0.High, + ContextRecord->Xmm0.Low, + L"\n Xmm1", + ContextRecord->Xmm1.High, + ContextRecord->Xmm1.Low, + L"\n Xmm2", + ContextRecord->Xmm2.High, + ContextRecord->Xmm2.Low, + L"\n Xmm3", + ContextRecord->Xmm3.High, + ContextRecord->Xmm3.Low, + L"\n Xmm4", + ContextRecord->Xmm4.High, + ContextRecord->Xmm4.Low, + L"\n Xmm5", + ContextRecord->Xmm5.High, + ContextRecord->Xmm5.Low, + L"\n Xmm6", + ContextRecord->Xmm6.High, + ContextRecord->Xmm6.Low, + L"\n Xmm7", + ContextRecord->Xmm7.High, + ContextRecord->Xmm7.Low, + L"\n Xmm8", + ContextRecord->Xmm8.High, + ContextRecord->Xmm8.Low, + L"\n Xmm9", + ContextRecord->Xmm9.High, + ContextRecord->Xmm9.Low, + L"\n Xmm10", + ContextRecord->Xmm10.High, + ContextRecord->Xmm10.Low, + L"\n Xmm11", + ContextRecord->Xmm11.High, + ContextRecord->Xmm11.Low, + L"\n Xmm12", + ContextRecord->Xmm12.High, + ContextRecord->Xmm12.Low, + L"\n Xmm13", + ContextRecord->Xmm13.High, + ContextRecord->Xmm13.Low, + L"\n Xmm14", + ContextRecord->Xmm14.High, + ContextRecord->Xmm14.Low, + L"\n Xmm15", + ContextRecord->Xmm15.High, + ContextRecord->Xmm15.Low); + + Logger.print( + "\nVectorRegister" + L"\n 0", + ContextRecord->VectorRegister[0].High, + ContextRecord->VectorRegister[0].Low, + "\n 1", + ContextRecord->VectorRegister[1].High, + ContextRecord->VectorRegister[1].Low, + L"\n 2", + ContextRecord->VectorRegister[2].High, + ContextRecord->VectorRegister[2].Low, + L"\n 3", + ContextRecord->VectorRegister[3].High, + ContextRecord->VectorRegister[3].Low, + L"\n 4", + ContextRecord->VectorRegister[4].High, + ContextRecord->VectorRegister[4].Low, + L"\n 5", + ContextRecord->VectorRegister[5].High, + ContextRecord->VectorRegister[5].Low, + L"\n 6", + ContextRecord->VectorRegister[6].High, + ContextRecord->VectorRegister[6].Low, + L"\n 7", + ContextRecord->VectorRegister[7].High, + ContextRecord->VectorRegister[7].Low, + L"\n 8", + ContextRecord->VectorRegister[8].High, + ContextRecord->VectorRegister[8].Low, + L"\n 9", + ContextRecord->VectorRegister[9].High, + ContextRecord->VectorRegister[9].Low, + L"\n 10", + ContextRecord->VectorRegister[10].High, + ContextRecord->VectorRegister[10].Low, + L"\n 11", + ContextRecord->VectorRegister[11].High, + ContextRecord->VectorRegister[11].Low, + L"\n 12", + ContextRecord->VectorRegister[12].High, + ContextRecord->VectorRegister[12].Low, + L"\n 13", + ContextRecord->VectorRegister[13].High, + ContextRecord->VectorRegister[13].Low, + L"\n 14", + ContextRecord->VectorRegister[14].High, + ContextRecord->VectorRegister[14].Low, + L"\n 15", + ContextRecord->VectorRegister[15].High, + ContextRecord->VectorRegister[15].Low, + L"\n 16", + ContextRecord->VectorRegister[16].High, + ContextRecord->VectorRegister[16].Low, + L"\n 17", + ContextRecord->VectorRegister[17].High, + ContextRecord->VectorRegister[17].Low, + L"\n 18", + ContextRecord->VectorRegister[18].High, + ContextRecord->VectorRegister[18].Low, + L"\n 19", + ContextRecord->VectorRegister[19].High, + ContextRecord->VectorRegister[19].Low, + L"\n 20", + ContextRecord->VectorRegister[20].High, + ContextRecord->VectorRegister[20].Low, + L"\n 21", + ContextRecord->VectorRegister[21].High, + ContextRecord->VectorRegister[21].Low, + L"\n 22", + ContextRecord->VectorRegister[22].High, + ContextRecord->VectorRegister[22].Low, + L"\n 23", + ContextRecord->VectorRegister[23].High, + ContextRecord->VectorRegister[23].Low, + L"\n 24", + ContextRecord->VectorRegister[24].High, + ContextRecord->VectorRegister[24].Low, + L"\n 25", + ContextRecord->VectorRegister[25].High, + ContextRecord->VectorRegister[25].Low, + L"\n VectorControl", ContextRecord->VectorControl); + Logger.print( + L"\nSpecial debug control registers:" + L"\n DebugControl", ContextRecord->DebugControl, + L"\n LastBranchToRip", ContextRecord->LastBranchToRip, + L"\n LastBranchFromRip", ContextRecord->LastBranchFromRip, + L"\n LastExceptionToRip", ContextRecord->LastExceptionToRip, + L"\n LastExceptionFromRip", ContextRecord->LastExceptionFromRip + ); + + Logger.print( + L"\nExceptionRecord:" + L"\n ExceptionCode", ExceptionRecord->ExceptionCode, + L"\n ExceptionFlags", ExceptionRecord->ExceptionFlags, + L"\n ExceptionAddress", ExceptionRecord->ExceptionAddress, + L"\n NumberParameters", ExceptionRecord->NumberParameters, + L"\n ExceptionInformation", + L"\n ", ExceptionRecord->ExceptionInformation[0], + L"\n ", ExceptionRecord->ExceptionInformation[1], + L"\n ", ExceptionRecord->ExceptionInformation[2], + L"\n ", ExceptionRecord->ExceptionInformation[3], + L"\n ", ExceptionRecord->ExceptionInformation[4], + L"\n ", ExceptionRecord->ExceptionInformation[5], + L"\n ", ExceptionRecord->ExceptionInformation[6], + L"\n ", ExceptionRecord->ExceptionInformation[7], + L"\n ", ExceptionRecord->ExceptionInformation[8], + L"\n ", ExceptionRecord->ExceptionInformation[9], + L"\n ", ExceptionRecord->ExceptionInformation[10], + L"\n ", ExceptionRecord->ExceptionInformation[11], + L"\n ", ExceptionRecord->ExceptionInformation[12], + L"\n ", ExceptionRecord->ExceptionInformation[13], + L"\n ", ExceptionRecord->ExceptionInformation[14] + ); + + return EXCEPTION_EXECUTE_HANDLER; +} LRESULT __stdcall WndProc(const HWND hwnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) { switch (uMsg) { @@ -199,14 +464,16 @@ LRESULT __stdcall HookProc(const int code, const WPARAM wParam, const LPARAM lPa return CallNextHookEx(nullptr, code, wParam, lParam); } +using Time = std::chrono::time_point; +Time lastTick = std::chrono::system_clock::now(); + void gameThread() { try { using namespace std::chrono; using Time = time_point; - Time lastTick = system_clock::now(); while (isRunning) { const Time thisTime = system_clock::now(); - if (thisTime - lastTick < milliseconds(45)) { + if (thisTime - lastTick < milliseconds(interactSettings.constants.msPerTick)) { Sleep(1); continue; } @@ -229,11 +496,11 @@ void renderThread() { Time lastRender = system_clock::now(); while (isRunning) { const Time thisTime = system_clock::now(); - if (thisTime - lastRender < milliseconds(12)) { + if (thisTime - lastRender < milliseconds(interactSettings.constants.msPerRender)) { Sleep(1); continue; } - game.render(); + game.render(nRange(static_cast((thisTime - lastTick).count()) / interactSettings.constants.msPerRender, 0.0, 1.0)); lastRender = thisTime; } } catch (const Exception& e) { Logger.log(L"Render thread exception: " + e.getMessage()); } @@ -246,6 +513,7 @@ void renderThread() { int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unused]] const LPWSTR lpCmdLine, [[maybe_unused]] const int nShowCmd) { Logger.info(L"wWinMain started"); SetConsoleOutputCP(65001); + SetUnhandledExceptionFilter(UnhandledExceptionFilter); translator.initialize(); Logger.info(L"--------Program Start--------"); for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } @@ -295,6 +563,9 @@ int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unuse { game.initialize(); interactManager.initialize(); + World* w = StartWorld::create(); + game.worldManager->addWorld(w); + game.worldManager->setWorld(w); GameThread = Thread(gameThread); RenderThread = Thread(renderThread); } @@ -315,16 +586,8 @@ int __stdcall wWinMain(const HINSTANCE hInstance, const HINSTANCE, [[maybe_unuse Logger.info(L"------- Program End --------"); for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } _wsystem(L"pause"); + { + fontManager.finalize(); // 似乎GDI有终止自动回收,所以此代码需要提前 + } return static_cast(msg.wParam); } - -struct Release { - Release() = default; - - ~Release() { - delete &gc; - Logger.put(L"--------- Last Check ---------\n"); - for (const auto& [addr, info] : memoryManager.allocated) { Logger.print(L" using", addr, info.size, L"B", info.msg); } - delete &Logger; - } -} NEVER_REFERENCED_release; diff --git a/src/render/Renderer.h b/src/render/Renderer.h index 8def5e3..bb8e856 100644 --- a/src/render/Renderer.h +++ b/src/render/Renderer.h @@ -5,8 +5,11 @@ #pragma once #include "..\def.h" +#include "..\utils\math.h" #include "..\utils\exception.h" #include "..\utils\Task.h" +#include "..\interact\InteractManager.h" +#include "..\game\world\Location.h" class Game; @@ -17,7 +20,7 @@ enum class UILocation : char { LEFT_TOP, LEFT, LEFT_BOTTOM, TOP, CENTER, BOTTOM, interface IRenderable { virtual ~IRenderable() = default; - virtual void render() const noexcept = 0; + virtual void render(double tickDelta) const noexcept = 0; }; interface ITickable { @@ -39,16 +42,36 @@ inline static constexpr Color TextColor = { .clicked = 0xff000000 }; +class [[carlbeks::predecl, carlbeks::defineat("Entity.h")]] Entity; + +class Camera { + Vector2D position; // 当前位置 + Vector2D velocity; // 移动速度,如果设置了平滑Camera + Vector2D targetPosition; // Camera需要移动到的位置 + Entity* targeting = nullptr; + +public: + void setTargetEntity(Entity* target) noexcept { targeting = target; } + [[nodiscard]] double getCurrentX() const noexcept { return position.getX(); } + [[nodiscard]] double getCurrentY() const noexcept { return position.getY(); } + [[nodiscard]] double getTargetX() const noexcept { return targetPosition.getX(); } + [[nodiscard]] double getTargetY() const noexcept { return targetPosition.getY(); } + [[nodiscard]] Vector2D getCurrentPosition() const noexcept { return position; } + [[nodiscard]] Vector2D getVelocity() const noexcept { return velocity; } + [[nodiscard]] Vector2D getTargetPosition() const noexcept { return targetPosition; } +}; + class Renderer final : public ITickable { friend class Game; friend class Font; - mutable List failed; inline static BLENDFUNCTION blendFunction = { .BlendOp = AC_SRC_OVER, // Only .BlendFlags = 0, // Must 0 .SourceConstantAlpha = 255, // 预乘 .AlphaFormat = 0, // Not AC_SRC_ALPHA }; + mutable List failed; + mutable Camera camera; HDC MainDC = nullptr; // 8 HDC resizeCopyDC = nullptr; // 8 HBITMAP resizeCopyBitmap = nullptr; // 8 @@ -72,22 +95,24 @@ class Renderer final : public ITickable { public: byte windowSize = 0; // 1, Windows: SIZE_*** byte reserved[5]{}; - Task resizeReloadBitmap{ [this](Task& task) { - Logger.info(L"Scheduled task: resize reload bitmap " + std::to_wstring(windowWidth) + L" * " + std::to_wstring(windowHeight)); - if (!canvasBitmap) { - canvasBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight); - if (canvasBitmap) SelectObject(canvasDC, canvasBitmap); + Task resizeReloadBitmap{ + [this](Task& task) { + Logger.info(L"Scheduled task: resize reload bitmap " + std::to_wstring(windowWidth) + L" * " + std::to_wstring(windowHeight)); + if (!canvasBitmap) { + canvasBitmap = CreateCompatibleBitmap(MainDC, windowWidth, windowHeight); + if (canvasBitmap) SelectObject(canvasDC, canvasBitmap); + } + if (!assistBitmap) { + assistBitmap = CreateCompatibleBitmap(canvasDC, windowWidth, windowHeight); + if (assistBitmap) SelectObject(assistDC, assistBitmap); + } + if (canvasBitmap && assistBitmap) { + task.pop(); + Logger.info(L"Successfully reload bitmap " + ptrtow(reinterpret_cast(canvasBitmap)) + L" " + ptrtow(reinterpret_cast(assistBitmap))); + this->resizeEnd(); + } } - if (!assistBitmap) { - assistBitmap = CreateCompatibleBitmap(canvasDC, windowWidth, windowHeight); - if (assistBitmap) SelectObject(assistDC, assistBitmap); - } - if (canvasBitmap && assistBitmap) { - task.pop(); - Logger.info(L"Successfully reload bitmap " + ptrtow(reinterpret_cast(canvasBitmap)) + L" " + ptrtow(reinterpret_cast(assistBitmap))); - this->resizeEnd(); - } - } }; + }; private: void gameStartRender() noexcept; @@ -118,20 +143,16 @@ public: [[nodiscard]] int getSyncHeight() const noexcept { return syncHeight; } [[nodiscard]] bool checkResizing() const noexcept { return isResizing; } void tick() noexcept override {} - /** * @attention 会忽略A透明度值 * @param argb ARGB式颜色 * @return int BGR式颜色 */ [[nodiscard]] static unsigned int changeColorFormat(const unsigned int argb) { return argb << 16 & 0xff0000 | argb & 0xff00 | argb >> 16 & 0xff; } - void assertRendering() const noexcept(false) { if (!isRendering) throw InvalidOperationException(L"Operation should be done while rendering"); } - void assertRenderThread() const noexcept(false) { if (std::this_thread::get_id() != renderThread) throw InvalidOperationException(L"Operation should be done in render thread"); } - + Camera& getCamera() const noexcept { return camera; } void resizeStart() noexcept { isResizing = true; } - void resizeShow() const noexcept { StretchBlt(MainDC, 0, 0, syncWidth, syncHeight, resizeCopyDC, 0, 0, resizeCopyWidth, resizeCopyHeight, SRCCOPY); } void resizeEnd() noexcept { @@ -143,9 +164,13 @@ public: } void deleteObject(HGDIOBJ obj) const noexcept { - for (List::const_iterator iter = failed.cbegin(); iter != failed.cend(); ++iter) - if (!DeleteObject(*iter)) Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast(*iter))); - else failed.erase(iter); + List tempList; + tempList.swap(failed); + for (List::const_iterator iter = tempList.cbegin(); iter != tempList.cend(); ++iter) + if (!DeleteObject(*iter)) { + Logger.error(L"DeleteObject failed again. Deleting: " + std::to_wstring(reinterpret_cast(*iter))); + failed.push_back(*iter); + } if (obj && !DeleteObject(obj)) { failed.push_back(obj); Logger.error(L"DeleteObject failed. Deleting: " + std::to_wstring(reinterpret_cast(obj))); @@ -166,7 +191,8 @@ public: const HBRUSH clr = CreateSolidBrush(changeColorFormat(color)); FillRect(canvasDC, &rect, clr); deleteObject(clr); - } else { + } + else { const RECT rect{ .left = 0, .top = 0, @@ -185,10 +211,12 @@ public: assertRendering(); //assertRenderThread(); if ((color & 0xff000000) == 0) return; - if ((color & 0xff000000) == 0xff000000) { const HBRUSH clr = CreateSolidBrush(changeColorFormat(color)); + if ((color & 0xff000000) == 0xff000000) { + const HBRUSH clr = CreateSolidBrush(changeColorFormat(color)); FillRect(canvasDC, rect, clr); deleteObject(clr); - } else { + } + else { const RECT r{ .left = 0, .top = 0, @@ -202,6 +230,38 @@ public: if (!AlphaBlend(canvasDC, rect->left, rect->top, r.right, r.bottom, assistDC, 0, 0, r.right, r.bottom, blendFunction)) Logger.error(L"AlphaBlend failed"); } } + + void fillWorld(const Vector2D& from, const Vector2D& to, const unsigned int color) const { + RECT rect{}; + Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale; + rect.left = static_cast(vector.getX()); + if (rect.left >= windowWidth) return; + rect.top = static_cast(vector.getY()); + if (rect.top >= windowHeight) return; + vector = (to - camera.getCurrentPosition()) * interactSettings.actual.mapScale; + rect.right = static_cast(vector.getX()); + if (rect.right < 0) return; + rect.bottom = static_cast(vector.getY()); + if (rect.bottom < 0) return; + fill(&rect, color); + } + + void fillWorld(const Vector2D& from, const double blockWidth, const double blockHeight, const unsigned int color) const { + RECT rect{}; + Vector2D vector = (from - camera.getCurrentPosition()) * interactSettings.actual.mapScale; + rect.left = static_cast(vector.getX()); + if (rect.left >= windowWidth) return; + rect.top = static_cast(vector.getY()); + if (rect.top >= windowHeight) return; + vector += Vector2D(blockWidth, blockHeight).multiply(interactSettings.actual.mapScale); + rect.right = static_cast(vector.getX()); + if (rect.right < 0) return; + rect.bottom = static_cast(vector.getY()); + if (rect.bottom < 0) return; + fill(&rect, color); + } + + void fillWorldBlock(const BlockLocation& from, const unsigned int color) const { fillWorld(from.getPosition(), 1, 1, color); } }; extern Renderer renderer; diff --git a/src/ui/Hud.cpp b/src/ui/Hud.cpp index bc030c0..e510374 100644 --- a/src/ui/Hud.cpp +++ b/src/ui/Hud.cpp @@ -4,5 +4,5 @@ #include "Hud.h" -void Hud::render() const noexcept {} +void Hud::render(double tickDelta) const noexcept {} void Hud::tick() noexcept {} diff --git a/src/ui/Hud.h b/src/ui/Hud.h index cca536c..712ceab 100644 --- a/src/ui/Hud.h +++ b/src/ui/Hud.h @@ -8,7 +8,7 @@ class Hud final : public IRenderable, public ITickable { public: - void render() const noexcept override; + void render(double tickDelta) const noexcept override; void tick() noexcept override; void onResize() noexcept {} }; diff --git a/src/ui/Window.cpp b/src/ui/Window.cpp index 315ecd7..bc275a9 100644 --- a/src/ui/Window.cpp +++ b/src/ui/Window.cpp @@ -13,7 +13,7 @@ int Window::pop() noexcept { Success(); } -void Window::render() const noexcept { for (const Widget* widget : widgets) widget->render(); } +void Window::render(const double tickDelta) const noexcept { for (const Widget* widget : widgets) widget->render(tickDelta); } void Window::tick() noexcept { for (Widget* widget : widgets) widget->tick(); } void Window::onResize() { for (Widget* widget : widgets) widget->onResize(); } @@ -58,13 +58,13 @@ CaptionWindow::CaptionWindow() { close->foregroundColor.inactive = 0xff000000; close->foregroundColor.clicked = 0xff000000; - Widget* maxRestore = widgets.emplace_back(Button(-interactSettings.actual.captionHeight, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::RIGHT_TOP, IsZoomed(MainWindowHandle) ? L"\\f\1🗗"_literal : L"\\f\1🗖"_literal )); + Widget* maxRestore = widgets.emplace_back(Button(-interactSettings.actual.captionHeight, 0, interactSettings.actual.captionHeight, interactSettings.actual.captionHeight, UILocation::RIGHT_TOP, IsZoomed(MainWindowHandle) ? L"\\f\1🗗"_literal : L"\\f\1🗖"_literal)); maxRestore->mouseClick = [](Widget&, MouseButtonCode) {}; maxRestore->mouseClick = [](Widget& self, MouseButtonCode) { if ((self.unused[1] = static_cast(IsZoomed(MainWindowHandle)))) ShowWindow(MainWindowHandle, SW_RESTORE); else ShowWindow(MainWindowHandle, SW_MAXIMIZE); }; - maxRestore->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(self.unused[1] ? TranslatableText(L"hbp.caption.maximize").getRenderableString() : TranslatableText(L"hbp.caption.restore").getRenderableString() ); }; + maxRestore->onTick = [](const Widget& self, MouseButtonCode) { if (self.containsMouse()) game.getFloatWindow().push(self.unused[1] ? TranslatableText(L"hbp.caption.maximize").getRenderableString() : TranslatableText(L"hbp.caption.restore").getRenderableString()); }; maxRestore->absolute(); maxRestore->unused[1] = static_cast(IsZoomed(MainWindowHandle)); maxRestore->backgroundColor.hover = 0xffcccccc; @@ -111,9 +111,9 @@ CaptionWindow::CaptionWindow() { bool CaptionWindow::onOpen() { throw InvalidOperationException(L"Should not open CaptionWindow"); } void CaptionWindow::onClose() { throw InvalidOperationException(L"Should not close CaptionWindow"); } -void CaptionWindow::render() const noexcept { +void CaptionWindow::render(const double tickDelta) const noexcept { renderer.fill(0, 0, renderer.getWidth(), interactSettings.actual.captionHeight, 0xff666666); - for (const Widget* widget : widgets) widget->render(); + for (const Widget* widget : widgets) widget->render(tickDelta); } void CaptionWindow::onResize() { @@ -137,7 +137,7 @@ void CaptionWindow::onResize() { Window::onResize(); } -void FloatWindow::render() const noexcept { +void FloatWindow::render(double tickDelta) const noexcept { if (not interactManager.isInWindow()) { strings.async(); return; @@ -180,7 +180,7 @@ unsigned int Widget::colorSelector(const Color& clr) const { return clr.hover; } -void Widget::render() const noexcept { renderer.fill(left, top, width, height, colorSelector(backgroundColor)); } +void Widget::render(double tickDelta) const noexcept { renderer.fill(left, top, width, height, colorSelector(backgroundColor)); } void Widget::onResize() { if (isAbsoluteLocation) { @@ -268,8 +268,8 @@ void Widget::onResize() { } } -void Button::render() const noexcept { - Widget::render(); +void Button::render(const double tickDelta) const noexcept { + Widget::render(tickDelta); if (name) fontManager.getDefault().drawCenter(name->getRenderableString(), left, top, width, height, colorSelector(foregroundColor)); } diff --git a/src/ui/Window.h b/src/ui/Window.h index f391ad2..d98d280 100644 --- a/src/ui/Window.h +++ b/src/ui/Window.h @@ -65,7 +65,7 @@ public: Widget(const double x, const double y, const double w, const double h, const UILocation location) : x(x), y(y), w(w), h(h), location(location) {} unsigned int colorSelector(const Color& clr) const; - void render() const noexcept override; + void render(double tickDelta) const noexcept override; Widget& alignLocation(const UILocation loc) noexcept { location = loc; @@ -155,7 +155,7 @@ protected: public: int pop() noexcept override; - void render() const noexcept override; + void render(double tickDelta) const noexcept override; void tick() noexcept override; /** * 在Game.setWindow()时,本窗口开启时调用。 @@ -176,7 +176,7 @@ public: class WindowManager final : public AnywhereEditableList, public IRenderable, public ITickable { public: - void render() const noexcept override { for (const Window& i : *this) i.render(); } + void render(const double tickDelta) const noexcept override { for (const Window& i : *this) i.render(tickDelta); } void tick() noexcept override { for (Window& i : *this) i.tick(); } int pop(Window* value) noexcept override; void clear() noexcept; @@ -188,7 +188,7 @@ public: CaptionWindow(); bool onOpen() override; void onClose() override; - void render() const noexcept override; + void render(double tickDelta) const noexcept override; void onResize() override; }; @@ -209,7 +209,7 @@ public: void push(const ObjectHolder& string) const { if (strings.ptrNew()) strings.getNew().push_back(string); } void push(ObjectHolder&& string) const { if (strings.ptrNew()) strings.getNew().push_back(std::move(string)); } - void render() const noexcept override; + void render(double tickDelta) const noexcept override; void tick() noexcept override {} bool onOpen() override { return true; } void onClose() override {} @@ -222,7 +222,7 @@ public: Animation animation = Animation().features(Animation::AS_CUBIC).setDuration(20); Button(const double x, const double y, const double w, const double h, const UILocation location, const ObjectHolder& text) : Widget(x, y, w, h, location), name(text) {} Button(const double x, const double y, const double w, const double h, const UILocation location, ObjectHolder&& text) : Widget(x, y, w, h, location), name(std::move(text)) {} - void render() const noexcept override; + void render(double tickDelta) const noexcept override; }; @@ -240,13 +240,13 @@ private: public: ~ConfirmWindow() override = default; // 不需要delete,析构时Window会自动delete - void render() const noexcept override { + void render(const double tickDelta) const noexcept override { int w, h; w = renderer.getWidth(), h = renderer.getHeight(); w >>= 2, h >>= 2; renderer.fill(w, h, w + w, h + h, 0xcc222222); fontManager.getDefault().drawCenter(text->getRenderableString(), w, h, w + w, h + (h >> 1), 0xffeeeeee); - for (const ObjectHolder& widget : widgets) widget->render(); + for (const ObjectHolder& widget : widgets) widget->render(tickDelta); } ConfirmWindow& requireConfirm(const Function& func = {}); diff --git a/src/ui/xWindows.h b/src/ui/xWindows.h index d0183fd..23ac388 100644 --- a/src/ui/xWindows.h +++ b/src/ui/xWindows.h @@ -34,8 +34,8 @@ public: static StartWindow* create() noexcept { return allocatedFor(new StartWindow()); } void onClose() override { pop(); } - void render() const noexcept override { + void render(const double tickDelta) const noexcept override { fontManager.getDefault().drawCenter(title.getRenderableString(), 0, 0, renderer.getWidth(), renderer.getHeight()); - Window::render(); + Window::render(tickDelta); } }; diff --git a/src/utils/IText.h b/src/utils/IText.h index 4cdcc1c..d2bdf33 100644 --- a/src/utils/IText.h +++ b/src/utils/IText.h @@ -146,19 +146,22 @@ public: if (config.idFont == idFont || config.idFont != 0) { font = false; ++flags; - } else config.idFont = idFont; + } + else config.idFont = idFont; } if (clr) { if (config.color == color || config.color != 0xffffffff) { clr = false; ++flags; - } else config.color = color; + } + else config.color = color; } if (bg) { if (config.background == background || config.background != 0xffffffff) { bg = false; ++flags; - } else config.background = background; + } + else config.background = background; } configs.push_back(std::move(config)); ++iterator; @@ -374,14 +377,17 @@ private: const FontID id; bool adaptAllSize = false; - Font(const FontID id, const String& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{ name }, yOffset(yOffset), heightModifier(heightModifier), height(static_cast(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {} + Font(const FontID id, const String& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{name}, yOffset(yOffset), heightModifier(heightModifier), height(static_cast(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {} - Font(const FontID id, String&& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{ std::move(name) }, yOffset(yOffset), heightModifier(heightModifier), height(static_cast(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {} + Font(const FontID id, String&& name, const double heightModifier, const double yOffset, const long escapement, const long orientation, const bool adaptAllSize) : name{std::move(name)}, yOffset(yOffset), heightModifier(heightModifier), height(static_cast(interactSettings.actual.fontHeight * heightModifier)), escapement(escapement), orientation(orientation), yOffsetPx(static_cast(yOffset * height)), id(id), adaptAllSize(adaptAllSize) {} public: Font(const Font&) = default; Font(Font&&) = default; - ~Font() { clear(); } + ~Font() { + // 此处Font的回收已经到结束阶段,GDI应该已经收回了资源,不能在手动释放了 + if (fonts.size()) Logger.warn(L"Font is not successfully cleared when ~Font() called: " + name); + } private: HFONT tryCreate(const RenderableString::StringConfig& config) const { @@ -447,6 +453,8 @@ public: newFont(L"", 1.0, -0.05); } + void finalize() { for (auto& [id, font] : fonts) font.clear(); } + [[nodiscard]] const Font& get(const FontID id) const noexcept { const IterFonts iter = fonts.find(id); if (iter == fonts.cend()) return *defaultFont; @@ -543,7 +551,7 @@ void languageMakeChinese(Language&); class Translator { Map langMap{}; List langList{}; - TranslatedText nullText{ L"\\#FF""EE0000" }; + TranslatedText nullText{L"\\#FF""EE0000"}; String lang = L"zh-cn"; LangID idLangMax = 0; int langConfig = 1; @@ -583,7 +591,6 @@ public: void loadLang(); [[nodiscard]] const TranslatedText* getText(const String& id) const noexcept { - Logger.debug(L"getText called"); for (const Language* language : langList) { const Language::IterText iterator = language->translateTable.find(id); if (iterator != language->translateTable.cend()) return &iterator->second; diff --git a/src/utils/exception.h b/src/utils/exception.h index 525d871..10fbbea 100644 --- a/src/utils/exception.h +++ b/src/utils/exception.h @@ -162,7 +162,7 @@ public: template requires requires(T t, Ts... ts) { std::wcout << t; - std::wcout << (ts, ...); + (std::wcout << ... << std::forward(ts)); } PublicLogger& print(T&& msg, Ts&&... other) noexcept { std::wstringstream stream = {}; @@ -194,5 +194,5 @@ public: } }; -[[carlbeks::releasedat("main.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main"); +[[carlbeks::releasedat("def.cpp")]] inline PublicLogger& Logger = *new PublicLogger(L"Main"); diff --git a/src/utils/gc.h b/src/utils/gc.h index 1a1f03a..ac7a3d6 100644 --- a/src/utils/gc.h +++ b/src/utils/gc.h @@ -76,7 +76,7 @@ public: } /** 只能在gameThread调用 */ - template + template void submit(T* ptr) noexcept(false) { IGarbage* newedGarbage = allocatedFor(new Garbage(ptr)); builtinSubmit(newedGarbage); @@ -147,4 +147,4 @@ public: } }; -inline GarbageCollector& [[carlbeks::releasedat("main.cpp")]] gc = *new GarbageCollector(); +inline GarbageCollector& [[carlbeks::releasedat("def.cpp")]] gc = *new GarbageCollector(); diff --git a/src/utils/math.h b/src/utils/math.h index b63fbcb..cbcf413 100644 --- a/src/utils/math.h +++ b/src/utils/math.h @@ -5,11 +5,27 @@ #pragma once #include "..\warnings.h" +#include "..\def.h" -class Vector3D; -class Vector2D; +template +const T& nRange(const T& val, const T& min, const T& max) { return val < min ? min : val > max ? max : val; } -class Vector3D { +template +const T& nMin(const T& a, const T& b) { return a < b ? a : b; } + +template +const T& nMin(const T& val0, const T& val1, const T& vals...) { return nMin(nMin(val0, val1), nMin(vals...)); } + +template +const T& nMax(const T& a, const T& b) { return a > b ? a : b; } + +template +const T& nMax(const T& val0, const T& val1, const T& vals...) { return nMax(nMin(val0, val1), nMax(vals...)); } + +class [[carlbeks::TriviallyCopyable]] Vector3D; +class [[carlbeks::TriviallyCopyable]] Vector2D; + +class [[carlbeks::TriviallyCopyable]] Vector3D { double x, y, z; public: @@ -30,39 +46,13 @@ public: [[nodiscard]] Vector3D operator*(const double scalar) const noexcept { return Vector3D(x * scalar, y * scalar, z * scalar); } [[nodiscard]] Vector3D operator/(const double scalar) const noexcept { return Vector3D(x / scalar, y / scalar, z / scalar); } [[nodiscard]] double operator*(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; } - - Vector3D& operator+=(const Vector3D& other) noexcept { - x += other.x; - y += other.y; - z += other.z; - return *this; - } - - Vector3D& operator-=(const Vector3D& other) noexcept { - x -= other.x; - y -= other.y; - z -= other.z; - return *this; - } - - Vector3D& operator*=(const double scalar) noexcept { - x *= scalar; - y *= scalar; - z *= scalar; - return *this; - } - - Vector3D& operator/=(const double scalar) noexcept { - x /= scalar; - y /= scalar; - z /= scalar; - return *this; - } - + Vector3D& operator+=(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; } + Vector3D& operator-=(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; } + Vector3D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, z *= scalar, *this; } + Vector3D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, z /= scalar, *this; } [[nodiscard]] Vector3D operator-() const noexcept { return Vector3D(-x, -y, -z); } bool operator==(const Vector3D& other) const noexcept { return x == other.x && y == other.y && z == other.z; } bool operator!=(const Vector3D& other) const noexcept { return x != other.x || y != other.y || z != other.z; } - [[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y + z * z); } [[nodiscard]] Vector3D getNormalized() const noexcept { @@ -74,17 +64,21 @@ public: Vector3D& normalize() noexcept { if (x == 0 && y == 0 && z == 0) return *this; const double length = this->length(); - x /= length; - y /= length; - z /= length; + x /= length, y /= length, z /= length; return *this; } + Vector3D& add(const Vector3D& other) noexcept { return x += other.x, y += other.y, z += other.z, *this; } + Vector3D& add(const double x, const double y, const double z) noexcept { return this->x += x, this->y += y, this->z += z, *this; } + Vector3D& subtract(const Vector3D& other) noexcept { return x -= other.x, y -= other.y, z -= other.z, *this; } + Vector3D& subtract(const double x, const double y, const double z) noexcept { return this->x -= x, this->y -= y, this->z -= z, *this; } + Vector3D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, this->z *= value, *this; } + Vector3D& divide(const double value) noexcept { return this->x /= value, this->y /= value, this->z /= value, *this; } [[nodiscard]] double dot(const Vector3D& other) const noexcept { return x * other.x + y * other.y + z * other.z; } [[nodiscard]] Vector3D cross(const Vector3D& other) const noexcept { return Vector3D(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x); } }; -class Vector2D { +class [[carlbeks::TriviallyCopyable]] Vector2D { double x, y; public: @@ -104,35 +98,13 @@ public: [[nodiscard]] Vector2D operator*(const double scalar) const noexcept { return Vector2D(x * scalar, y * scalar); } [[nodiscard]] Vector2D operator/(const double scalar) const noexcept { return Vector2D(x / scalar, y / scalar); } [[nodiscard]] double operator*(const Vector2D& other) const noexcept { return x * other.x + y * other.y; } - - Vector2D& operator+=(const Vector2D& other) noexcept { - x += other.x; - y += other.y; - return *this; - } - - Vector2D& operator-=(const Vector2D& other) noexcept { - x -= other.x; - y -= other.y; - return *this; - } - - Vector2D& operator*=(const double scalar) noexcept { - x *= scalar; - y *= scalar; - return *this; - } - - Vector2D& operator/=(const double scalar) noexcept { - x /= scalar; - y /= scalar; - return *this; - } - + Vector2D& operator+=(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; } + Vector2D& operator-=(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; } + Vector2D& operator*=(const double scalar) noexcept { return x *= scalar, y *= scalar, *this; } + Vector2D& operator/=(const double scalar) noexcept { return x /= scalar, y /= scalar, *this; } [[nodiscard]] Vector2D operator-() const noexcept { return Vector2D(-x, -y); } bool operator==(const Vector2D& other) const noexcept { return x == other.x && y == other.y; } bool operator!=(const Vector2D& other) const noexcept { return x != other.x || y != other.y; } - [[nodiscard]] double length() const noexcept { return std::sqrt(x * x + y * y); } [[nodiscard]] Vector2D getNormalized() const noexcept { @@ -144,11 +116,16 @@ public: Vector2D& normalize() noexcept { if (x == 0 && y == 0) return *this; const double length = this->length(); - x /= length; - y /= length; + x /= length, y /= length; return *this; } + Vector2D& add(const Vector2D& other) noexcept { return x += other.x, y += other.y, *this; } + Vector2D& add(const double x, const double y) noexcept { return this->x += x, this->y += y, *this; } + Vector2D& subtract(const Vector2D& other) noexcept { return x -= other.x, y -= other.y, *this; } + Vector2D& subtract(const double x, const double y) noexcept { return this->x -= x, this->y -= y, *this; } + Vector2D& multiply(const double value) noexcept { return this->x *= value, this->y *= value, *this; } + Vector2D& divide(const double value) noexcept { return this->x /= value, this->y /= value, *this; } [[nodiscard]] double dot(const Vector2D& other) const noexcept { return x * other.x + y * other.y; } [[nodiscard]] Vector3D cross(const Vector2D& other) const noexcept { return Vector3D(0, 0, x * other.y - y * other.x); } };