diff --git a/CHANGELOG.md b/CHANGELOG.md index 33a7351..5b02c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to Light Engine will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.16-advanced.8] - 2026-10-04 + +### Fixed +- Destroyed menu-repeat controls are removed and safely recycled. +- Desktop overlay HTTP timeouts end the request without a second retry. +- Menu navigation consumes a quick direction tap once, including presses that + end before an update. Held keys retain the existing repeat delay. +- Portrait windows retain the full logical game viewport and letterbox it, + keeping menus and gameplay visible when Android rotates the device. + +Android version code: 124. + ## [0.1.16-advanced.7] - 2026-10-04 ### Fixed diff --git a/funkin/backend/throttle.lua b/funkin/backend/throttle.lua index 925b6e3..72ca665 100644 --- a/funkin/backend/throttle.lua +++ b/funkin/backend/throttle.lua @@ -37,8 +37,8 @@ function ThrottleObject:check() end function ThrottleObject:destroy() - table.destroy(Throttle.list, self) - table.insert(dead, setmetatable(self)) + if not table.delete(Throttle.list, self) then return end + table.insert(dead, setmetatable(self, nil)) end -- Throttle diff --git a/funkin/ui/menulist.lua b/funkin/ui/menulist.lua index 39b7564..e6f360a 100644 --- a/funkin/ui/menulist.lua +++ b/funkin/ui/menulist.lua @@ -1,6 +1,10 @@ local MenuList = SpriteGroup:extend("MenuList") MenuList.selectionCache = {} +local function navigationInput(name) + return controls:pressed(name) or controls:down(name) +end + local defaultScrolls = { default = function(self, obj, dt, time) obj.y = self:lerp(dt, obj, "y", time) @@ -83,11 +87,11 @@ function MenuList:new(sound, cache, scroll, hover) self.throttles = {} if self.scroll ~= "horizontal" and not self.isMobile then - self.throttles[-1] = Throttle:make({controls.down, controls, "ui_up"}) - self.throttles[1] = Throttle:make({controls.down, controls, "ui_down"}) + self.throttles[-1] = Throttle:make({navigationInput, "ui_up"}) + self.throttles[1] = Throttle:make({navigationInput, "ui_down"}) elseif self.scroll == "horizontal" and not self.isMobile then - self.throttles[-1] = Throttle:make({controls.down, controls, "ui_left"}) - self.throttles[1] = Throttle:make({controls.down, controls, "ui_right"}) + self.throttles[-1] = Throttle:make({navigationInput, "ui_left"}) + self.throttles[1] = Throttle:make({navigationInput, "ui_right"}) end end @@ -322,4 +326,4 @@ function MenuList:_getBoundary() self.origin.x, self.origin.y end -return MenuList \ No newline at end of file +return MenuList diff --git a/loxel/init.lua b/loxel/init.lua index a66a68f..f93070e 100644 --- a/loxel/init.lua +++ b/loxel/init.lua @@ -277,7 +277,7 @@ function game.init(app, state, ...) local width, height = app.width, app.height if app.adaptableWidth then local sw, sh = love.graphics.getDimensions() - if sw > 0 and sh > 0 then width = math.max(1, math.floor(height * sw / sh)) end + if sw > 0 and sh > 0 and sw >= sh then width = math.max(1, math.floor(height * sw / sh)) end end game.width, game.height = width, height Display.refresh() @@ -405,7 +405,7 @@ function game.resize(w, h) if Project.adaptableWidth then local oldWidth, oldHeight = game.width, game.height if oldHeight > 0 then - game.width = math.max(1, math.floor(oldHeight * w / h)) + game.width = w < h and Project.width or math.max(1, math.floor(oldHeight * w / h)) for _, camera in ipairs(game.cameras.list) do if camera.x == 0 and camera.y == 0 and camera.width == oldWidth and camera.height == oldHeight then camera:resize(game.width, oldHeight, camera.resolutionX, camera.resolutionY) diff --git a/project.lua b/project.lua index 9f36d26..68af52d 100644 --- a/project.lua +++ b/project.lua @@ -6,8 +6,8 @@ return { title = "Light Engine", file = "Light Engine", icon = "art/logo.png", - version = "0.1.16-advanced.7", - androidVersionCode = 123, + version = "0.1.16-advanced.8", + androidVersionCode = 124, package = "com.zyn.lightengine", width = 1280, height = 720, diff --git a/separate-addons/screen-overlay/modules/overlay.lua b/separate-addons/screen-overlay/modules/overlay.lua index f5b39ad..bc8c3dd 100644 --- a/separate-addons/screen-overlay/modules/overlay.lua +++ b/separate-addons/screen-overlay/modules/overlay.lua @@ -128,7 +128,7 @@ while true do if success then code,body,reason=a,b,c else code,body,reason=nil,'',a end end local pending=((connecting and code and code>=200 and code<300)or code==403)and body:find('"pending"%s*:%s*true') - local startup=code==nil and love.timer.getTime()-started<3 + local startup=code==nil and reason~='timeout' and love.timer.getTime()-started<3 if not pending and not startup then break end if task.close or cancel:peek() or tasks:peek() or love.timer.getTime()-started>=30 then break end local untilTime=love.timer.getTime()+.5 diff --git a/tests/engine/compat-controls.lua b/tests/engine/compat-controls.lua index 38fb4d4..e7d6754 100644 --- a/tests/engine/compat-controls.lua +++ b/tests/engine/compat-controls.lua @@ -92,8 +92,7 @@ function love.load() assert(state.camHUD.__shakeDuration==0 and state.camHUD.__fadeDuration==0,'owned camera effects survived disposal') assert(not pcall(cleanupFont.getHeight,cleanupFont) and pcall(cachedFont.getHeight,cachedFont),'font ownership cleanup incorrect') local destroyedCamera=Camera();state.camOther=destroyedCamera - local weakContext - do + local function disposedContext() local abandonedScript=Script('tests/engine/compat-fixture.lua',false,true,true) local abandoned=Script.createCompatibility(state,abandonedScript) abandonedScript.variables.requestAsset('image','test-controls','abandoned') @@ -101,8 +100,11 @@ function love.load() destroyedCamera:destroy() abandoned:dispose();abandonedScript:close() assert(destroyedCamera.__flashComplete==nil,'destroyed camera retained owned completion closure') - weakContext=setmetatable({abandoned},{__mode='v'}) + return setmetatable({abandoned},{__mode='v'}) end + -- End the allocating stack frame before checking reachability; LuaJIT may + -- retain registers from a lexical block until its enclosing function returns. + local weakContext=disposedContext() collectgarbage('collect') assert(weakContext[1]==nil,'pending callback retained a disposed context/state') while paths.async.getProgress()<1 do assert(love.timer.getTime()