From 0975fcd8193025c171acf0644babfb570e53c468 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 May 2026 15:36:49 +0300 Subject: [PATCH 1/5] fix tick intervals in headless mode --- src/logic/BlocksController.cpp | 12 ++++++---- src/objects/Entities.cpp | 42 +++++++++++++++++++--------------- src/util/Clock.cpp | 36 +++++++++++++++++------------ src/util/Clock.hpp | 7 +++--- 4 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/logic/BlocksController.cpp b/src/logic/BlocksController.cpp index 3c0d0412e..58594f6ee 100644 --- a/src/logic/BlocksController.cpp +++ b/src/logic/BlocksController.cpp @@ -130,11 +130,15 @@ void BlocksController::updateBlock(int x, int y, int z) { } void BlocksController::update(float delta, uint padding) { - if (randTickClock.update(delta)) { - randomTick(randTickClock.getPart(), randTickClock.getParts(), padding); + if (int parts = randTickClock.update(delta)) { + for (int i = 0; i < parts; i++) { + randomTick(randTickClock.convertPart(i), randTickClock.getParts(), padding); + } } - if (blocksTickClock.update(delta)) { - onBlocksTick(blocksTickClock.getTickId(), blocksTickClock.getParts()); + if (int parts = blocksTickClock.update(delta)) { + for (int i = 0; i < parts; i++) { + onBlocksTick(blocksTickClock.convertPart(i), blocksTickClock.getParts()); + } } if (worldTickClock.update(delta)) { scripting::on_world_tick(worldTickClock.getTickRate()); diff --git a/src/objects/Entities.cpp b/src/objects/Entities.cpp index 7d2db5b84..61b312a24 100644 --- a/src/objects/Entities.cpp +++ b/src/objects/Entities.cpp @@ -288,22 +288,24 @@ void Entities::preparePhysics(float delta) { auto& hitboxes = physics.getHitboxesWriteable(); auto& solidHitboxes = physics.getSolidHitboxesWriteable(); - if (sensorsTickClock.update(delta)) { - auto part = sensorsTickClock.getPart(); - auto parts = sensorsTickClock.getParts(); + if (int parts = sensorsTickClock.update(delta)) { + for (int i = 0; i < parts; i++) { + auto part = sensorsTickClock.convertPart(i); + auto allParts = sensorsTickClock.getParts(); - auto& sensors = physics.getSensorsWriteable(); - sensors.clear(); + auto& sensors = physics.getSensorsWriteable(); + sensors.clear(); - auto view = registry->view(); - for (auto [entity, eid, transform, rigidbody] : view.each()) { - if (!rigidbody.enabled) { - continue; + auto view = registry->view(); + for (auto [entity, eid, transform, rigidbody] : view.each()) { + if (!rigidbody.enabled) { + continue; + } + if ((eid.uid + part) % allParts != 0) { + continue; + } + updateSensors(rigidbody, transform, sensors); } - if ((eid.uid + part) % parts != 0) { - continue; - } - updateSensors(rigidbody, transform, sensors); } } @@ -364,12 +366,14 @@ void Entities::updatePhysics(float delta) { } void Entities::update(float delta) { - if (updateTickClock.update(delta)) { - scripting::on_entities_update( - updateTickClock.getTickRate(), - updateTickClock.getParts(), - updateTickClock.getPart() - ); + if (int parts = updateTickClock.update(delta)) { + for (int i = 0; i < parts; i++) { + scripting::on_entities_update( + updateTickClock.getTickRate(), + updateTickClock.getParts(), + updateTickClock.convertPart(i) + ); + } } updatePhysics(delta); scripting::on_entities_physics_update(delta); diff --git a/src/util/Clock.cpp b/src/util/Clock.cpp index 0d4f74021..cc210b997 100644 --- a/src/util/Clock.cpp +++ b/src/util/Clock.cpp @@ -8,30 +8,32 @@ Clock::Clock(int tickRate, int tickParts) : tickRate(tickRate), tickParts(tickParts) { } -bool Clock::update(float delta) { +int Clock::update(float delta) { tickTimer += delta; float delay = 1.0f / static_cast(tickRate); - if (tickTimer > delay || tickPartsUndone) { - if (tickPartsUndone) { - tickPartsUndone--; - } else { - tickTimer = std::fmod(tickTimer, delay); - tickPartsUndone = tickParts - 1; - } - ++tickId; - return true; + if (tickTimer < delay / tickParts) { + return 0; } - return false; + int parts = tickTimer / (delay / tickParts); + if (parts) { + tickTimer -= parts * delay / tickParts; + tickTimer = std::min(tickTimer, delay); + } + currentTickPart += parts; + if (currentTickPart >= tickParts) { + currentTickPart %= tickParts; + } + return parts; +} + +int Clock::getPart() const { + return currentTickPart; } int Clock::getParts() const { return tickParts; } -int Clock::getPart() const { - return tickParts - tickPartsUndone - 1; -} - int Clock::getTickRate() const { return tickRate; } @@ -39,3 +41,7 @@ int Clock::getTickRate() const { int Clock::getTickId() const { return tickId; } + +int Clock::convertPart(int index) const { + return (tickParts - currentTickPart) % tickParts + index; +} diff --git a/src/util/Clock.hpp b/src/util/Clock.hpp index 37c5f4ebf..a2c529d0f 100644 --- a/src/util/Clock.hpp +++ b/src/util/Clock.hpp @@ -7,15 +7,16 @@ namespace util { float tickTimer = 0.0f; int tickId = 0; - int tickPartsUndone = 0; + int currentTickPart = 0; public: Clock(int tickRate, int tickParts); - bool update(float delta); + int update(float delta); - int getParts() const; int getPart() const; + int getParts() const; int getTickRate() const; int getTickId() const; + int convertPart(int index) const; }; } From 957ef0aed39e78980544c80e1d35a5ca5eeaf616 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 2 May 2026 16:23:04 +0300 Subject: [PATCH 2/5] add missing include --- src/util/Clock.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/Clock.cpp b/src/util/Clock.cpp index cc210b997..c682a207d 100644 --- a/src/util/Clock.cpp +++ b/src/util/Clock.cpp @@ -1,6 +1,7 @@ #include "Clock.hpp" #include +#include using namespace util; From 47f2bf75d79f73a28afd618f52509767c0876881 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 May 2026 17:51:52 +0300 Subject: [PATCH 3/5] fix player ticks --- src/logic/LevelController.cpp | 11 +++++++---- src/util/Clock.cpp | 4 ---- src/util/Clock.hpp | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/logic/LevelController.cpp b/src/logic/LevelController.cpp index 4849df308..870fed923 100644 --- a/src/logic/LevelController.cpp +++ b/src/logic/LevelController.cpp @@ -104,10 +104,12 @@ void LevelController::update(float delta, bool pause) { if (player->isSuspended()) { continue; } - if (playerTickClock.update(delta)) { - if (player->getId() % playerTickClock.getParts() == - playerTickClock.getPart()) { - + if (int parts = playerTickClock.update(delta)) { + for (int i = 0; i < parts; i++) { + if (player->getId() % playerTickClock.getParts() != + playerTickClock.convertPart(i)) { + continue; + } const auto& position = player->getPosition(); if (player->chunks->get( std::floor(position.x), @@ -119,6 +121,7 @@ void LevelController::update(float delta, bool pause) { ); } } + } } } diff --git a/src/util/Clock.cpp b/src/util/Clock.cpp index c682a207d..dbe7b870f 100644 --- a/src/util/Clock.cpp +++ b/src/util/Clock.cpp @@ -27,10 +27,6 @@ int Clock::update(float delta) { return parts; } -int Clock::getPart() const { - return currentTickPart; -} - int Clock::getParts() const { return tickParts; } diff --git a/src/util/Clock.hpp b/src/util/Clock.hpp index a2c529d0f..a8ae83fd2 100644 --- a/src/util/Clock.hpp +++ b/src/util/Clock.hpp @@ -13,7 +13,6 @@ namespace util { int update(float delta); - int getPart() const; int getParts() const; int getTickRate() const; int getTickId() const; From 1d4abd7758db633fd389990ddd015f37c5803ed3 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 May 2026 18:12:59 +0300 Subject: [PATCH 4/5] fix selection reset on end reached with shift pressed --- src/graphics/ui/elements/TextBox.cpp | 42 ++++++++-------------------- src/graphics/ui/elements/TextBox.hpp | 3 +- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 1daab49c5..fbf851f84 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -755,15 +755,16 @@ void TextBox::resetMaxLocalCaret() { caret - label->getTextLineOffset(label->getLineByTextIndex(caret)); } -void TextBox::stepLeft(bool shiftPressed, bool breakSelection) { +void TextBox::stepCaret(bool shiftPressed, bool breakSelection, bool right) { uint previousCaret = this->caret; - size_t caret = breakSelection ? selectionStart : this->caret; - if (caret > 0) { - if (caret > input.length()) { - setCaret(input.length() - 1); - } else { - setCaret(caret - 1); - } + size_t caret = + breakSelection ? (right ? selectionEnd : selectionStart) : this->caret; + if (right ? (caret < input.length()) : (caret > 0)) { + setCaret( + right ? (caret + 1) + : ((caret > input.length()) ? (input.length() - 1) + : (caret - 1)) + ); if (shiftPressed) { if (selectionStart == selectionEnd) { selectionOrigin = previousCaret; @@ -774,28 +775,9 @@ void TextBox::stepLeft(bool shiftPressed, bool breakSelection) { } } else { setCaret(caret); - resetSelection(); - } - resetMaxLocalCaret(); -} - -void TextBox::stepRight(bool shiftPressed, bool breakSelection) { - uint previousCaret = this->caret; - size_t caret = breakSelection ? selectionEnd : this->caret; - if (caret < input.length()) { - setCaret(caret + 1); - caretLastMove = gui.getWindow().time(); - if (shiftPressed) { - if (selectionStart == selectionEnd) { - selectionOrigin = previousCaret; - } - extendSelection(this->caret); - } else { + if (!shiftPressed) { resetSelection(); } - } else { - setCaret(caret); - resetSelection(); } resetMaxLocalCaret(); } @@ -983,9 +965,9 @@ void TextBox::performEditingKeyboardEvents(Keycode key) { } else if (key == Keycode::TAB) { onTab(shiftPressed); } else if (key == Keycode::LEFT) { - stepLeft(shiftPressed, breakSelection); + stepCaret(shiftPressed, breakSelection, false); } else if (key == Keycode::RIGHT) { - stepRight(shiftPressed, breakSelection); + stepCaret(shiftPressed, breakSelection, true); } else if (key == Keycode::UP && onUpPressed) { onUpPressed(); } else if (key == Keycode::DOWN && onDownPressed) { diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index cd70f80e7..ff6eef0e1 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -66,8 +66,7 @@ namespace gui { std::string markup; std::string syntax; - void stepLeft(bool shiftPressed, bool breakSelection); - void stepRight(bool shiftPressed, bool breakSelection); + void stepCaret(bool shiftPressed, bool breakSelection, bool right); void stepDefaultDown(bool shiftPressed, bool breakSelection); void stepDefaultUp(bool shiftPressed, bool breakSelection); From bebb5049efaa8b242b3eba25eeb8227dfb06a161 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 3 May 2026 21:14:56 +0300 Subject: [PATCH 5/5] fix textbox scrolling --- src/graphics/ui/elements/Container.cpp | 24 ++++++++++++++++-------- src/graphics/ui/elements/Container.hpp | 6 ++++-- src/graphics/ui/elements/TextBox.cpp | 13 ++++++++----- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/graphics/ui/elements/Container.cpp b/src/graphics/ui/elements/Container.cpp index aa9b5977f..c869e9b3b 100644 --- a/src/graphics/ui/elements/Container.cpp +++ b/src/graphics/ui/elements/Container.cpp @@ -43,20 +43,28 @@ std::shared_ptr Container::getAt(const glm::vec2& pos) { return UINode::getAt(pos); } +void Container::click(int x, int y) { + UINode::click(x, y); + + if (scrollable) { + auto pos = calcPos(); + x -= pos.x; + y -= pos.y; + if (x >= size.x - scrollBarWidth) { + scrollbarTriggered = true; + prevScrollY = y; + } + } +} + void Container::mouseMove(int x, int y) { UINode::mouseMove(x, y); - if (!scrollable) { + if (!scrollable || !scrollbarTriggered) { return; } auto pos = calcPos(); x -= pos.x; y -= pos.y; - if (prevScrollY == -1) { - if (x >= size.x - scrollBarWidth) { - prevScrollY = y; - } - return; - } int diff = (actualLength-size.y); if (diff > 0) { scroll -= (y - prevScrollY) / static_cast(size.y) * actualLength; @@ -69,7 +77,7 @@ void Container::mouseMove(int x, int y) { void Container::mouseRelease(int x, int y) { UINode::mouseRelease(x, y); - prevScrollY = -1; + scrollbarTriggered = false; } void Container::act(float delta) { diff --git a/src/graphics/ui/elements/Container.hpp b/src/graphics/ui/elements/Container.hpp index 0506bf38d..3ede2c2a1 100644 --- a/src/graphics/ui/elements/Container.hpp +++ b/src/graphics/ui/elements/Container.hpp @@ -9,6 +9,7 @@ namespace gui { class Container : public UINode, public ::util::ObjectsKeeper { int prevScrollY = -1; + bool scrollbarTriggered = false; protected: std::vector> nodes; std::vector intervalEvents; @@ -18,13 +19,14 @@ namespace gui { int actualLength = 0; bool scrollable = true; - bool isScrolling() { - return prevScrollY != -1; + bool isScrolling() const { + return scrollbarTriggered; } public: Container(GUI& gui, glm::vec2 size); virtual ~Container(); + virtual void click(int, int) override; virtual void act(float delta) override; virtual void drawBackground(const DrawContext& pctx, const Assets& assets); virtual void draw(const DrawContext& pctx, const Assets& assets) override; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index fbf851f84..d04ca5786 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -648,7 +648,7 @@ void TextBox::refresh() { label->setSize( size - glm::vec2( padding.z + padding.x + - LINE_NUMBERS_PANE_WIDTH * showLineNumbers + textOffset, + LINE_NUMBERS_PANE_WIDTH * showLineNumbers - textOffset, padding.w + padding.y ) ); @@ -671,7 +671,9 @@ size_t TextBox::normalizeIndex(int index) { /// @param y screen Y position /// @return non-normalized character index int TextBox::calcIndexAt(int x, int y) const { - if (!rawTextCache.metrics.font.has_value()) return 0; + if (!rawTextCache.metrics.font.has_value()) { + return 0; + } const auto& labelText = label->getText(); glm::vec2 lcoord = label->calcPos(); uint line = label->getLineByYOffset(y - lcoord.y); @@ -733,6 +735,7 @@ void TextBox::doubleClick(int x, int y) { } void TextBox::click(int x, int y) { + Container::click(x, y); int index = normalizeIndex(calcIndexAt(x, y)); selectionStart = index; selectionEnd = index; @@ -1200,7 +1203,7 @@ void TextBox::setCaret(size_t position) { if (font == nullptr) { return; } - int width = label->getSize().x; + int width = size.x - padding.x - padding.z - LINE_NUMBERS_PANE_WIDTH * showLineNumbers; rawTextCache.prepare(font, rawTextCache.metrics, width); rawTextCache.update(input, multiline, label->isTextWrapping()); @@ -1221,12 +1224,12 @@ void TextBox::setCaret(size_t position) { } int lcaret = caret - rawTextCache.getTextLineOffset(line); int realoffset = rawTextCache.metrics.calcWidth(labelText, 0, lcaret) - - static_cast(textOffset) + 2; + static_cast(textOffset); if (realoffset - width > 0) { setTextOffset(textOffset + realoffset - width); } else if (realoffset < 0) { - setTextOffset(std::max(textOffset + realoffset, static_cast(0))); + setTextOffset(std::max(textOffset + realoffset - static_cast(padding.x + padding.w), 0)); } }