Skip to content

Commit

Permalink
Add a delay to the pause feature (#6643)
Browse files Browse the repository at this point in the history
Co-authored-by: lL1l1 <[email protected]>
  • Loading branch information
Garanas and lL1l1 authored Feb 7, 2025
1 parent 71b1568 commit a34ffb7
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 11 deletions.
3 changes: 3 additions & 0 deletions changelog/snippets/features.6643.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- (#6643) Introduce a small enforced delay before a paused session can resume

The delay applies to all players but the player that initiated the pause. The delay is 10 seconds. The delay is not configurable.
9 changes: 5 additions & 4 deletions engine/User.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1096,10 +1096,11 @@ end
--- Resume the world simulation
function SessionResume()
end

---
---@param client? number | number[] client or clients
---@param message table | number | string

--- Sends a message to one, more or all other connected clients. This message is sent separately from the simulation. But it is sent in order.
---@overload fun(message: table | number | string)
---@param client? number | number[] # client or clients
---@param message table | number | string #
function SessionSendChatMessage(client, message)
end

Expand Down
17 changes: 14 additions & 3 deletions lua/ui/game/gamemain.lua
Original file line number Diff line number Diff line change
Expand Up @@ -747,9 +747,12 @@ function OnQueueChanged(newQueue)
end
end

-- Called after the Sim has confirmed the game is indeed paused. This will happen
-- on everyone's machine in a network game.
--- Called by the engine after the sim confirmed that the game is indeed paused. This is run on all instances that are connected to the lobby.
---@param pausedBy integer # The index of the client in the clients list (that you get via `GetSessionClients`)
---@param timeoutsRemaining number
function OnPause(pausedBy, timeoutsRemaining)
import("/lua/ui/game/pause.lua").OnPause(pausedBy, timeoutsRemaining)

PauseSound("World",true)
PauseSound("Music",true)
PauseVoice("VO",true)
Expand All @@ -759,11 +762,17 @@ end

-- Called after the Sim has confirmed that the game has resumed.
local ResumedBy = nil

--- Transmitted via a Chat command by another user to inform Lua who sent the resume command.
---@param sender string # The name of the player that resumed the game.
function SendResumedBy(sender)
if not ResumedBy then ResumedBy = sender end
end

--- Called by the engine when the simulation is resumed
function OnResume()
import("/lua/ui/game/pause.lua").OnResume()

PauseSound("World",false)
PauseSound("Music",false)
PauseVoice("VO",false)
Expand All @@ -775,6 +784,8 @@ end
-- Called immediately when the user hits the pause button on the machine
-- that initiated the pause and other network players won't call this function
function OnUserPause(pause)
import("/lua/ui/game/pause.lua").OnUserPause(pause)

local Tabs = import("/lua/ui/game/tabs.lua")
local focus = GetArmiesTable().focusArmy
if Tabs.CanUserPause() then
Expand All @@ -792,7 +803,7 @@ function OnUserPause(pause)
else
SessionSendChatMessage(import('/lua/ui/game/clientutils.lua').GetAll(), {
to = 'all',
text = 'Unpaused the game',
text = 'Resumed the game',
Chat = true,
})
end
Expand Down
100 changes: 100 additions & 0 deletions lua/ui/game/pause.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
--******************************************************************************************************
--** Copyright (c) 2025 Willem 'Jip' Wijnia
--**
--** 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.
--******************************************************************************************************

---@type integer
local OnPauseClientIndex = -1

---@type number
local OnPauseTimestamp = 0

---@type number
local ResumeThreshold = 10 -- seconds

---@return integer # The index of the client, like the parameter `pausedBy` of OnPause
---@return Client? # The data of the client
local function FindLocalClient()
local allClients = GetSessionClients()
for k = 1, table.getn(allClients) do
local client = allClients[k]
if client["local"] then
return k, client
end
end

return -1, nil
end

--- Called from `gamemain.lua` when the simulation pauses for all clients.
---@param pausedBy integer # The index of the client in the clients list (that you get via `GetSessionClients`)
---@param timeoutsRemaining number
function OnPause(pausedBy, timeoutsRemaining)
-- keep track of who paused and when
OnPauseClientIndex = pausedBy
OnPauseTimestamp = GetSystemTimeSeconds()
end

--- Called by the engine when the simulation resumed for all clients.
function OnResume()
OnPauseClientIndex = -1
OnPauseTimestamp = 0
end

-- Called immediately by the engine on the machine that initiated the pause. This function is called only by the client that is initiating the (un)pause.
function OnUserPause(pause)
end

local oldSessionRequestPause = _G.SessionRequestPause
_G.SessionRequestPause = function()
-- makes no sense to request a pause on top of a pause
if SessionIsPaused() then
return
end

oldSessionRequestPause()
end

local oldSessionResume = _G.SessionResume
---@return 'Accepted' | 'Declined'
_G.SessionResume = function()
local localClientIndex, clientData = FindLocalClient()
local timeDifference = GetSystemTimeSeconds() - OnPauseTimestamp

-- conditions that allow an immediate resume of the session
if SessionIsReplay() or
not SessionIsMultiplayer() or
OnPauseClientIndex == localClientIndex or -- feature: the person who initiated the pause can resume at any time
timeDifference > ResumeThreshold -- feature: any person can resume after the pause lasted past the threshold
then
SessionSendChatMessage({ SendResumedBy = true })
oldSessionResume()
return 'Accepted'
else
-- inform other clients
SessionSendChatMessage(import('/lua/ui/game/clientutils.lua').GetAll(), {
to = 'all',
text = string.format('Wants to resume the game but has to wait %d seconds', ResumeThreshold - timeDifference),
Chat = true,
})

return 'Declined'
end
end
13 changes: 9 additions & 4 deletions lua/ui/game/tabs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,21 @@ function CommonLogic()
end
end
tab.OnCheck = function(self, checked)
if checked then
if not SessionIsPaused() then
if not CanUserPause() then
return
end

SessionRequestPause()
self:SetGlowState(checked)
else
SessionSendChatMessage({SendResumedBy=true})
SessionResume()
self:SetGlowState(checked)
local status = SessionResume()
if status == 'Accepted' then
self:SetGlowState(checked)
else
-- reset the check since the resume was declined
self:SetCheck(not checked, true)
end
end
end
tab.OnClick = function(self, modifiers)
Expand Down

0 comments on commit a34ffb7

Please sign in to comment.