diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..a53f103242 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## 05-12-2023 +### Breaking API Changes +- `UIWidget` property `qr-code` & `qr-code-border` replaced with `UIQrCode` properties `code` & `code-border` +- `image-source-base64` replaced with `image-source: base64:/path/to/image` +- `#include "shadermanager.h"` moved to `#include ` \ No newline at end of file diff --git a/README.md b/README.md index e2b33c2ca2..31e4b666cd 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,9 @@ - Colored text [@conde2](https://github.com/conde2) - widget:setColoredText("{Colored text, #ff00ff} normal text") - QR Code support, with auto generate it from string [@conde2] - - qr-code-border: 2 - - qr-code: Hail OTClient Redemption - Conde2 Dev + - UIQrCode: + - code-border: 2 + - code: Hail OTClient Redemption - Conde2 Dev - Smooth Walk Elevation Feature by [@SkullzOTS](https://github.com/SkullzOTS) - View Feature: [Gyazo](https://i.gyazo.com/af0ed0f15a9e4d67bd4d0b2847bd6be7.gif) - To enable just go to [modules/game_features/features.lua](https://github.com/mehah/otclient/blob/main/modules/game_features/features.lua#L5), and uncomment line 5 (g_game.enableFeature(GameSmoothWalkElevation)). diff --git a/data/setup.otml b/data/setup.otml index 8b3eaf1488..d9e7fffdde 100644 --- a/data/setup.otml +++ b/data/setup.otml @@ -19,6 +19,10 @@ game shield-blink-ticks: 500 volatile-square-duration: 1000 adjust-creature-information-based-crop-size: false + diagonal-walk-speed: 3 + + player + diagonal-walk-speed: 3 render invisible-ticks-per-frame: 500 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 324a814444..20bd5dc658 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ project(otclient) # ***************************************************************************** # Options # ***************************************************************************** +option(TOGGLE_FRAMEWORK_GRAPHICS "Use Graphics " ON) option(TOGGLE_FRAMEWORK_SOUND "Use SOUND " ON) option(TOGGLE_FRAMEWORK_XML "Use XML " ON) option(TOGGLE_FRAMEWORK_NET "Use NET " ON) @@ -50,6 +51,9 @@ endif() # Define Framework options for use in compilation set(FRAMEWORK_DEFINITIONS "") +if (TOGGLE_FRAMEWORK_GRAPHICS) + set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DFRAMEWORK_GRAPHICS) +endif() if (TOGGLE_FRAMEWORK_SOUND) set(FRAMEWORK_DEFINITIONS ${FRAMEWORK_DEFINITIONS} -DFRAMEWORK_SOUND) endif() @@ -232,7 +236,6 @@ endif() # OTClient source files configuration # ***************************************************************************** set(SOURCE_FILES - framework/core/adaptativeframecounter.cpp framework/core/application.cpp framework/core/asyncdispatcher.cpp framework/core/binarytree.cpp @@ -242,7 +245,6 @@ set(SOURCE_FILES framework/core/event.cpp framework/core/eventdispatcher.cpp framework/core/filestream.cpp - framework/core/graphicalapplication.cpp framework/core/logger.cpp framework/core/module.cpp framework/core/modulemanager.cpp @@ -252,32 +254,6 @@ set(SOURCE_FILES framework/core/unzipper.h framework/core/timer.cpp framework/discord/discord.cpp - framework/graphics/animatedtexture.cpp - framework/graphics/apngloader.cpp - framework/graphics/bitmapfont.cpp - framework/graphics/cachedtext.cpp - framework/graphics/coordsbuffer.cpp - framework/graphics/drawpool.cpp - framework/graphics/drawpoolmanager.cpp - framework/graphics/fontmanager.cpp - framework/graphics/framebuffer.cpp - framework/graphics/graphics.cpp - framework/graphics/hardwarebuffer.cpp - framework/graphics/image.cpp - framework/graphics/painter.cpp - framework/graphics/paintershaderprogram.cpp - framework/graphics/particle.cpp - framework/graphics/particleaffector.cpp - framework/graphics/particleeffect.cpp - framework/graphics/particleemitter.cpp - framework/graphics/particlemanager.cpp - framework/graphics/particlesystem.cpp - framework/graphics/particletype.cpp - framework/graphics/shader.cpp - framework/graphics/shaderprogram.cpp - framework/graphics/texture.cpp - framework/graphics/texturemanager.cpp - framework/input/mouse.cpp framework/luaengine/luaexception.cpp framework/luaengine/luainterface.cpp framework/luaengine/luaobject.cpp @@ -305,8 +281,6 @@ set(SOURCE_FILES framework/platform/unixplatform.cpp framework/platform/win32crashhandler.cpp framework/platform/win32platform.cpp - framework/platform/win32window.cpp - framework/platform/x11window.cpp framework/stdext/demangle.cpp framework/stdext/math.cpp framework/stdext/net.cpp @@ -314,20 +288,6 @@ set(SOURCE_FILES framework/stdext/time.cpp framework/stdext/uri.cpp framework/stdext/qrcodegen.cpp - framework/ui/uianchorlayout.cpp - framework/ui/uiboxlayout.cpp - framework/ui/uigridlayout.cpp - framework/ui/uihorizontallayout.cpp - framework/ui/uilayout.cpp - framework/ui/uimanager.cpp - framework/ui/uiparticles.cpp - framework/ui/uitextedit.cpp - framework/ui/uitranslator.cpp - framework/ui/uiverticallayout.cpp - framework/ui/uiwidget.cpp - framework/ui/uiwidgetbasestyle.cpp - framework/ui/uiwidgetimage.cpp - framework/ui/uiwidgettext.cpp framework/util/color.cpp framework/util/crypt.cpp @@ -362,7 +322,6 @@ set(SOURCE_FILES client/protocolgame.cpp client/protocolgameparse.cpp client/protocolgamesend.cpp - client/shadermanager.cpp client/spriteappearances.cpp client/spritemanager.cpp client/statictext.cpp @@ -385,6 +344,57 @@ set(SOURCE_FILES androidmain.cpp ) +if (TOGGLE_FRAMEWORK_GRAPHICS) + set(SOURCE_FILES ${SOURCE_FILES} + framework/core/adaptativeframecounter.cpp + framework/core/graphicalapplication.cpp + framework/input/mouse.cpp + framework/graphics/animatedtexture.cpp + framework/graphics/apngloader.cpp + framework/graphics/bitmapfont.cpp + framework/graphics/cachedtext.cpp + framework/graphics/coordsbuffer.cpp + framework/graphics/drawpool.cpp + framework/graphics/drawpoolmanager.cpp + framework/graphics/fontmanager.cpp + framework/graphics/framebuffer.cpp + framework/graphics/graphics.cpp + framework/graphics/hardwarebuffer.cpp + framework/graphics/image.cpp + framework/graphics/painter.cpp + framework/graphics/paintershaderprogram.cpp + framework/graphics/particle.cpp + framework/graphics/particleaffector.cpp + framework/graphics/particleeffect.cpp + framework/graphics/particleemitter.cpp + framework/graphics/particlemanager.cpp + framework/graphics/particlesystem.cpp + framework/graphics/particletype.cpp + framework/graphics/shader.cpp + framework/graphics/shaderprogram.cpp + framework/graphics/texture.cpp + framework/graphics/texturemanager.cpp + framework/graphics/shadermanager.cpp + framework/platform/win32window.cpp + framework/platform/x11window.cpp + framework/ui/uianchorlayout.cpp + framework/ui/uiboxlayout.cpp + framework/ui/uigridlayout.cpp + framework/ui/uihorizontallayout.cpp + framework/ui/uilayout.cpp + framework/ui/uimanager.cpp + framework/ui/uiparticles.cpp + framework/ui/uitextedit.cpp + framework/ui/uitranslator.cpp + framework/ui/uiverticallayout.cpp + framework/ui/uiwidget.cpp + framework/ui/uiwidgetbasestyle.cpp + framework/ui/uiwidgetimage.cpp + framework/ui/uiwidgettext.cpp + framework/ui/uiqrcode.cpp + ) +endif() + if (TOGGLE_FRAMEWORK_SOUND) set(SOURCE_FILES ${SOURCE_FILES} framework/sound/combinedsoundsource.cpp diff --git a/src/client/attachableobject.cpp b/src/client/attachableobject.cpp index 1a1a7baa0d..9ade368b43 100644 --- a/src/client/attachableobject.cpp +++ b/src/client/attachableobject.cpp @@ -29,6 +29,7 @@ #include #include +#include "client.h" #include "game.h" #include "mapview.h" #include "tile.h" @@ -174,7 +175,7 @@ void AttachableObject::attachWidget(const UIWidgetPtr& widget) { if (!widget) return; - const auto& mapWidget = g_ui.getMapWidget(); + const auto& mapWidget = g_client.getMapWidget(); if (!mapWidget) return; diff --git a/src/client/attachedeffect.cpp b/src/client/attachedeffect.cpp index ba05f08532..a8dd16f571 100644 --- a/src/client/attachedeffect.cpp +++ b/src/client/attachedeffect.cpp @@ -21,12 +21,12 @@ */ #include "attachedeffect.h" -#include "shadermanager.h" #include "gameconfig.h" #include "lightview.h" #include #include +#include AttachedEffectPtr AttachedEffect::clone() { diff --git a/src/client/client.cpp b/src/client/client.cpp index 46821b7f72..69bca88b5d 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -21,14 +21,19 @@ */ #include "client.h" -#include #include "game.h" #include "map.h" +#include "uimap.h" #include "minimap.h" -#include "shadermanager.h" #include "spriteappearances.h" #include "spritemanager.h" +#include +#include +#include +#include +#include + Client g_client; void Client::init(std::vector& /*args*/) @@ -59,4 +64,55 @@ void Client::terminate() g_spriteAppearances.terminate(); g_shaders.terminate(); g_gameConfig.terminate(); -} \ No newline at end of file +} + +void Client::drawMap() +{ + if (g_game.isOnline()) { + if (!m_mapWidget) + m_mapWidget = g_ui.getRootWidget()->recursiveGetChildById("gameMapPanel")->static_self_cast(); + + m_mapWidget->drawSelf(DrawPoolType::MAP); + } else m_mapWidget = nullptr; +} + +void Client::drawForgroundMap() +{ + if (m_mapWidget) + m_mapWidget->drawSelf(DrawPoolType::FOREGROUND_MAP); +} + +bool Client::canDraw(DrawPoolType type) const +{ + switch (type) { + case DrawPoolType::FOREGROUND_MAP: + return g_game.isOnline(); + default: + return false; + } +} + +bool Client::canDrawUI() const +{ + return g_game.isOnline(); +} + +bool Client::canDrawTexts() const +{ + return !g_map.getStaticTexts().empty() || !g_map.getAnimatedTexts().empty(); +} + +bool Client::isLoadingAsyncTexture() +{ + return g_game.isUsingProtobuf(); +} + +bool Client::isUsingProtobuf() +{ + return g_game.isUsingProtobuf(); +} + +void Client::onLoadingAsyncTextureChanged(bool loadingAsync) +{ + g_sprites.reload(); +} diff --git a/src/client/client.h b/src/client/client.h index 475ac18dc3..735498453b 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -24,12 +24,33 @@ #include "global.h" -class Client +#include "uimap.h" + +#include +#include + +class Client : public ApplicationDrawEvents { public: - static void init(std::vector& args); - static void terminate(); + void init(std::vector& args); + void terminate(); static void registerLuaFunctions(); + + void drawMap() override; + void drawForgroundMap() override; + + bool canDraw(DrawPoolType type) const override; + bool canDrawUI() const override; + bool canDrawTexts() const override; + bool isLoadingAsyncTexture() override; + bool isUsingProtobuf() override; + + void onLoadingAsyncTextureChanged(bool loadingAsync) override; + + UIMapPtr getMapWidget() { return m_mapWidget; } + +private: + UIMapPtr m_mapWidget; }; extern Client g_client; diff --git a/src/client/creature.cpp b/src/client/creature.cpp index 89444c4ddc..81de4a960d 100644 --- a/src/client/creature.cpp +++ b/src/client/creature.cpp @@ -26,7 +26,6 @@ #include "localplayer.h" #include "luavaluecasts_client.h" #include "map.h" -#include "shadermanager.h" #include "thingtypemanager.h" #include "tile.h" #include "statictext.h" @@ -37,6 +36,7 @@ #include #include #include +#include double Creature::speedA = 0; double Creature::speedB = 0; @@ -878,7 +878,10 @@ uint16_t Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir) m_stepCache.duration = stepDuration + 10; m_stepCache.walkDuration = std::min(stepDuration / g_gameConfig.getSpriteSize(), DrawPool::FPS60); - m_stepCache.diagonalDuration = stepDuration * (g_game.getClientVersion() > 810 || g_gameConfig.isForcingNewWalkingFormula() ? 3 : 2); + m_stepCache.diagonalDuration = stepDuration * + (g_game.getClientVersion() > 810 || g_gameConfig.isForcingNewWalkingFormula() + ? (isPlayer() ? g_gameConfig.getPlayerDiagonalWalkSpeed() : g_gameConfig.getCreatureDiagonalWalkSpeed()) + : 2); } return ignoreDiagonal ? m_stepCache.duration : m_stepCache.getDuration(m_lastStepDirection); @@ -997,4 +1000,4 @@ bool Creature::canShoot(int distance) { return getTile() ? getTile()->canShoot(distance) : false; } -#endif \ No newline at end of file +#endif diff --git a/src/client/declarations.h b/src/client/declarations.h index d0295209e5..522f069c74 100644 --- a/src/client/declarations.h +++ b/src/client/declarations.h @@ -26,7 +26,7 @@ #include #include "global.h" - // core +// core class Map; class Game; class MapView; diff --git a/src/client/gameconfig.cpp b/src/client/gameconfig.cpp index 24fe10920b..f5325f2a9e 100644 --- a/src/client/gameconfig.cpp +++ b/src/client/gameconfig.cpp @@ -64,6 +64,9 @@ void GameConfig::loadFonts() { m_animatedTextFont = g_fonts.getFont(m_animatedTextFontName); m_staticTextFont = g_fonts.getFont(m_staticTextFontName); m_widgetTextFont = g_fonts.getFont(m_widgetTextFontName); + + if (m_widgetTextFont) + g_fonts.setDefaultWidgetFont(m_widgetTextFont); } void GameConfig::loadGameNode(const OTMLNodePtr& mainNode) { @@ -78,6 +81,8 @@ void GameConfig::loadGameNode(const OTMLNodePtr& mainNode) { loadTileNode(node); else if (node->tag() == "creature") loadCreatureNode(node); + else if (node->tag() == "player") + loadPlayerNode(node); else if (node->tag() == "render") loadRenderNode(node); } @@ -132,6 +137,15 @@ void GameConfig::loadCreatureNode(const OTMLNodePtr& mainNode) { m_volatileSquareDuration = node->value(); else if (node->tag() == "adjust-creature-information-based-crop-size") m_adjustCreatureInformationBasedCropSize = node->value(); + else if (node->tag() == "diagonal-walk-speed") + m_creatureDiagonalWalkSpeed = node->value(); + } +} + +void GameConfig::loadPlayerNode(const OTMLNodePtr& mainNode) { + for (const auto& node : mainNode->children()) { + if (node->tag() == "diagonal-walk-speed") + m_playerDiagonalWalkSpeed = node->value(); } } @@ -152,4 +166,4 @@ void GameConfig::loadRenderNode(const OTMLNodePtr& mainNode) { else if (node->tag() == "min-static-text-duration") m_minStatictextDuration = node->value(); } -}; \ No newline at end of file +}; diff --git a/src/client/gameconfig.h b/src/client/gameconfig.h index 48f39231d7..60f8cf7c92 100644 --- a/src/client/gameconfig.h +++ b/src/client/gameconfig.h @@ -60,6 +60,9 @@ class GameConfig uint16_t getStaticDurationPerCharacter() const { return m_staticDurationPerCharacter; } uint16_t getMinStatictextDuration() const { return m_minStatictextDuration; } + double getPlayerDiagonalWalkSpeed() const { return m_playerDiagonalWalkSpeed; } + double getCreatureDiagonalWalkSpeed() const { return m_creatureDiagonalWalkSpeed; } + BitmapFontPtr getCreatureNameFont() const { return m_creatureNameFont; } BitmapFontPtr getAnimatedTextFont() const { return m_animatedTextFont; } BitmapFontPtr getStaticTextFont() const { return m_staticTextFont; } @@ -73,6 +76,7 @@ class GameConfig void loadMapNode(const OTMLNodePtr& node); void loadTileNode(const OTMLNodePtr& node); void loadCreatureNode(const OTMLNodePtr& node); + void loadPlayerNode(const OTMLNodePtr& node); void loadRenderNode(const OTMLNodePtr& node); // Game @@ -96,6 +100,10 @@ class GameConfig bool m_adjustCreatureInformationBasedCropSize{ false }; uint16_t m_shieldBlinkTicks{ 500 }; uint16_t m_volatileSquareDuration{ 1000 }; + double m_creatureDiagonalWalkSpeed{ 3 }; + + // Player + double m_playerDiagonalWalkSpeed{ 3 }; // Render uint16_t m_invisibleTicksPerFrame{ 500 }; diff --git a/src/client/item.cpp b/src/client/item.cpp index b254d2a422..14f351ae96 100644 --- a/src/client/item.cpp +++ b/src/client/item.cpp @@ -34,8 +34,7 @@ #include #include #include - -#include "shadermanager.h" +#include ItemPtr Item::create(int id) { diff --git a/src/client/item.h b/src/client/item.h index 36295ff6ea..5acf113b13 100644 --- a/src/client/item.h +++ b/src/client/item.h @@ -85,7 +85,7 @@ class Item : public Thing void setColor(const Color& c) { if (m_color != c) m_color = c; } void setPosition(const Position& position, uint8_t stackPos = 0, bool hasElevation = false) override; - int getCountOrSubType() const { return m_countOrSubType; } + int getCountOrSubType() { return m_countOrSubType; } int getSubType(); int getCount() { return isStackable() ? m_countOrSubType : 1; } diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 387b5cc932..3729599ecf 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -37,7 +37,6 @@ #include "outfit.h" #include "player.h" #include "protocolgame.h" -#include "shadermanager.h" #include "attachedeffectmanager.h" #include "spriteappearances.h" #include "spritemanager.h" @@ -364,17 +363,6 @@ void Client::registerLuaFunctions() g_lua.registerSingletonClass("g_gameConfig"); g_lua.bindSingletonFunction("g_gameConfig", "loadFonts", &GameConfig::loadFonts, &g_gameConfig); - g_lua.registerSingletonClass("g_shaders"); - g_lua.bindSingletonFunction("g_shaders", "createShader", &ShaderManager::createShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "createFragmentShader", &ShaderManager::createFragmentShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "createFragmentShaderFromCode", &ShaderManager::createFragmentShaderFromCode, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "setupMapShader", &ShaderManager::setupMapShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "setupItemShader", &ShaderManager::setupItemShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "setupOutfitShader", &ShaderManager::setupOutfitShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "setupMountShader", &ShaderManager::setupMountShader, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "addMultiTexture", &ShaderManager::addMultiTexture, &g_shaders); - g_lua.bindSingletonFunction("g_shaders", "getShader", &ShaderManager::getShader, &g_shaders); - g_lua.registerSingletonClass("g_attachedEffects"); g_lua.bindSingletonFunction("g_attachedEffects", "getById", &AttachedEffectManager::getById, &g_attachedEffects); g_lua.bindSingletonFunction("g_attachedEffects", "registerByThing", &AttachedEffectManager::registerByThing, &g_attachedEffects); @@ -657,12 +645,12 @@ void Client::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("create", &Item::create); - g_lua.bindClassMemberFunction("clone", &Item::clone); g_lua.bindClassMemberFunction("setCount", &Item::setCount); g_lua.bindClassMemberFunction("getCount", &Item::getCount); g_lua.bindClassMemberFunction("getSubType", &Item::getSubType); + g_lua.bindClassMemberFunction("getCountOrSubType", &Item::getCountOrSubType); g_lua.bindClassMemberFunction("getId", &Item::getId); g_lua.bindClassMemberFunction("isStackable", &Item::isStackable); @@ -976,4 +964,4 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("setShowLabels", &UIGraph::setShowLabels); g_lua.registerClass(); -} \ No newline at end of file +} diff --git a/src/client/map.cpp b/src/client/map.cpp index 1aa80a192a..b8978dda27 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -46,6 +46,10 @@ Map g_map; void Map::init() { + g_platform.addKeyListener([&](const InputEvent& inputEvent) { + notificateKeyRelease(inputEvent); + }); + m_floors.resize(g_gameConfig.getMapMaxZ() + 1); resetAwareRange(); diff --git a/src/client/mapview.cpp b/src/client/mapview.cpp index 99e1db4b89..7f4afbacb5 100644 --- a/src/client/mapview.cpp +++ b/src/client/mapview.cpp @@ -28,7 +28,6 @@ #include "lightview.h" #include "map.h" #include "missile.h" -#include "shadermanager.h" #include "statictext.h" #include "tile.h" @@ -37,9 +36,8 @@ #include #include #include - #include "framework/graphics/texturemanager.h" - +#include #include MapView::MapView() : m_pool(g_drawPool.get(DrawPoolType::MAP)), m_lightView(std::make_shared(Size(), g_gameConfig.getSpriteSize())) diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 6d857eaa0c..c6c2e24ccd 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -91,7 +91,7 @@ void ProtocolGame::sendLoginPacket(uint32_t challengeTimestamp, uint8_t challeng if (g_game.getFeature(Otc::GameAccountNames)) msg->addString(m_accountName); else - msg->addU32(stdext::from_string(m_accountName)); + msg->addU32(stdext::from_string(m_accountName)); msg->addString(m_characterName); msg->addString(m_accountPassword); diff --git a/src/client/thing.cpp b/src/client/thing.cpp index a174403d76..e284fd70cc 100644 --- a/src/client/thing.cpp +++ b/src/client/thing.cpp @@ -23,12 +23,12 @@ #include "thing.h" #include "game.h" #include "map.h" -#include "shadermanager.h" #include "thingtypemanager.h" #include "tile.h" #include #include +#include void Thing::setPosition(const Position& position, uint8_t stackPos, bool hasElevation) { diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index 4e1def6bb0..b7a7c589b6 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -610,7 +610,7 @@ void ThingType::drawWithFrameBuffer(const TexturePtr& texture, const Rect& scree g_drawPool.bindFrameBuffer(destDiff.size()); { // Debug - // g_drawPool.addBoundingRect(Rect(Point(0), destDiff.size()), Color::red); + // g_drawPool.addBoundingRect(Rect(Point(0), destDiff.size()), Color::red); g_drawPool.addTexturedRect(Rect(p, screenRect.size()), texture, textureRect, color, conductor); } g_drawPool.releaseFrameBuffer(destDiff); @@ -1066,4 +1066,4 @@ void ThingType::exportImage(const std::string& fileName) image->savePNG(fileName); } -#endif \ No newline at end of file +#endif diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 90aa7dbf18..4e34c59c17 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -26,6 +26,7 @@ #include #include #include +#include "client.h" #include "effect.h" #include "game.h" #include "item.h" @@ -160,14 +161,14 @@ void Tile::drawWidgets(const MapPosInfo& mapRect) void Tile::clean() { - if (g_ui.getMapWidget() + if (g_client.getMapWidget() #ifndef BOT_PROTECTION && (m_text || m_timerText) #endif ) { g_dispatcher.scheduleEvent([tile = static_self_cast()] { - if (g_ui.getMapWidget()) - g_ui.getMapWidget()->getMapView()->removeForegroundTile(tile); + if (g_client.getMapWidget()) + g_client.getMapWidget()->getMapView()->removeForegroundTile(tile); }, g_game.getServerBeat()); } @@ -185,7 +186,7 @@ void Tile::clean() #ifdef FRAMEWORK_EDITOR m_flags = 0; #endif - } +} void Tile::addWalkingCreature(const CreaturePtr& creature) { @@ -910,4 +911,4 @@ bool Tile::canShoot(int distance) return false; return g_map.isSightClear(playerPos, m_position); } -#endif \ No newline at end of file +#endif diff --git a/src/framework/core/application.cpp b/src/framework/core/application.cpp index ec1f2d2908..4896434dc5 100644 --- a/src/framework/core/application.cpp +++ b/src/framework/core/application.cpp @@ -21,7 +21,7 @@ */ #include "application.h" -#include + #include #include #include @@ -32,6 +32,7 @@ #include #include "asyncdispatcher.h" +#include #include #define ADD_QUOTES_HELPER(s) #s @@ -57,8 +58,10 @@ void exitSignalHandler(int sig) } } -void Application::init(std::vector& args, uint8_t asyncDispatchMaxThreads) +void Application::init(std::vector& args, ApplicationContext* context) { + m_context = std::unique_ptr(context); + // capture exit signals signal(SIGTERM, exitSignalHandler); signal(SIGINT, exitSignalHandler); @@ -70,7 +73,7 @@ void Application::init(std::vector& args, uint8_t asyncDispatchMaxT // setup locale std::locale::global(std::locale()); - g_asyncDispatcher.init(asyncDispatchMaxThreads); + g_asyncDispatcher.init(context->getAsyncDispatchMaxThreads()); g_dispatcher.init(); g_textDispatcher.init(); g_mainDispatcher.init(); @@ -148,10 +151,7 @@ void Application::poll() Connection::poll(); #endif - { - std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND)->getMutexPreDraw()); - g_dispatcher.poll(); - } + dispatchPoll(); // poll connection again to flush pending write #ifdef FRAMEWORK_NET @@ -161,6 +161,11 @@ void Application::poll() g_clock.update(); } +void Application::dispatchPoll() +{ + g_dispatcher.poll(); +} + void Application::exit() { g_lua.callGlobalField("g_app", "onExit"); diff --git a/src/framework/core/application.h b/src/framework/core/application.h index 88d4eba1ec..6ffca1ad5f 100644 --- a/src/framework/core/application.h +++ b/src/framework/core/application.h @@ -23,6 +23,19 @@ #pragma once #include +#include + +class ApplicationContext +{ +public: + ApplicationContext(uint8_t asyncDispatchMaxThreads) : m_asyncDispatchMaxThreads(asyncDispatchMaxThreads) {} + + void setAsyncDispatchMaxThreads(uint8_t maxThreads) { m_asyncDispatchMaxThreads = maxThreads; } + uint8_t getAsyncDispatchMaxThreads() { return m_asyncDispatchMaxThreads; } + +protected: + uint8_t m_asyncDispatchMaxThreads; +}; //@bindsingleton g_app class Application @@ -30,11 +43,12 @@ class Application public: virtual ~Application() = default; - virtual void init(std::vector& args, uint8_t asyncDispatchMaxThreads = 0); + virtual void init(std::vector& args, ApplicationContext* context); virtual void deinit(); virtual void terminate(); virtual void run() = 0; virtual void poll(); + virtual void dispatchPoll(); virtual void exit(); virtual void close(); virtual void restart(); @@ -75,6 +89,12 @@ class Application bool m_running{ false }; bool m_terminated{ false }; bool m_stopping{ false }; + + std::unique_ptr m_context; }; +#ifdef FRAMEWORK_GRAPHICS #include "graphicalapplication.h" +#else +#include "consoleapplication.h" +#endif diff --git a/src/framework/core/config.cpp b/src/framework/core/config.cpp index f8beb0e631..b78ff8a7db 100644 --- a/src/framework/core/config.cpp +++ b/src/framework/core/config.cpp @@ -151,6 +151,17 @@ int Config::getNodeSize(const std::string& key) return 0; } +OTMLNodePtr Config::getOrCreateNode(const std::string& key, const OTMLNodePtr& ifNotExists) +{ + OTMLNodePtr node = m_confsDoc->get(key); + if (!node && ifNotExists != nullptr) { + setNode(key, ifNotExists); + node = ifNotExists; + save(); + } + return node; +} + bool Config::isLoaded() const { return !m_fileName.empty() && m_confsDoc; @@ -159,4 +170,4 @@ bool Config::isLoaded() const std::string Config::getFileName() { return m_fileName; -} \ No newline at end of file +} diff --git a/src/framework/core/config.h b/src/framework/core/config.h index b72cdbfedb..535c57a2f7 100644 --- a/src/framework/core/config.h +++ b/src/framework/core/config.h @@ -47,6 +47,7 @@ class Config : public LuaObject void mergeNode(const std::string& key, const OTMLNodePtr& node); OTMLNodePtr getNode(const std::string& key); int getNodeSize(const std::string& key); + OTMLNodePtr getOrCreateNode(const std::string& key, const OTMLNodePtr& ifNotExists); bool exists(const std::string& key); void remove(const std::string& key); diff --git a/src/framework/core/consoleapplication.cpp b/src/framework/core/consoleapplication.cpp new file mode 100644 index 0000000000..98efbb76de --- /dev/null +++ b/src/framework/core/consoleapplication.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2013 OTClient + * + * 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. + */ + + +#include "consoleapplication.h" +#include +#include +#include +#include +#include + +#include + +#ifdef FW_NET +#include +#endif + +ConsoleApplication g_app; + +void ConsoleApplication::run() +{ + // run the first poll + mainPoll(); + poll(); + + g_lua.callGlobalField("g_app", "onRun"); + + // clang c++20 dont support jthread + std::thread t1([&]() { + while (!m_stopping) { + poll(); + + stdext::millisleep(1); + } + }); + + m_running = true; + while (!m_stopping) { + mainPoll(); + } + + t1.join(); + + m_stopping = false; + m_running = false; +} + +void ConsoleApplication::mainPoll() +{ + g_clock.update(); + + // poll window input events + g_mainDispatcher.poll(); +} diff --git a/src/framework/core/consoleapplication.h b/src/framework/core/consoleapplication.h new file mode 100644 index 0000000000..053da03e75 --- /dev/null +++ b/src/framework/core/consoleapplication.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2013 OTClient + * + * 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. + */ + +#pragma once + +#include "application.h" + +class ConsoleApplicationContext : public ApplicationContext +{ +public: + ConsoleApplicationContext(uint8_t asyncDispatchMaxThreads) : + ApplicationContext(asyncDispatchMaxThreads) + {} + +protected: + // reserved +}; + +class ConsoleApplication : public Application +{ +public: + void run() override; + + void mainPoll(); +}; + +extern ConsoleApplication g_app; diff --git a/src/framework/core/event.h b/src/framework/core/event.h index ad23ac1704..17d5dd1286 100644 --- a/src/framework/core/event.h +++ b/src/framework/core/event.h @@ -36,6 +36,7 @@ class Event : public LuaObject bool isCanceled() { return m_canceled; } bool isExecuted() { return m_executed; } + bool isPending() { return !m_canceled && !m_executed; } protected: std::function m_callback; diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index 7cfc06ade6..020f1e35b4 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -56,6 +56,21 @@ void EventDispatcher::poll() executeScheduledEvents(); } +void EventDispatcher::startEvent(const ScheduledEventPtr& event) +{ + if (m_disabled) + return; + + if (!event) { + g_logger.error("EventDispatcher::startEvent called with null event"); + return; + } + + const auto& thread = getThreadTask(); + std::scoped_lock lock(thread->mutex); + thread->scheduledEventList.emplace_back(event); +} + ScheduledEventPtr EventDispatcher::scheduleEvent(const std::function& callback, int delay) { if (m_disabled) diff --git a/src/framework/core/eventdispatcher.h b/src/framework/core/eventdispatcher.h index 21b1222c90..c00853f45e 100644 --- a/src/framework/core/eventdispatcher.h +++ b/src/framework/core/eventdispatcher.h @@ -43,6 +43,8 @@ class EventDispatcher ScheduledEventPtr scheduleEvent(const std::function& callback, int delay); ScheduledEventPtr cycleEvent(const std::function& callback, int delay); + void startEvent(const ScheduledEventPtr& event); + private: inline void mergeEvents(); inline void executeEvents(); diff --git a/src/framework/core/graphicalapplication.cpp b/src/framework/core/graphicalapplication.cpp index 3adab4b811..62a7a02c3b 100644 --- a/src/framework/core/graphicalapplication.cpp +++ b/src/framework/core/graphicalapplication.cpp @@ -21,10 +21,7 @@ */ #include "graphicalapplication.h" -#include -#include -#include -#include + #include #include #include @@ -43,11 +40,16 @@ #include #endif +#include + GraphicalApplication g_app; -void GraphicalApplication::init(std::vector& args, uint8_t asyncDispatchMaxThreads) +void GraphicalApplication::init(std::vector& args, ApplicationContext* context) { - Application::init(args, asyncDispatchMaxThreads); + Application::init(args, context); + + GraphicalApplicationContext* graphicalContext = static_cast(context); + setDrawEvents(graphicalContext->getDrawEvents()); // setup platform window g_window.init(); @@ -72,7 +74,7 @@ void GraphicalApplication::init(std::vector& args, uint8_t asyncDis // initialize graphics g_graphics.init(); - g_drawPool.init(g_gameConfig.getSpriteSize()); + g_drawPool.init(graphicalContext->getSpriteSize()); // fire first resize event resize(g_window.getSize()); @@ -147,13 +149,13 @@ void GraphicalApplication::run() const auto& foregroundMap = g_drawPool.get(DrawPoolType::FOREGROUND_MAP); if (foregroundUI->canRepaint()) { - if (g_game.isOnline()) + if (m_drawEvents && m_drawEvents->canDrawUI()) foregroundUICondition.notify_one(); else g_ui.render(DrawPoolType::FOREGROUND); } - if (g_game.isOnline() && foregroundMap->canRepaint()) + if (m_drawEvents && m_drawEvents->canDraw(DrawPoolType::FOREGROUND_MAP) && foregroundMap->canRepaint()) foregroundMapCondition.notify_one(); }; @@ -172,8 +174,8 @@ void GraphicalApplication::run() const auto& pool = g_drawPool.get(DrawPoolType::FOREGROUND_MAP); std::unique_lock lock(pool->getMutexPreDraw()); condition.wait(lock, [this]() -> bool { - if (g_ui.m_mapWidget) - g_ui.m_mapWidget->drawSelf(DrawPoolType::FOREGROUND_MAP); + if (m_drawEvents) + m_drawEvents->drawForgroundMap(); return m_stopping; }); }); @@ -192,12 +194,8 @@ void GraphicalApplication::run() drawForeground(); - if (g_game.isOnline()) { - if (!g_ui.m_mapWidget) - g_ui.m_mapWidget = g_ui.getRootWidget()->recursiveGetChildById("gameMapPanel")->static_self_cast(); - - g_ui.m_mapWidget->drawSelf(DrawPoolType::MAP); - } else g_ui.m_mapWidget = nullptr; + if (m_drawEvents) + m_drawEvents->drawMap(); m_mapProcessFrameCounter.update(); } @@ -247,6 +245,12 @@ void GraphicalApplication::poll() } } +void GraphicalApplication::dispatchPoll() +{ + std::scoped_lock l(g_drawPool.get(DrawPoolType::FOREGROUND)->getMutexPreDraw()); + Application::dispatchPoll(); +} + void GraphicalApplication::mainPoll() { g_clock.update(); @@ -292,16 +296,17 @@ void GraphicalApplication::inputEvent(const InputEvent& event) void GraphicalApplication::repaintMap() { g_drawPool.get(DrawPoolType::MAP)->repaint(); } void GraphicalApplication::repaint() { g_drawPool.get(DrawPoolType::FOREGROUND)->repaint(); } -bool GraphicalApplication::canDrawTexts() const { return m_drawText && (!g_map.getStaticTexts().empty() || !g_map.getAnimatedTexts().empty()); } - -bool GraphicalApplication::isLoadingAsyncTexture() { return m_loadingAsyncTexture || g_game.isUsingProtobuf(); } +bool GraphicalApplication::canDrawTexts() const { return m_drawText && (!m_drawEvents || m_drawEvents->canDrawTexts()); } +bool GraphicalApplication::isLoadingAsyncTexture() { return m_loadingAsyncTexture || (m_drawEvents && m_drawEvents->isLoadingAsyncTexture()); } void GraphicalApplication::setLoadingAsyncTexture(bool v) { - if (g_game.isUsingProtobuf()) + if (m_drawEvents && m_drawEvents->isUsingProtobuf()) v = true; else if (isEncrypted()) v = false; m_loadingAsyncTexture = v; - g_sprites.reload(); + + if (m_drawEvents) + m_drawEvents->onLoadingAsyncTextureChanged(v); } \ No newline at end of file diff --git a/src/framework/core/graphicalapplication.h b/src/framework/core/graphicalapplication.h index 6ca438527a..7e0cd25193 100644 --- a/src/framework/core/graphicalapplication.h +++ b/src/framework/core/graphicalapplication.h @@ -22,22 +22,59 @@ #pragma once +#include "application.h" + +#include #include #include #include -#include #include -#include "application.h" +class ApplicationDrawEvents +{ +protected: + virtual void drawMap() = 0; + virtual void drawForgroundMap() = 0; + + virtual bool canDraw(DrawPoolType type) const = 0; + virtual bool canDrawUI() const = 0; + virtual bool canDrawTexts() const = 0; + virtual bool isLoadingAsyncTexture() = 0; + virtual bool isUsingProtobuf() = 0; + virtual void onLoadingAsyncTextureChanged(bool loadingAsync) = 0; + + friend class GraphicalApplication; +}; + +class GraphicalApplicationContext : public ApplicationContext +{ +public: + GraphicalApplicationContext(uint8_t asyncDispatchMaxThreads, uint8_t spriteSize, ApplicationDrawEventsPtr drawEvents) : + ApplicationContext(asyncDispatchMaxThreads), + m_spriteSize(spriteSize), + m_drawEvents(drawEvents) + {} + + void setSpriteSize(uint8_t size) { m_spriteSize = size; } + uint8_t getSpriteSize() { return m_spriteSize; } + + void setDrawEvents(ApplicationDrawEventsPtr drawEvents) { m_drawEvents = drawEvents; } + ApplicationDrawEventsPtr getDrawEvents() { return m_drawEvents; } + +protected: + uint8_t m_spriteSize; + ApplicationDrawEventsPtr m_drawEvents; +}; class GraphicalApplication : public Application { public: - void init(std::vector& args, uint8_t asyncDispatchMaxThreads = 0) override; + void init(std::vector& args, ApplicationContext* context) override; void deinit() override; void terminate() override; void run() override; void poll() override; + void dispatchPoll() override; void mainPoll(); void close() override; @@ -94,6 +131,8 @@ class GraphicalApplication : public Application void repaint(); void repaintMap(); + void setDrawEvents(const ApplicationDrawEventsPtr& drawEvents) { m_drawEvents = drawEvents; } + protected: void resize(const Size& size); void inputEvent(const InputEvent& event); @@ -114,6 +153,8 @@ class GraphicalApplication : public Application AdaptativeFrameCounter m_mapProcessFrameCounter; AdaptativeFrameCounter m_graphicFrameCounter; + + ApplicationDrawEventsPtr m_drawEvents; }; extern GraphicalApplication g_app; diff --git a/src/framework/core/logger.cpp b/src/framework/core/logger.cpp index 38fb7516aa..2814964b84 100644 --- a/src/framework/core/logger.cpp +++ b/src/framework/core/logger.cpp @@ -28,7 +28,10 @@ #include #include + +#ifdef FRAMEWORK_GRAPHICS #include +#endif #ifdef ANDROID #include @@ -93,7 +96,9 @@ void Logger::log(Fw::LogLevel level, const std::string_view message) } if (level == Fw::LogFatal) { +#ifdef FRAMEWORK_GRAPHICS g_window.displayFatalError(message); +#endif s_ignoreLogs = true; // NOTE: Threads must finish before the process can exit. diff --git a/src/framework/core/module.cpp b/src/framework/core/module.cpp index 350d85c756..003f20c8f0 100644 --- a/src/framework/core/module.cpp +++ b/src/framework/core/module.cpp @@ -37,6 +37,8 @@ bool Module::load() if (!m_supportedDevices.empty() && !hasSupportedDevice(g_platform.getDevice())) return true; + ticks_t startTime = stdext::millis(); + g_modules.m_currentModule = static_self_cast(); try { // add to package.loaded @@ -83,7 +85,9 @@ bool Module::load() g_lua.resetGlobalEnvironment(); m_loaded = true; - g_logger.debug(stdext::format("Loaded module '%s'", m_name)); + + g_logger.debug(stdext::format("Loaded module '%s' (%s)", m_name, + stdext::format("%.2fs", (stdext::millis() - startTime) / 1000.0))); } catch (const stdext::exception& e) { // remove from package.loaded g_lua.getGlobalField("package", "loaded"); @@ -267,4 +271,4 @@ void Module::discover(const OTMLNodePtr& moduleNode) if (const auto& node = moduleNode->get("@onUnload")) m_onUnloadFunc = std::make_tuple(node->value(), "@" + node->source() + ":[" + node->tag() + "]"); -} \ No newline at end of file +} diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index ca7901a18a..097e1a45a3 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -439,6 +439,11 @@ bool ResourceManager::isFileType(const std::string& filename, const std::string& return false; } +std::string ResourceManager::getFileName(const std::string& filePath) +{ + return std::filesystem::path(filePath).filename().string(); +} + ticks_t ResourceManager::getFileTime(const std::string& filename) { return g_platform.getFileModificationTime(getRealPath(filename)); @@ -729,4 +734,4 @@ std::unordered_map ResourceManager::decompressArchive( { std::unordered_map ret; return ret; -} \ No newline at end of file +} diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index 5eea6509f2..e17d8995d6 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -76,6 +76,7 @@ class ResourceManager std::string guessFilePath(const std::string& filename, const std::string& type); bool isFileType(const std::string& filename, const std::string& type); + std::string getFileName(const std::string& filePath); ticks_t getFileTime(const std::string& filename); std::string encrypt(const std::string& data, const std::string& password); diff --git a/src/framework/core/scheduledevent.h b/src/framework/core/scheduledevent.h index b4cdee2870..03e0c96b42 100644 --- a/src/framework/core/scheduledevent.h +++ b/src/framework/core/scheduledevent.h @@ -29,7 +29,7 @@ class ScheduledEvent : public Event { public: - ScheduledEvent(const std::function& callback, int delay, int maxCycles); + ScheduledEvent(const std::function& callback, int delay, int maxCycles = 0); void execute() override; void postpone() { m_ticks = g_clock.millis() + m_delay; } bool nextCycle(); diff --git a/src/framework/discord/discord.cpp b/src/framework/discord/discord.cpp index 386bdb17d9..db94a2df26 100644 --- a/src/framework/discord/discord.cpp +++ b/src/framework/discord/discord.cpp @@ -24,8 +24,6 @@ #ifndef ANDROID #if ENABLE_DISCORD_RPC == 1 - #include - #include #include #include @@ -33,8 +31,10 @@ Discord g_discord; - void Discord::init() + void Discord::init(std::function& canUpdate, std::function& onUpdate) { + m_canUpdate = canUpdate; + m_onUpdate = onUpdate; DiscordEventHandlers Handle; memset(&Handle, 0, sizeof(Handle)); Discord_Initialize(RPC_API_KEY, &Handle, 1, NULL); @@ -44,20 +44,8 @@ void Discord::update() { std::string info; - if (g_game.isOnline()) { - #if SHOW_CHARACTER_NAME_RPC == 1 - info = "Name: " + g_game.getCharacterName(); - #endif - - #if SHOW_CHARACTER_LEVEL_RPC == 1 - const auto& level = std::to_string(g_game.getLocalPlayer()->getLevel()); - info += info.empty() ? "Level: " + level : "[" + level + "]"; - #endif - - #if SHOW_CHARACTER_WORLD_RPC == 1 - if (!info.empty()) info += "\n"; - info += "World: " + g_game.getWorldName(); - #endif + if (m_canUpdate()) { + m_onUpdate(info); } else { info = std::string{ OFFLINE_RPC_TEXT }; } diff --git a/src/framework/discord/discord.h b/src/framework/discord/discord.h index 5bdfd987be..fb92070051 100644 --- a/src/framework/discord/discord.h +++ b/src/framework/discord/discord.h @@ -23,6 +23,9 @@ #pragma once #include +#include +#include + #ifndef ANDROID #if ENABLE_DISCORD_RPC == 1 #include @@ -31,10 +34,13 @@ class Discord { public: - void init(); + void init(std::function& canUpdate, std::function& onUpdate); private: void update(); + + std::function m_canUpdate; + std::function m_onUpdate; }; extern Discord g_discord; diff --git a/src/framework/graphics/declarations.h b/src/framework/graphics/declarations.h index 7640578a25..1d88ad06a5 100644 --- a/src/framework/graphics/declarations.h +++ b/src/framework/graphics/declarations.h @@ -49,6 +49,9 @@ class SpriteSheet; class DrawPool; class DrawPoolManager; class CoordsBuffer; +class ApplicationDrawEvents; +class ApplicationContext; +class GraphicalApplicationContext; using ImagePtr = std::shared_ptr; using TexturePtr = std::shared_ptr; @@ -66,8 +69,10 @@ using ParticleAffectorPtr = std::shared_ptr; using ParticleSystemPtr = std::shared_ptr; using ParticleEffectPtr = std::shared_ptr; using SpriteSheetPtr = std::shared_ptr; -using ShaderList = std::vector; - +using ApplicationDrawEventsPtr = std::shared_ptr; +using ApplicationContextPtr = std::shared_ptr; +using GraphicalApplicationContextPtr = std::shared_ptr; using CoordsBufferPtr = std::shared_ptr; - using ParticleEffectTypePtr = std::shared_ptr; + +using ShaderList = std::vector; \ No newline at end of file diff --git a/src/framework/graphics/fontmanager.cpp b/src/framework/graphics/fontmanager.cpp index 7d7aa74cb7..dc65dfd650 100644 --- a/src/framework/graphics/fontmanager.cpp +++ b/src/framework/graphics/fontmanager.cpp @@ -23,8 +23,6 @@ #include "fontmanager.h" #include "texture.h" -#include - #include #include @@ -60,6 +58,8 @@ bool FontManager::importFont(const std::string& file) // set as default if needed if (!m_defaultFont || fontNode->valueAt("default", false)) m_defaultFont = font; + else if (!m_defaultWidgetFont || fontNode->valueAt("widget-default", false)) + m_defaultWidgetFont = font; return true; } catch (const stdext::exception& e) { diff --git a/src/framework/graphics/fontmanager.h b/src/framework/graphics/fontmanager.h index 2abcb233a3..82f49db64c 100644 --- a/src/framework/graphics/fontmanager.h +++ b/src/framework/graphics/fontmanager.h @@ -37,10 +37,15 @@ class FontManager BitmapFontPtr getFont(const std::string_view fontName); BitmapFontPtr getDefaultFont() const { return m_defaultFont; } + BitmapFontPtr getDefaultWidgetFont() const { return m_defaultWidgetFont; } + + void setDefaultFont(const BitmapFontPtr& font) { m_defaultFont = font; } + void setDefaultWidgetFont(const BitmapFontPtr& font) { m_defaultWidgetFont = font; } private: std::vector m_fonts; BitmapFontPtr m_defaultFont; + BitmapFontPtr m_defaultWidgetFont; }; extern FontManager g_fonts; diff --git a/src/framework/graphics/shadermanager.cpp b/src/framework/graphics/shadermanager.cpp new file mode 100644 index 0000000000..cdd8dc02f8 --- /dev/null +++ b/src/framework/graphics/shadermanager.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * 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. + */ + +#include "shadermanager.h" + +#include +#include +#include +#include +#include + +ShaderManager g_shaders; + +void ShaderManager::init() { PainterShaderProgram::release(); } +void ShaderManager::terminate() { m_shaders.clear(); } + +void ShaderManager::createShader(const std::string_view name, bool useFramebuffer) +{ + g_mainDispatcher.addEvent([this, name = name.data(), useFramebuffer] { + const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); + m_shaders[name] = shader; + return shader; + }); +} + +void ShaderManager::createFragmentShader(const std::string_view name, const std::string_view file, bool useFramebuffer) +{ + const auto& filePath = g_resources.resolvePath(file.data()); + g_mainDispatcher.addEvent([this, name = name.data(), filePath, useFramebuffer] { + const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); + if (!shader) + return; + + const auto& path = g_resources.guessFilePath(filePath, "frag"); + + shader->addShaderFromSourceCode(ShaderType::VERTEX, std::string{ glslMainWithTexCoordsVertexShader } + glslPositionOnlyVertexShader.data()); + if (!shader->addShaderFromSourceFile(ShaderType::FRAGMENT, path)) { + g_logger.error(stdext::format("unable to load fragment shader '%s' from source file '%s'", name, path)); + return; + } + + if (!shader->link()) { + g_logger.error(stdext::format("unable to link shader '%s' from file '%s'", name, path)); + return; + } + + m_shaders[name] = shader; + }); +} + +void ShaderManager::createFragmentShaderFromCode(const std::string_view name, const std::string_view code, bool useFramebuffer) +{ + g_mainDispatcher.addEvent([this, name = name.data(), code = code.data(), useFramebuffer] { + const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); + if (!shader) + return; + + shader->addShaderFromSourceCode(ShaderType::VERTEX, std::string{ glslMainWithTexCoordsVertexShader } + glslPositionOnlyVertexShader.data()); + if (!shader->addShaderFromSourceCode(ShaderType::FRAGMENT, code)) { + g_logger.error(stdext::format("unable to load fragment shader '%s'", name)); + return; + } + + if (!shader->link()) { + g_logger.error(stdext::format("unable to link shader '%s'", name)); + return; + } + + m_shaders[name] = shader; + }); +} + +void ShaderManager::setupItemShader(const std::string_view name) +{ + g_mainDispatcher.addEvent([&, name = name.data()] { + const auto& shader = getShader(name); + if (!shader) return; + shader->bindUniformLocation(ITEM_ID_UNIFORM, "u_ItemId"); + }); +} + +void ShaderManager::setupOutfitShader(const std::string_view name) +{ + g_mainDispatcher.addEvent([&, name = name.data()] { + const auto& shader = getShader(name); + if (!shader) return; + shader->bindUniformLocation(OUTFIT_ID_UNIFORM, "u_OutfitId"); + }); +} + +void ShaderManager::setupMountShader(const std::string_view name) +{ + g_mainDispatcher.addEvent([&, name = name.data()] { + const auto& shader = getShader(name); + if (!shader) return; + shader->bindUniformLocation(MOUNT_ID_UNIFORM, "u_MountId"); + }); +} + +void ShaderManager::setupMapShader(const std::string_view name) +{ + g_mainDispatcher.addEvent([&, name = name.data()] { + const auto& shader = getShader(name); + if (!shader) return; + shader->bindUniformLocation(MAP_CENTER_COORD, "u_MapCenterCoord"); + shader->bindUniformLocation(MAP_GLOBAL_COORD, "u_MapGlobalCoord"); + shader->bindUniformLocation(MAP_WALKOFFSET, "u_WalkOffset"); + shader->bindUniformLocation(MAP_ZOOM, "u_MapZoom"); + }); +} + +void ShaderManager::addMultiTexture(const std::string_view name, const std::string_view file) +{ + const auto& filePath = g_resources.resolvePath(file.data()); + g_mainDispatcher.addEvent([&, name = name.data(), filePath] { + const auto& shader = getShader(name); + if (!shader) return; + shader->addMultiTexture(filePath); + }); +} + +PainterShaderProgramPtr ShaderManager::getShader(const std::string_view name) +{ + const auto it = m_shaders.find(name.data()); + if (it != m_shaders.end()) + return it->second; + + return nullptr; +} \ No newline at end of file diff --git a/src/client/shadermanager.h b/src/framework/graphics/shadermanager.h similarity index 97% rename from src/client/shadermanager.h rename to src/framework/graphics/shadermanager.h index 969b425452..4ccc2bde07 100644 --- a/src/client/shadermanager.h +++ b/src/framework/graphics/shadermanager.h @@ -44,6 +44,7 @@ class ShaderManager void init(); void terminate(); + // TODO: Move these setup methods to a ClientShaderManager void setupMapShader(const std::string_view name); void setupItemShader(const std::string_view name); void setupOutfitShader(const std::string_view name); diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 883d7c333f..1a88fe3a0a 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -27,20 +27,22 @@ #include #include #include -#include #include #include #include #include -#include "graphics/particleeffect.h" - +#ifdef FRAMEWORK_GRAPHICS +#include "framework/graphics/particleeffect.h" +#include "framework/graphics/texturemanager.h" #include "framework/graphics/fontmanager.h" #include "framework/graphics/graphics.h" #include "framework/graphics/particlemanager.h" +#include "framework/graphics/shadermanager.h" #include "framework/input/mouse.h" #include "framework/platform/platformwindow.h" #include "framework/ui/ui.h" +#endif #ifdef FRAMEWORK_SOUND #include @@ -232,6 +234,7 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_resources", "writeFileContents", &ResourceManager::writeFileContents, &g_resources); g_lua.bindSingletonFunction("g_resources", "guessFilePath", &ResourceManager::guessFilePath, &g_resources); g_lua.bindSingletonFunction("g_resources", "isFileType", &ResourceManager::isFileType, &g_resources); + g_lua.bindSingletonFunction("g_resources", "getFileName", &ResourceManager::getFileName, &g_resources); g_lua.bindSingletonFunction("g_resources", "getFileTime", &ResourceManager::getFileTime, &g_resources); g_lua.bindSingletonFunction("g_resources", "makeDir", &ResourceManager::makeDir, &g_resources); g_lua.bindSingletonFunction("g_resources", "deleteFile", &ResourceManager::deleteFile, &g_resources); @@ -256,6 +259,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("setNode", &Config::setNode); g_lua.bindClassMemberFunction("getNode", &Config::getNode); g_lua.bindClassMemberFunction("getNodeSize", &Config::getNodeSize); + g_lua.bindClassMemberFunction("getOrCreateNode", &Config::getOrCreateNode); g_lua.bindClassMemberFunction("mergeNode", &Config::mergeNode); g_lua.bindClassMemberFunction("getFileName", &Config::getFileName); @@ -294,6 +298,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("cyclesExecuted", &ScheduledEvent::cyclesExecuted); g_lua.bindClassMemberFunction("maxCycles", &ScheduledEvent::maxCycles); +#ifdef FRAMEWORK_GRAPHICS // GraphicalApplication g_lua.bindSingletonFunction("g_app", "isOnInputEvent", &GraphicalApplication::isOnInputEvent, &g_app); @@ -412,6 +417,18 @@ void Application::registerLuaFunctions() g_lua.bindSingletonFunction("g_particles", "getEffectsTypes", &ParticleManager::getEffectsTypes, &g_particles); g_lua.bindSingletonFunction("g_particles", "terminate", &ParticleManager::terminate, &g_particles); + // ShaderManager + g_lua.registerSingletonClass("g_shaders"); + g_lua.bindSingletonFunction("g_shaders", "createShader", &ShaderManager::createShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "createFragmentShader", &ShaderManager::createFragmentShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "createFragmentShaderFromCode", &ShaderManager::createFragmentShaderFromCode, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "setupMapShader", &ShaderManager::setupMapShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "setupItemShader", &ShaderManager::setupItemShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "setupOutfitShader", &ShaderManager::setupOutfitShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "setupMountShader", &ShaderManager::setupMountShader, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "addMultiTexture", &ShaderManager::addMultiTexture, &g_shaders); + g_lua.bindSingletonFunction("g_shaders", "getShader", &ShaderManager::getShader, &g_shaders); + // UIWidget g_lua.registerClass(); g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); @@ -448,6 +465,9 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("bindRectToParent", &UIWidget::bindRectToParent); g_lua.bindClassMemberFunction("destroy", &UIWidget::destroy); g_lua.bindClassMemberFunction("destroyChildren", &UIWidget::destroyChildren); + g_lua.bindClassMemberFunction("removeChildren", &UIWidget::removeChildren); + g_lua.bindClassMemberFunction("hideChildren", &UIWidget::hideChildren); + g_lua.bindClassMemberFunction("showChildren", &UIWidget::showChildren); g_lua.bindClassMemberFunction("setId", &UIWidget::setId); g_lua.bindClassMemberFunction("setParent", &UIWidget::setParent); g_lua.bindClassMemberFunction("setLayout", &UIWidget::setLayout); @@ -504,6 +524,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("isDisabled", &UIWidget::isDisabled); g_lua.bindClassMemberFunction("isFocused", &UIWidget::isFocused); g_lua.bindClassMemberFunction("isHovered", &UIWidget::isHovered); + g_lua.bindClassMemberFunction("isChildHovered", &UIWidget::isChildHovered); g_lua.bindClassMemberFunction("isPressed", &UIWidget::isPressed); g_lua.bindClassMemberFunction("isFirst", &UIWidget::isFirst); g_lua.bindClassMemberFunction("isMiddle", &UIWidget::isMiddle); @@ -521,14 +542,19 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("isFixedSize", &UIWidget::isFixedSize); g_lua.bindClassMemberFunction("isClipping", &UIWidget::isClipping); g_lua.bindClassMemberFunction("isDestroyed", &UIWidget::isDestroyed); + g_lua.bindClassMemberFunction("isFirstOnStyle", &UIWidget::isFirstOnStyle); g_lua.bindClassMemberFunction("isTextWrap", &UIWidget::isTextWrap); g_lua.bindClassMemberFunction("hasChildren", &UIWidget::hasChildren); g_lua.bindClassMemberFunction("containsMarginPoint", &UIWidget::containsMarginPoint); g_lua.bindClassMemberFunction("containsPaddingPoint", &UIWidget::containsPaddingPoint); g_lua.bindClassMemberFunction("containsPoint", &UIWidget::containsPoint); + g_lua.bindClassMemberFunction("intersects", &UIWidget::intersects); + g_lua.bindClassMemberFunction("intersectsMargin", &UIWidget::intersectsMargin); + g_lua.bindClassMemberFunction("intersectsPadding", &UIWidget::intersectsPadding); g_lua.bindClassMemberFunction("getId", &UIWidget::getId); g_lua.bindClassMemberFunction("getParent", &UIWidget::getParent); g_lua.bindClassMemberFunction("getFocusedChild", &UIWidget::getFocusedChild); + g_lua.bindClassMemberFunction("getHoveredChild", &UIWidget::getHoveredChild); g_lua.bindClassMemberFunction("getChildren", &UIWidget::getChildren); g_lua.bindClassMemberFunction("getFirstChild", &UIWidget::getFirstChild); g_lua.bindClassMemberFunction("getLastChild", &UIWidget::getLastChild); @@ -602,6 +628,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("getX", &UIWidget::getX); g_lua.bindClassMemberFunction("getY", &UIWidget::getY); g_lua.bindClassMemberFunction("getPosition", &UIWidget::getPosition); + g_lua.bindClassMemberFunction("getCenter", &UIWidget::getCenter); g_lua.bindClassMemberFunction("getWidth", &UIWidget::getWidth); g_lua.bindClassMemberFunction("getHeight", &UIWidget::getHeight); g_lua.bindClassMemberFunction("getSize", &UIWidget::getSize); @@ -707,7 +734,6 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("getFont", &UIWidget::getFont); g_lua.bindClassMemberFunction("getTextSize", &UIWidget::getTextSize); g_lua.bindClassMemberFunction("hasShader", &UIWidget::hasShader); - g_lua.bindClassMemberFunction("setQRCode", &UIWidget::setQRCode); // UILayout g_lua.registerClass(); @@ -817,6 +843,14 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("isShiftNavigation", &UITextEdit::isShiftNavigation); g_lua.bindClassMemberFunction("isMultiline", &UITextEdit::isMultiline); + // UIQrCode + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); + g_lua.bindClassMemberFunction("getCode", &UIQrCode::getCode); + g_lua.bindClassMemberFunction("getCodeBorder", &UIQrCode::getCodeBorder); + g_lua.bindClassMemberFunction("setCode", &UIQrCode::setCode); + g_lua.bindClassMemberFunction("setCodeBorder", &UIQrCode::setCodeBorder); + // Shader g_lua.registerClass(); g_lua.registerClass(); @@ -832,6 +866,7 @@ void Application::registerLuaFunctions() g_lua.registerClass(); g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); g_lua.bindClassMemberFunction("addEffect", &UIParticles::addEffect); +#endif #ifdef FRAMEWORK_NET // Server diff --git a/src/framework/otml/otmlparser.cpp b/src/framework/otml/otmlparser.cpp index 2be9b70bde..df8d3f8489 100644 --- a/src/framework/otml/otmlparser.cpp +++ b/src/framework/otml/otmlparser.cpp @@ -93,7 +93,7 @@ void OTMLParser::parseLine(std::string line) return; // skip comments - if (line.starts_with("//")) + if (line.starts_with("//") || line.starts_with("#")) return; // a depth above, change current parent to the previous added node @@ -208,4 +208,4 @@ void OTMLParser::parseNode(const std::string_view data) currentParent->addChild(node); parentMap[node] = currentParent; previousNode = node; -} \ No newline at end of file +} diff --git a/src/framework/platform/platform.h b/src/framework/platform/platform.h index 53bdd94935..161379a72a 100644 --- a/src/framework/platform/platform.h +++ b/src/framework/platform/platform.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -83,6 +84,7 @@ class Platform std::string getDeviceShortName(DeviceType type = DeviceUnknown); std::string getOsShortName(OperatingSystem os = OsUnknown); std::string traceback(const std::string_view where, int level = 1, int maxDepth = 32); + void addKeyListener(std::function listener) {} static Platform::DeviceType getDeviceTypeByName(std::string shortName); static Platform::OperatingSystem getOsByName(std::string shortName); diff --git a/src/framework/platform/platformwindow.h b/src/framework/platform/platformwindow.h index 7cc3f34aac..765e7ca0ee 100644 --- a/src/framework/platform/platformwindow.h +++ b/src/framework/platform/platformwindow.h @@ -100,6 +100,8 @@ class PlatformWindow void setOnResize(const OnResizeCallback& onResize) { m_onResize = onResize; } void setOnInputEvent(const OnInputEventCallback& onInputEvent) { m_onInputEvent = onInputEvent; } + void addKeyListener(std::function listener) { m_keyListeners.push_back(listener); } + protected: virtual int internalLoadMouseCursor(const ImagePtr& image, const Point& hotSpot) = 0; @@ -136,6 +138,8 @@ class PlatformWindow std::function m_onClose; OnResizeCallback m_onResize; OnInputEventCallback m_onInputEvent; + + std::vector> m_keyListeners; }; extern PlatformWindow& g_window; diff --git a/src/framework/platform/win32crashhandler.cpp b/src/framework/platform/win32crashhandler.cpp index 39dcc26057..15f51044ec 100644 --- a/src/framework/platform/win32crashhandler.cpp +++ b/src/framework/platform/win32crashhandler.cpp @@ -20,7 +20,7 @@ * THE SOFTWARE. */ -#include "framework/core/graphicalapplication.h" +#include "framework/core/application.h" #if defined(WIN32) && defined(CRASH_HANDLER) #include @@ -182,4 +182,4 @@ void installCrashHandler() SetUnhandledExceptionFilter(ExceptionHandler); } -#endif \ No newline at end of file +#endif diff --git a/src/framework/platform/win32platform.cpp b/src/framework/platform/win32platform.cpp index 4f4cef364d..112811f532 100644 --- a/src/framework/platform/win32platform.cpp +++ b/src/framework/platform/win32platform.cpp @@ -22,7 +22,6 @@ #ifdef WIN32 - #include "platform.h" #include #include @@ -451,4 +450,4 @@ std::string Platform::traceback(const std::string_view where, int, int) return ss.str(); } -#endif \ No newline at end of file +#endif diff --git a/src/framework/platform/win32window.cpp b/src/framework/platform/win32window.cpp index 5402edd18c..62480dbf5a 100644 --- a/src/framework/platform/win32window.cpp +++ b/src/framework/platform/win32window.cpp @@ -23,9 +23,7 @@ #ifdef WIN32 #include "win32window.h" -#include #include -#include #include #include #include @@ -582,7 +580,7 @@ LRESULT WIN32Window::windowProc(HWND hWnd, uint32_t uMsg, WPARAM wParam, LPARAM m_inputEvent.keyboardModifiers |= Fw::KeyboardAltModifier; #endif - bool notificateMapKeyEvent = false; + bool signalKeyEvent = false; switch (uMsg) { case WM_SETCURSOR: { @@ -621,19 +619,19 @@ LRESULT WIN32Window::windowProc(HWND hWnd, uint32_t uMsg, WPARAM wParam, LPARAM } case WM_KEYDOWN: { - notificateMapKeyEvent = true; + signalKeyEvent = true; processKeyDown(retranslateVirtualKey(wParam, lParam)); break; } case WM_KEYUP: { - notificateMapKeyEvent = true; + signalKeyEvent = true; processKeyUp(retranslateVirtualKey(wParam, lParam)); break; } case WM_SYSKEYUP: { - notificateMapKeyEvent = true; + signalKeyEvent = true; processKeyUp(retranslateVirtualKey(wParam, lParam)); break; } @@ -642,7 +640,7 @@ LRESULT WIN32Window::windowProc(HWND hWnd, uint32_t uMsg, WPARAM wParam, LPARAM if (wParam == VK_F4 && m_inputEvent.keyboardModifiers & Fw::KeyboardAltModifier) return DefWindowProc(hWnd, uMsg, wParam, lParam); - notificateMapKeyEvent = true; + signalKeyEvent = true; processKeyDown(retranslateVirtualKey(wParam, lParam)); break; } @@ -817,8 +815,9 @@ LRESULT WIN32Window::windowProc(HWND hWnd, uint32_t uMsg, WPARAM wParam, LPARAM return DefWindowProc(hWnd, uMsg, wParam, lParam); } - if (m_inputEvent.keyboardModifiers || notificateMapKeyEvent) { - g_map.notificateKeyRelease(m_inputEvent); + if (m_inputEvent.keyboardModifiers || signalKeyEvent) { + for (auto& keyListener : m_keyListeners) + keyListener(m_inputEvent); } return 0; @@ -1097,4 +1096,4 @@ Rect WIN32Window::adjustWindowRect(const Rect& clientRect) const return rect; } -#endif \ No newline at end of file +#endif diff --git a/src/framework/stdext/exception.h b/src/framework/stdext/exception.h index 518f77375e..8d9216792c 100644 --- a/src/framework/stdext/exception.h +++ b/src/framework/stdext/exception.h @@ -38,7 +38,7 @@ namespace stdext template exception(std::string_view what, const Args&... args) : m_what(stdext::format({ what }, args...)) {} - ~exception() noexcept override = default;; + ~exception() noexcept override = default; const char* what() const noexcept override { return m_what.data(); } protected: std::string m_what; diff --git a/src/framework/stdext/math.cpp b/src/framework/stdext/math.cpp index 8e99e4f3b9..dc3955f392 100644 --- a/src/framework/stdext/math.cpp +++ b/src/framework/stdext/math.cpp @@ -20,6 +20,7 @@ * THE SOFTWARE. */ +#include "math.h" #include #include #include @@ -62,4 +63,30 @@ namespace stdext static std::uniform_real_distribution dis(0.0, 1.0); return min + (max - min) * dis(gen); } -} \ No newline at end of file + + std::mt19937& random_gen() + { + static std::random_device rd; + static std::mt19937 generator(rd()); + return generator; + } + + bool random_bool(double probability) + { + static std::bernoulli_distribution booleanRand; + return booleanRand(random_gen(), std::bernoulli_distribution::param_type(probability)); + } + + int32_t normal_random(int32_t minNumber, int32_t maxNumber) + { + static std::normal_distribution normalRand(0.5f, 0.25f); + + float v; + do { + v = normalRand(stdext::random_gen()); + } while (v < 0.0 || v > 1.0); + + auto&& [a, b] = std::minmax(minNumber, maxNumber); + return a + std::lround(v * (b - a)); + } +} diff --git a/src/framework/stdext/math.h b/src/framework/stdext/math.h index c1851f9fed..8e89c16f46 100644 --- a/src/framework/stdext/math.h +++ b/src/framework/stdext/math.h @@ -24,8 +24,13 @@ #include "types.h" +#include + namespace stdext { + inline bool is_power_of_two(size_t v) { return ((v != 0) && !(v & (v - 1))); } + inline size_t to_power_of_two(size_t v) { if (v == 0) return 0; size_t r = 1; while (r < v && r != 0xffffffff) r <<= 1; return r; } + inline uint16_t readULE16(const uint8_t* addr) { return static_cast(addr[1]) << 8 | addr[0]; } inline uint32_t readULE32(const uint8_t* addr) { return static_cast(readULE16(addr + 2)) << 16 | readULE16(addr); } inline uint64_t readULE64(const uint8_t* addr) { return static_cast(readULE32(addr + 4)) << 32 | readULE32(addr); } @@ -45,4 +50,9 @@ namespace stdext uint32_t adler32(const uint8_t* buffer, size_t size); int random_range(int min, int max); float random_range(float min, float max); + int32_t normal_random(int32_t minNumber, int32_t maxNumber); + bool random_bool(double probability = 0.5); + std::mt19937& random_gen(); + + inline static uint32_t circularShift(int bits, uint32_t value) { return (value << bits) | (value >> (32 - bits)); } } diff --git a/src/framework/stdext/net.h b/src/framework/stdext/net.h index 1175971d35..8de681cd5e 100644 --- a/src/framework/stdext/net.h +++ b/src/framework/stdext/net.h @@ -30,5 +30,5 @@ namespace stdext { std::string ip_to_string(uint32_t ip); uint32_t string_to_ip(const std::string_view string); - std::vector listSubnetAddresses(uint32_t address, uint8_t mask); + std::vector listSubnetAddresses(uint32_t address, uint8_t mask); } diff --git a/src/framework/stdext/storage.h b/src/framework/stdext/storage.h index b6976a4cd4..f6a58e70b8 100644 --- a/src/framework/stdext/storage.h +++ b/src/framework/stdext/storage.h @@ -30,13 +30,13 @@ namespace stdext class Hash = phmap::priv::hash_default_hash, class Eq = phmap::priv::hash_default_eq, class Alloc = phmap::priv::Allocator>> - using map = phmap::flat_hash_map< K, V, Hash, Eq, Alloc>; + using map = phmap::flat_hash_map; template , class Eq = phmap::priv::hash_default_eq, class Alloc = phmap::priv::Allocator> - using set = phmap::flat_hash_set; + using set = phmap::flat_hash_set; template concept OnlyEnum = std::is_enum_v; diff --git a/src/framework/ui/declarations.h b/src/framework/ui/declarations.h index 4aebd615af..ec7c1a3ae1 100644 --- a/src/framework/ui/declarations.h +++ b/src/framework/ui/declarations.h @@ -38,9 +38,7 @@ class UIAnchor; class UIAnchorGroup; class UIAnchorLayout; class UIParticles; -class UIMap; -using UIMapPtr = std::shared_ptr; using UIWidgetPtr = std::shared_ptr; using UIParticlesPtr = std::shared_ptr; using UITextEditPtr = std::shared_ptr; diff --git a/src/framework/ui/ui.h b/src/framework/ui/ui.h index 9f30b5a61e..99316b8cb2 100644 --- a/src/framework/ui/ui.h +++ b/src/framework/ui/ui.h @@ -31,3 +31,4 @@ #include "uitextedit.h" #include "uiverticallayout.h" #include "uiwidget.h" +#include "uiqrcode.h" diff --git a/src/framework/ui/uianchorlayout.h b/src/framework/ui/uianchorlayout.h index d0f0dce63d..064dfb6af0 100644 --- a/src/framework/ui/uianchorlayout.h +++ b/src/framework/ui/uianchorlayout.h @@ -26,7 +26,7 @@ #include "uilayout.h" -class UIAnchor :public std::enable_shared_from_this +class UIAnchor : public std::enable_shared_from_this { public: virtual ~UIAnchor() {} // fix clang warning diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index abde91c634..72fb3a8235 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -293,6 +293,7 @@ void UIManager::onWidgetDestroy(const UIWidgetPtr& widget) if (m_draggingWidget == widget) updateDraggingWidget(nullptr); + // Avoid the garbage collector if (!g_modules.isAutoReloadEnabled()) return; @@ -599,4 +600,4 @@ UIWidgetPtr UIManager::createWidgetFromOTML(const OTMLNodePtr& widgetNode, const widget->callLuaField("onSetup"); return widget; -} \ No newline at end of file +} diff --git a/src/framework/ui/uimanager.h b/src/framework/ui/uimanager.h index 199c89662c..b778dd409c 100644 --- a/src/framework/ui/uimanager.h +++ b/src/framework/ui/uimanager.h @@ -50,7 +50,6 @@ class UIManager std::string getStyleClass(const std::string_view styleName); OTMLNodePtr findMainWidgetNode(const OTMLDocumentPtr& doc); - UIMapPtr getMapWidget() const { return m_mapWidget; } UIWidgetPtr loadUI(const std::string& file, const UIWidgetPtr& parent); UIWidgetPtr loadUIFromString(const std::string& data, const UIWidgetPtr& parent); OTMLNodePtr loadDeviceUI(const std::string& file, Platform::OperatingSystem os); @@ -70,6 +69,7 @@ class UIManager UIWidgetPtr getHoveredWidget() { return m_hoveredWidget; } UIWidgetPtr getPressedWidget() { return m_pressedWidget; } UIWidgetPtr getRootWidget() { return m_rootWidget; } + bool isMouseGrabbed() { return m_mouseReceiver != m_rootWidget; } bool isKeyboardGrabbed() { return m_keyboardReceiver != m_rootWidget; } @@ -85,7 +85,6 @@ class UIManager private: UIWidgetPtr m_rootWidget; - UIMapPtr m_mapWidget; UIWidgetPtr m_mouseReceiver; UIWidgetPtr m_keyboardReceiver; UIWidgetPtr m_draggingWidget; diff --git a/src/framework/ui/uiqrcode.cpp b/src/framework/ui/uiqrcode.cpp new file mode 100644 index 0000000000..b1141edea7 --- /dev/null +++ b/src/framework/ui/uiqrcode.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * 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. + */ + +#include "uiqrcode.h" + +#include + +void UIQrCode::parseCustomStyle(const OTMLNodePtr& styleNode) +{ + UIWidget::parseCustomStyle(styleNode); + + for (const auto& node : styleNode->children()) { + if (node->tag() == "code") + setCode(node->value(), getCodeBorder()); + else if (node->tag() == "code-border") + setCodeBorder(node->value()); + } +} + +void UIQrCode::setCode(const std::string& code, int border) +{ + if (code.empty()) { + m_imageTexture = nullptr; + m_qrCode = {}; + return; + } + + m_qrCode = code; + m_imageTexture = TexturePtr(new Texture(Image::fromQRCode(code, border))); + + if (m_imageTexture && (!m_rect.isValid() || isImageAutoResize())) { + const auto& imageSize = m_imageTexture->getSize(); + + Size size = getSize(); + if (size.width() <= 0 || hasProp(PropImageAutoResize)) + size.setWidth(imageSize.width()); + + if (size.height() <= 0 || hasProp(PropImageAutoResize)) + size.setHeight(imageSize.height()); + + setSize(size); + } +} diff --git a/src/framework/ui/uiqrcode.h b/src/framework/ui/uiqrcode.h new file mode 100644 index 0000000000..0937138927 --- /dev/null +++ b/src/framework/ui/uiqrcode.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * 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. + */ + +#pragma once + +#include "declarations.h" + +#include "uiwidget.h" + +// @bindclass +class UIQrCode : public UIWidget +{ +public: + void setCode(const std::string& code, int border); + void setCodeBorder(int border) { m_qrCodeBorder = border; setCode(m_qrCode, border); } + + std::string getCode() { return m_qrCode; } + int getCodeBorder() { return m_qrCodeBorder; } + +private: + void parseCustomStyle(const OTMLNodePtr& styleNode) override; + +protected: + std::string m_qrCode; + uint32_t m_qrCodeBorder{ 1 }; +}; diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 29da4002c8..80b0d96935 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -30,9 +30,8 @@ #include #include #include - #include "framework/graphics/drawpoolmanager.h" -#include +#include "framework/graphics/shadermanager.h" UIWidget::UIWidget() { @@ -152,7 +151,7 @@ void UIWidget::addChild(const UIWidgetPtr& child) } if (child->isDestroyed()) { - g_logger.traceWarning("attemp to add a destroyed child into a UIWidget"); + g_logger.traceWarning("attempt to add a destroyed child into a UIWidget"); return; } @@ -916,6 +915,34 @@ void UIWidget::destroyChildren() layout->enableUpdates(); } +void UIWidget::removeChildren() +{ + UILayoutPtr layout = getLayout(); + if (layout) + layout->disableUpdates(); + + m_focusedChild = nullptr; + m_lockedChildren.clear(); + while (!m_children.empty()) { + removeChild(m_children.front()); + } + + if (layout) + layout->enableUpdates(); +} + +void UIWidget::hideChildren() +{ + for (auto& child : m_children) + child->hide(); +} + +void UIWidget::showChildren() +{ + for (auto& child : m_children) + child->show(); +} + void UIWidget::setId(const std::string_view id) { if (id == m_id) @@ -1664,6 +1691,7 @@ void UIWidget::onStyleApply(const std::string_view, const OTMLNodePtr& styleNode parseBaseStyle(styleNode); parseImageStyle(styleNode); parseTextStyle(styleNode); + parseCustomStyle(styleNode); g_app.repaint(); } @@ -1791,6 +1819,12 @@ bool UIWidget::onDoubleClick(const Point& mousePos) return callLuaField("onDoubleClick", mousePos); } +UIWidgetPtr UIWidget::getHoveredChild() +{ + const auto& hovered = g_ui.getHoveredWidget(); + return hovered ? getChildById(hovered->getId()) : nullptr; +} + bool UIWidget::propagateOnKeyText(const std::string_view keyText) { // do a backup of children list, because it may change while looping it @@ -1946,4 +1980,4 @@ void UIWidget::setShader(const std::string_view name) { }); } -void UIWidget::repaint() { g_app.repaint(); } \ No newline at end of file +void UIWidget::repaint() { g_app.repaint(); } diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 4f5a8346b2..9e435ca88d 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -148,6 +148,9 @@ class UIWidget : public LuaObject void bindRectToParent(); void destroy(); void destroyChildren(); + void removeChildren(); + void hideChildren(); + void showChildren(); void setId(const std::string_view id); void setParent(const UIWidgetPtr& parent); @@ -172,7 +175,7 @@ class UIWidget : public LuaObject bool isAnchored(); bool isChildLocked(const UIWidgetPtr& child); bool hasChild(const UIWidgetPtr& child); - int getChildIndex(const UIWidgetPtr& child) { return child && child->getParent().get() == this ? child->m_childIndex : -1; } + int getChildIndex(const UIWidgetPtr& child = nullptr) { return child ? (child->getParent().get() == this ? child->m_childIndex : -1) : m_childIndex; } Rect getPaddingRect(); Rect getMarginRect(); Rect getChildrenRect(); @@ -271,7 +274,8 @@ class UIWidget : public LuaObject bool isEnabled() { return !hasState(Fw::DisabledState); } bool isDisabled() { return hasState(Fw::DisabledState); } bool isFocused() { return hasState(Fw::FocusState); } - bool isHovered() { return hasState(Fw::HoverState); } + bool isHovered(bool orChild = false) { return hasState(Fw::HoverState) || (orChild && isChildHovered()); } + bool isChildHovered() { return getHoveredChild() != nullptr; } bool isPressed() { return hasState(Fw::PressedState); } bool isFirst() { return hasState(Fw::FirstState); } bool isMiddle() { return hasState(Fw::MiddleState); } @@ -290,6 +294,7 @@ class UIWidget : public LuaObject bool isFixedSize() { return hasProp(PropFixedSize); } bool isClipping() { return hasProp(PropClipping); } bool isDestroyed() { return hasProp(PropDestroyed); } + bool isFirstOnStyle() { return hasProp(PropFirstOnStyle); } bool isFirstChild() { return m_parent && m_childIndex == 1; } bool isLastChild() { return m_parent && m_childIndex == m_parent->m_children.size(); } @@ -299,10 +304,14 @@ class UIWidget : public LuaObject bool containsMarginPoint(const Point& point) { return getMarginRect().contains(point); } bool containsPaddingPoint(const Point& point) { return getPaddingRect().contains(point); } bool containsPoint(const Point& point) { return m_rect.contains(point); } + bool intersects(const Rect rect) { return m_rect.intersects(rect); } + bool intersectsMargin(const Rect rect) { return getMarginRect().intersects(rect); } + bool intersectsPadding(const Rect rect) { return getPaddingRect().intersects(rect); } std::string getId() { return m_id; } UIWidgetPtr getParent() { return m_parent; } UIWidgetPtr getFocusedChild() { return m_focusedChild; } + UIWidgetPtr getHoveredChild(); UIWidgetList getChildren() { return m_children; } UIWidgetPtr getFirstChild() { return getChildByIndex(1); } UIWidgetPtr getLastChild() { return getChildByIndex(-1); } @@ -406,6 +415,7 @@ class UIWidget : public LuaObject int getX() { return m_rect.x(); } int getY() { return m_rect.y(); } Point getPosition() { return m_rect.topLeft(); } + Point getCenter() { return m_rect.center(); } int getWidth() { return m_rect.width(); } int getHeight() { return m_rect.height(); } Size getSize() { return m_rect.size(); } @@ -470,7 +480,6 @@ class UIWidget : public LuaObject protected: void drawImage(const Rect& screenCoords); std::string m_imageSource; - std::string m_qrCode; TexturePtr m_imageTexture; Rect m_imageClipRect; @@ -479,8 +488,7 @@ class UIWidget : public LuaObject Point m_iconOffset; Timer m_imageAnimatorTimer; uint32_t m_currentFrame{ 0 }; - uint32_t m_qrCodeBorder{ 1 }; - + EdgeGroup m_imageBorder; public: @@ -504,8 +512,6 @@ class UIWidget : public LuaObject void setImageBorderBottom(int border) { m_imageBorder.bottom = border; configureBorderImage(); } void setImageBorderLeft(int border) { m_imageBorder.left = border; configureBorderImage(); } void setImageBorder(int border) { m_imageBorder.set(border); configureBorderImage(); } - void setQRCode(const std::string& code, int border); - void setQRCodeBorder(int border) { m_qrCodeBorder = border; setQRCode(m_qrCode, border); } std::string getImageSource() { return m_imageSource; } Rect getImageClip() { return m_imageClipRect; } @@ -527,7 +533,6 @@ class UIWidget : public LuaObject int getImageBorderLeft() { return m_imageBorder.left; } int getImageTextureWidth() { return m_imageTexture ? m_imageTexture->getWidth() : 0; } int getImageTextureHeight() { return m_imageTexture ? m_imageTexture->getHeight() : 0; } - int getQrCodeBorder() { return m_qrCodeBorder; } // text related private: @@ -581,4 +586,8 @@ class UIWidget : public LuaObject bool isTextWrap() { return hasProp(PropTextWrap); } std::string getFont() { return m_font->getName(); } Size getTextSize() { return m_textSize; } + + // custom style +protected: + virtual void parseCustomStyle(const OTMLNodePtr& styleNode) {}; }; diff --git a/src/framework/ui/uiwidgetimage.cpp b/src/framework/ui/uiwidgetimage.cpp index 57a6f388bf..79707314c3 100644 --- a/src/framework/ui/uiwidgetimage.cpp +++ b/src/framework/ui/uiwidgetimage.cpp @@ -35,10 +35,18 @@ void UIWidget::initImage() {} void UIWidget::parseImageStyle(const OTMLNodePtr& styleNode) { for (const auto& node : styleNode->children()) { - if (node->tag() == "image-source") - setImageSource(stdext::resolve_path(node->value(), node->source()), false); - else if (node->tag() == "image-source-base64") - setImageSource(stdext::resolve_path(node->value(), node->source()), true); + if (node->tag() == "image-source") { + auto split = stdext::split(node->value(), ":"); + if (split.size() == 0) split.push_back("none"); + bool base64 = split.size() > 1 && split[0] == "base64"; + auto& value = split.size() > 1 ? split[1] : split[0]; + + if (value == "" || value == "none") { + setImageSource("", base64); + } else { + setImageSource(stdext::resolve_path(value, node->source()), base64); + } + } else if (node->tag() == "image-offset-x") setImageOffsetX(node->value()); else if (node->tag() == "image-offset-y") @@ -77,10 +85,6 @@ void UIWidget::parseImageStyle(const OTMLNodePtr& styleNode) setImageAutoResize(node->value()); else if (node->tag() == "image-individual-animation") setImageIndividualAnimation(node->value()); - else if (node->tag() == "qr-code") - setQRCode(node->value(), getQrCodeBorder()); - else if (node->tag() == "qr-code-border") - setQRCodeBorder(node->value()); } } @@ -201,7 +205,7 @@ void UIWidget::setImageSource(const std::string_view source, bool base64) stream.write(decoded.c_str(), decoded.size()); m_imageTexture = g_textures.loadTexture(stream); } else { - m_imageTexture = g_textures.getTexture(m_imageSource = source, isImageSmooth()); + m_imageTexture = g_textures.getTexture(m_imageSource = source, isImageSmooth()); } if (!m_imageTexture) @@ -228,28 +232,3 @@ void UIWidget::setImageSource(const std::string_view source, bool base64) setSize(size); } } - -void UIWidget::setQRCode(const std::string& code, int border) -{ - if (code.empty()) { - m_imageTexture = nullptr; - m_qrCode = {}; - return; - } - - m_qrCode = code; - m_imageTexture = TexturePtr(new Texture(Image::fromQRCode(code, border))); - - if (m_imageTexture && (!m_rect.isValid() || isImageAutoResize())) { - const auto& imageSize = m_imageTexture->getSize(); - - Size size = getSize(); - if (size.width() <= 0 || hasProp(PropImageAutoResize)) - size.setWidth(imageSize.width()); - - if (size.height() <= 0 || hasProp(PropImageAutoResize)) - size.setHeight(imageSize.height()); - - setSize(size); - } -} diff --git a/src/framework/ui/uiwidgettext.cpp b/src/framework/ui/uiwidgettext.cpp index 3cb63eb762..740d69b204 100644 --- a/src/framework/ui/uiwidgettext.cpp +++ b/src/framework/ui/uiwidgettext.cpp @@ -23,14 +23,13 @@ #include #include #include -#include #include #include "uitranslator.h" #include "uiwidget.h" void UIWidget::initText() { - m_font = g_gameConfig.getWidgetTextFont(); + m_font = g_fonts.getDefaultWidgetFont(); m_textAlign = Fw::AlignCenter; m_coordsBuffer = std::make_shared(); } diff --git a/src/framework/util/crypt.cpp b/src/framework/util/crypt.cpp index 8aac1d4871..b8d3630ac8 100644 --- a/src/framework/util/crypt.cpp +++ b/src/framework/util/crypt.cpp @@ -23,11 +23,10 @@ #include "crypt.h" #include #include +#include "framework/core/application.h" #include #include -#include "framework/core/graphicalapplication.h" - #ifndef USE_GMP #include #include @@ -376,3 +375,111 @@ std::string Crypt::crc32(const std::string& decoded_string, bool upperCase) std::transform(result.begin(), result.end(), result.begin(), tolower); return result; } + +std::string Crypt::sha1Encrpyt(const std::string& input) +{ + uint32_t H[] = { + 0x67452301, + 0xEFCDAB89, + 0x98BADCFE, + 0x10325476, + 0xC3D2E1F0 + }; + + uint8_t messageBlock[64]; + size_t index = 0; + + uint32_t length_low = 0; + uint32_t length_high = 0; + for (char ch : input) { + messageBlock[index++] = ch; + + length_low += 8; + if (length_low == 0) { + length_high++; + } + + if (index == 64) { + sha1Block(messageBlock, H); + index = 0; + } + } + + messageBlock[index++] = 0x80; + + if (index > 56) { + while (index < 64) { + messageBlock[index++] = 0; + } + + sha1Block(messageBlock, H); + index = 0; + } + + while (index < 56) { + messageBlock[index++] = 0; + } + + messageBlock[56] = length_high >> 24; + messageBlock[57] = length_high >> 16; + messageBlock[58] = length_high >> 8; + messageBlock[59] = length_high; + + messageBlock[60] = length_low >> 24; + messageBlock[61] = length_low >> 16; + messageBlock[62] = length_low >> 8; + messageBlock[63] = length_low; + + sha1Block(messageBlock, H); + + char hexstring[41]; + static const char hexDigits[] = {"0123456789abcdef"}; + for (int hashByte = 20; --hashByte >= 0;) { + const uint8_t byte = H[hashByte >> 2] >> (((3 - hashByte) & 3) << 3); + index = hashByte << 1; + hexstring[index] = hexDigits[byte >> 4]; + hexstring[index + 1] = hexDigits[byte & 15]; + } + return std::string(hexstring, 40); +} + +void Crypt::sha1Block(uint8_t* block, uint32_t* H) +{ + uint32_t W[80]; + for (int i = 0; i < 16; ++i) { + const size_t offset = i << 2; + W[i] = block[offset] << 24 | block[offset + 1] << 16 | block[offset + 2] << 8 | block[offset + 3]; + } + + for (int i = 16; i < 80; ++i) { + W[i] = stdext::circularShift(1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]); + } + + uint32_t A = H[0], B = H[1], C = H[2], D = H[3], E = H[4]; + + for (int i = 0; i < 20; ++i) { + const uint32_t tmp = stdext::circularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[i] + 0x5A827999; + E = D; D = C; C = stdext::circularShift(30, B); B = A; A = tmp; + } + + for (int i = 20; i < 40; ++i) { + const uint32_t tmp = stdext::circularShift(5, A) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1; + E = D; D = C; C = stdext::circularShift(30, B); B = A; A = tmp; + } + + for (int i = 40; i < 60; ++i) { + const uint32_t tmp = stdext::circularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC; + E = D; D = C; C = stdext::circularShift(30, B); B = A; A = tmp; + } + + for (int i = 60; i < 80; ++i) { + const uint32_t tmp = stdext::circularShift(5, A) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6; + E = D; D = C; C = stdext::circularShift(30, B); B = A; A = tmp; + } + + H[0] += A; + H[1] += B; + H[2] += C; + H[3] += D; + H[4] += E; +} diff --git a/src/framework/util/crypt.h b/src/framework/util/crypt.h index 7370dfa0fd..fd02c335e5 100644 --- a/src/framework/util/crypt.h +++ b/src/framework/util/crypt.h @@ -22,6 +22,8 @@ #pragma once +#include "../global.h" + #include #include @@ -53,6 +55,10 @@ class Crypt int rsaGetSize(); std::string crc32(const std::string& decoded_string, bool upperCase); + std::string sha1Encrpyt(const std::string& input); + +protected: + void sha1Block(uint8_t* block, uint32_t* H); private: std::string _encrypt(const std::string& decrypted_string, bool useMachineUUID); diff --git a/src/main.cpp b/src/main.cpp index 296185d3d8..c51a601208 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,9 @@ */ #include +#include +#include +#include #include #include #include @@ -77,12 +80,27 @@ int main(int argc, const char* argv[]) #ifndef ANDROID #if ENABLE_DISCORD_RPC == 1 - g_discord.init(); + g_discord.init([]() -> bool { + return g_game.isOnline(); + }, [](std::string& info) { +#if SHOW_CHARACTER_NAME_RPC == 1 + info = "Name: " + g_game.getCharacterName(); +#endif +#if SHOW_CHARACTER_LEVEL_RPC == 1 + const auto& level = std::to_string(g_game.getLocalPlayer()->getLevel()); + info += info.empty() ? "Level: " + level : "[" + level + "]"; +#endif +#if SHOW_CHARACTER_WORLD_RPC == 1 + if (!info.empty()) info += "\n"; + info += "World: " + g_game.getWorldName(); +#endif + }); #endif #endif // initialize application framework and otclient - g_app.init(args, ASYNC_DISPATCHER_MAX_THREAD); + g_app.init(args, new GraphicalApplicationContext( + (uint8_t)ASYNC_DISPATCHER_MAX_THREAD, g_gameConfig.getSpriteSize(), ApplicationDrawEventsPtr(&g_client))); g_client.init(args); #ifdef FRAMEWORK_NET g_http.init(); @@ -98,13 +116,13 @@ int main(int argc, const char* argv[]) g_app.deinit(); // terminate everything and free memory - Client::terminate(); + g_client.terminate(); g_app.terminate(); #ifdef FRAMEWORK_NET g_http.terminate(); #endif return 0; - } +} #ifdef ANDROID } #endif diff --git a/vc17/otclient.vcxproj b/vc17/otclient.vcxproj index 149b23df86..cb39d594a0 100644 --- a/vc17/otclient.vcxproj +++ b/vc17/otclient.vcxproj @@ -213,11 +213,11 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + - @@ -240,7 +240,6 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ - @@ -296,6 +295,7 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + @@ -346,6 +346,7 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + @@ -390,7 +391,6 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ - @@ -442,6 +442,7 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + @@ -519,6 +520,7 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + diff --git a/vc17/otclient.vcxproj.filters b/vc17/otclient.vcxproj.filters index e44c647652..4ec4f45ae1 100644 --- a/vc17/otclient.vcxproj.filters +++ b/vc17/otclient.vcxproj.filters @@ -186,9 +186,6 @@ Source Files\framework\graphics - - Source Files\framework\graphics - Source Files\framework\graphics @@ -462,9 +459,6 @@ Source Files\client - - Source Files\client - Source Files\client @@ -555,6 +549,12 @@ Source Files\client + + Source Files\framework\graphics + + + Source Files\framework\ui + @@ -647,9 +647,6 @@ Header Files\framework\graphics - - Header Files\framework\graphics - Header Files\framework\graphics @@ -971,9 +968,6 @@ Header Files\client - - Header Files\client - Header Files\client @@ -1076,6 +1070,12 @@ Header Files\client + + Header Files\framework\graphics + + + Header Files\framework\ui + diff --git a/vc17/settings.props b/vc17/settings.props index 7a25c9244a..681b3a57a4 100644 --- a/vc17/settings.props +++ b/vc17/settings.props @@ -8,6 +8,7 @@ _WIN32_WINNT=0x0501; BOT_PROTECTION; CRASH_HANDLER; + FRAMEWORK_GRAPHICS; FRAMEWORK_NET; FRAMEWORK_SOUND; BUILD_TYPE="RelWithDebInfo";