Intial Commit

This commit is contained in:
2026-02-16 11:54:35 -05:00
parent a738551e6b
commit 2bb6fe4f86
38 changed files with 585 additions and 2 deletions

View File

@@ -1,3 +1,29 @@
# UpdatePLZ <p align="center">
<img src="https://i.imgur.com/3yztk0D.png"/>
</p>
A B42 Update of UpdatePLZ https://github.com/WilliamVenner/pz-updateplz/tree/master Don't you hate it when you try and join your server and it disconnects telling you there are outdated Workshop mods? Hate no more thanks to Update PLZ!
This mod will query the server's Workshop mods every minute and check if they were updated after the server started up. If it detects outdated Workshop items, the server will be saved & restarted the next time it is empty.
# For dedicated servers **ONLY**
This mod ONLY works on DEDICATED SERVERS and does nothing in co-op/singleplayer!
# How "restarting" works
This mod will SHUT DOWN the server - currently there is no way to RESTART the server from a mod, so please make sure you are using a daemon that will automatically restart the server when it shuts itself down.
If you don't currently have this setup for your server, you can find a simple script (bash script for Linux, Batch script for Windows) online for automatically restarting a program if it exits.
# Choosing the restart time
| **Mod ID** | **Restarts after** |
|-------------|-------------------------------|
| UpdatePlz | When the server becomes empty |
| UpdatePlz1 | 1 minute |
| UpdatePlz5 | 5 minutes |
| UpdatePlz10 | 10 minutes |
| UpdatePlz15 | 15 minutes |
| UpdatePlz30 | 30 minutes |
| UpdatePlz60 | 1 hour |

View File

@@ -0,0 +1,132 @@
local getTimestamp = getTimestamp
local querySteamWorkshopItemDetails = querySteamWorkshopItemDetails
local getSteamWorkshopItemIDs = getSteamWorkshopItemIDs
local getOnlinePlayers = getOnlinePlayers
local getCore = getCore
if not getCore():isDedicated() then
print("[UpdatePLZ] Refusing to load (this is not a dedicated server)")
return
end
UpdatePLZ = UpdatePLZ or {}
local serverStarted = getTimestamp()
local pendingReboot = false
function UpdatePLZ.setRestartDelaySeconds(delay)
print("[UpdatePLZ] Restart delay set to " .. delay .. " seconds")
UpdatePLZ.restartDelay = delay
end
local function isServerEmpty()
return getOnlinePlayers():size() == 0
end
local rebootServer do
local rebootingNow = false
function rebootServer()
if rebootingNow then return end
rebootingNow = true
print("[UpdatePLZ] Saving...")
saveGame()
print("[UpdatePLZ] Quitting...")
getCore():quit()
end
end
local restartingAt
local scheduleServerRestart do
ModData.remove("UpdatePLZ")
Events.OnInitGlobalModData.Add(function()
if restartingAt then
ModData.add("UpdatePLZ", { restartingAt = restartingAt })
ModData.transmit("UpdatePLZ")
else
ModData.remove("UpdatePLZ")
end
end)
function scheduleServerRestart(timestamp)
restartingAt = timestamp
ModData.add("UpdatePLZ", { restartingAt = timestamp })
ModData.transmit("UpdatePLZ")
UpdatePLZ.startRestartCountdown(timestamp)
end
end
local function rebootWhenEmpty()
if pendingReboot and isServerEmpty() then
rebootServer()
Events.OnTickEvenPaused.Remove(rebootWhenEmpty)
end
end
local function workshopOutdated()
if pendingReboot then return end
pendingReboot = true
if isServerEmpty() then
print("[UpdatePLZ] Restarting the server (server empty and outdated Workshop items were detected)")
rebootServer()
return
end
if UpdatePLZ.restartDelay then
print("[UpdatePLZ] Detected outdated Workshop item - restarting server in " .. UpdatePLZ.minutes(UpdatePLZ.restartDelay))
scheduleServerRestart(getTimestamp() + UpdatePLZ.restartDelay)
else
print("[UpdatePLZ] Restarting the server when it becomes empty... (outdated Workshop items were detected)")
Events.OnTickEvenPaused.Add(rebootWhenEmpty)
end
end
local pollWorkshop do
local fakeTable = {}
function pollWorkshop()
if pendingReboot then return end
print("[UpdatePLZ] Checking for outdated Workshop items...")
querySteamWorkshopItemDetails(getSteamWorkshopItemIDs(), function(_, status, info)
if status ~= "Completed" then return end
for i = 0, info:size() - 1 do
local details = info:get(i)
local updated = details:getTimeUpdated()
if updated >= serverStarted then
workshopOutdated()
return
end
end
end, fakeTable)
end
end
Events.OnDisconnect.Add(pollWorkshop)
local nextPoll
Events.OnTickEvenPaused.Add(function()
if restartingAt then
if isServerEmpty() then
print("[UpdatePLZ] Restarting the server now! (Outdated Workshop items were detected and server is empty)")
rebootServer()
elseif restartingAt - getTimestamp() <= 0 then
print("[UpdatePLZ] Restarting the server now! (Outdated Workshop items were detected)")
rebootServer()
end
return
end
if pendingReboot then return end
-- Don't bother checking for outdated Workshop items if there's no restart delay and the server has players on it
if not UpdatePLZ.restartDelay and not isServerEmpty() then return end
local timestamp = getTimestamp()
if not nextPoll or getTimestamp() >= nextPoll then
nextPoll = timestamp + 60
return pollWorkshop()
end
end)

View File

@@ -0,0 +1,88 @@
local getTimestamp = getTimestamp
local isServer = getCore():isDedicated()
UpdatePLZ = UpdatePLZ or {}
local chat do
if isServer then
chat = print
else
require "client/Chat/ISChat.lua"
local chatMsg = {
getTextWithPrefix = function(self)
return self.msg
end,
getText = function(self)
return self.msg
end,
isServerAlert = function() return true end,
isShowAuthor = function() return false end,
getAuthor = function() return "SERVER" end
}
chatMsg.__index = chatMsg
function chat(...)
local msg = table.concat({...}, "\t")
ISChat.addLineInChat(setmetatable({ msg = msg }, chatMsg), 0)
end
end
end
function UpdatePLZ.minutes(secs)
if secs < 60 then
if secs == 1 then
return secs .. " second"
else
return secs .. " seconds"
end
else
if secs / 60 == 1 then
return "1 minute"
else
return (string.gsub(string.gsub(string.format("%.2f", secs / 60), "(%.%d-)0*$", "%1"), "%.$", "")) .. " minutes"
end
end
end
local restartingAt
local nextChatPrint
local function countdown()
local time = getTimestamp()
local delta = restartingAt - time
if not nextChatPrint or nextChatPrint - time <= 0 then
nextChatPrint = time + math.min(delta / 2, 60 * 15)
if math.floor(delta / 60) <= 0 then
chat("WARNING: Server is restarting to update Workshop mods!")
Events.OnTickEvenPaused.Remove(countdown)
else
chat("WARNING: Server is restarting in " .. UpdatePLZ.minutes(delta) .. " to update Workshop mods")
end
end
end
function UpdatePLZ.startRestartCountdown(__restartingAt)
nextChatPrint = nil
restartingAt = __restartingAt
if restartingAt then
Events.OnTickEvenPaused.Add(countdown)
else
Events.OnTickEvenPaused.Remove(countdown)
end
end
if not isServer then
Events.OnInitGlobalModData.Add(function()
Events.OnReceiveGlobalModData.Add(function(key, modData)
if key == "UpdatePLZ" then
UpdatePLZ.startRestartCountdown(modData and modData.restartingAt or nil)
end
end)
ModData.request("UpdatePLZ")
end)
end

View File

@@ -0,0 +1,3 @@
name=Update PLZ
id=UpdatePlz
description=Restart the (empty) dedicated server if mods are outdated

View File

@@ -0,0 +1,3 @@
name=Update PLZ
id=UpdatePlz
description=Restart the (empty) dedicated server if mods are outdated

View File

@@ -0,0 +1,132 @@
local getTimestamp = getTimestamp
local querySteamWorkshopItemDetails = querySteamWorkshopItemDetails
local getSteamWorkshopItemIDs = getSteamWorkshopItemIDs
local getOnlinePlayers = getOnlinePlayers
local getCore = getCore
if not getCore():isDedicated() then
print("[UpdatePLZ] Refusing to load (this is not a dedicated server)")
return
end
UpdatePLZ = UpdatePLZ or {}
local serverStarted = getTimestamp()
local pendingReboot = false
function UpdatePLZ.setRestartDelaySeconds(delay)
print("[UpdatePLZ] Restart delay set to " .. delay .. " seconds")
UpdatePLZ.restartDelay = delay
end
local function isServerEmpty()
return getOnlinePlayers():size() == 0
end
local rebootServer do
local rebootingNow = false
function rebootServer()
if rebootingNow then return end
rebootingNow = true
print("[UpdatePLZ] Saving...")
saveGame()
print("[UpdatePLZ] Quitting...")
getCore():quit()
end
end
local restartingAt
local scheduleServerRestart do
ModData.remove("UpdatePLZ")
Events.OnInitGlobalModData.Add(function()
if restartingAt then
ModData.add("UpdatePLZ", { restartingAt = restartingAt })
ModData.transmit("UpdatePLZ")
else
ModData.remove("UpdatePLZ")
end
end)
function scheduleServerRestart(timestamp)
restartingAt = timestamp
ModData.add("UpdatePLZ", { restartingAt = timestamp })
ModData.transmit("UpdatePLZ")
UpdatePLZ.startRestartCountdown(timestamp)
end
end
local function rebootWhenEmpty()
if pendingReboot and isServerEmpty() then
rebootServer()
Events.OnTickEvenPaused.Remove(rebootWhenEmpty)
end
end
local function workshopOutdated()
if pendingReboot then return end
pendingReboot = true
if isServerEmpty() then
print("[UpdatePLZ] Restarting the server (server empty and outdated Workshop items were detected)")
rebootServer()
return
end
if UpdatePLZ.restartDelay then
print("[UpdatePLZ] Detected outdated Workshop item - restarting server in " .. UpdatePLZ.minutes(UpdatePLZ.restartDelay))
scheduleServerRestart(getTimestamp() + UpdatePLZ.restartDelay)
else
print("[UpdatePLZ] Restarting the server when it becomes empty... (outdated Workshop items were detected)")
Events.OnTickEvenPaused.Add(rebootWhenEmpty)
end
end
local pollWorkshop do
local fakeTable = {}
function pollWorkshop()
if pendingReboot then return end
print("[UpdatePLZ] Checking for outdated Workshop items...")
querySteamWorkshopItemDetails(getSteamWorkshopItemIDs(), function(_, status, info)
if status ~= "Completed" then return end
for i = 0, info:size() - 1 do
local details = info:get(i)
local updated = details:getTimeUpdated()
if updated >= serverStarted then
workshopOutdated()
return
end
end
end, fakeTable)
end
end
Events.OnDisconnect.Add(pollWorkshop)
local nextPoll
Events.OnTickEvenPaused.Add(function()
if restartingAt then
if isServerEmpty() then
print("[UpdatePLZ] Restarting the server now! (Outdated Workshop items were detected and server is empty)")
rebootServer()
elseif restartingAt - getTimestamp() <= 0 then
print("[UpdatePLZ] Restarting the server now! (Outdated Workshop items were detected)")
rebootServer()
end
return
end
if pendingReboot then return end
-- Don't bother checking for outdated Workshop items if there's no restart delay and the server has players on it
if not UpdatePLZ.restartDelay and not isServerEmpty() then return end
local timestamp = getTimestamp()
if not nextPoll or getTimestamp() >= nextPoll then
nextPoll = timestamp + 60
return pollWorkshop()
end
end)

View File

@@ -0,0 +1,88 @@
local getTimestamp = getTimestamp
local isServer = getCore():isDedicated()
UpdatePLZ = UpdatePLZ or {}
local chat do
if isServer then
chat = print
else
require "client/Chat/ISChat.lua"
local chatMsg = {
getTextWithPrefix = function(self)
return self.msg
end,
getText = function(self)
return self.msg
end,
isServerAlert = function() return true end,
isShowAuthor = function() return false end,
getAuthor = function() return "SERVER" end
}
chatMsg.__index = chatMsg
function chat(...)
local msg = table.concat({...}, "\t")
ISChat.addLineInChat(setmetatable({ msg = msg }, chatMsg), 0)
end
end
end
function UpdatePLZ.minutes(secs)
if secs < 60 then
if secs == 1 then
return secs .. " second"
else
return secs .. " seconds"
end
else
if secs / 60 == 1 then
return "1 minute"
else
return (string.gsub(string.gsub(string.format("%.2f", secs / 60), "(%.%d-)0*$", "%1"), "%.$", "")) .. " minutes"
end
end
end
local restartingAt
local nextChatPrint
local function countdown()
local time = getTimestamp()
local delta = restartingAt - time
if not nextChatPrint or nextChatPrint - time <= 0 then
nextChatPrint = time + math.min(delta / 2, 60 * 15)
if math.floor(delta / 60) <= 0 then
chat("WARNING: Server is restarting to update Workshop mods!")
Events.OnTickEvenPaused.Remove(countdown)
else
chat("WARNING: Server is restarting in " .. UpdatePLZ.minutes(delta) .. " to update Workshop mods")
end
end
end
function UpdatePLZ.startRestartCountdown(__restartingAt)
nextChatPrint = nil
restartingAt = __restartingAt
if restartingAt then
Events.OnTickEvenPaused.Add(countdown)
else
Events.OnTickEvenPaused.Remove(countdown)
end
end
if not isServer then
Events.OnInitGlobalModData.Add(function()
Events.OnReceiveGlobalModData.Add(function(key, modData)
if key == "UpdatePLZ" then
UpdatePLZ.startRestartCountdown(modData and modData.restartingAt or nil)
end
end)
ModData.request("UpdatePLZ")
end)
end

3
mods/updateplz/mod.info Normal file
View File

@@ -0,0 +1,3 @@
name=Update PLZ
id=UpdatePlz
description=Restart the (empty) dedicated server if mods are outdated

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (1 minute)
id=UpdatePlz1
description=Restart the dedicated server if mods are outdated after 1 minute
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (1 minute)
id=UpdatePlz1
description=Restart the dedicated server if mods are outdated after 1 minute
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(60)

4
mods/updateplz1/mod.info Normal file
View File

@@ -0,0 +1,4 @@
name=Update PLZ (1 minute)
id=UpdatePlz1
description=Restart the dedicated server if mods are outdated after 1 minute
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(10 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (10 minutes)
id=UpdatePlz10
description=Restart the dedicated server if mods are outdated after 10 minutes
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (10 minutes)
id=UpdatePlz10
description=Restart the dedicated server if mods are outdated after 10 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(10 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (10 minutes)
id=UpdatePlz10
description=Restart the dedicated server if mods are outdated after 10 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(15 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (15 minutes)
id=UpdatePlz15
description=Restart the dedicated server if mods are outdated after 15 minutes
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (15 minutes)
id=UpdatePlz15
description=Restart the dedicated server if mods are outdated after 15 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(15 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (15 minutes)
id=UpdatePlz15
description=Restart the dedicated server if mods are outdated after 15 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(30 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (30 minutes)
id=UpdatePlz30
description=Restart the dedicated server if mods are outdated after 30 minutes
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (30 minutes)
id=UpdatePlz30
description=Restart the dedicated server if mods are outdated after 30 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(30 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (30 minutes)
id=UpdatePlz30
description=Restart the dedicated server if mods are outdated after 30 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(5 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (5 minutes)
id=UpdatePlz5
description=Restart the dedicated server if mods are outdated after 5 minutes
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (5 minutes)
id=UpdatePlz5
description=Restart the dedicated server if mods are outdated after 5 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(5 * 60)

4
mods/updateplz5/mod.info Normal file
View File

@@ -0,0 +1,4 @@
name=Update PLZ (5 minutes)
id=UpdatePlz5
description=Restart the dedicated server if mods are outdated after 5 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(60 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (60 minutes)
id=UpdatePlz60
description=Restart the dedicated server if mods are outdated after 60 minutes
require=UpdatePlz

View File

@@ -0,0 +1,4 @@
name=Update PLZ (60 minutes)
id=UpdatePlz60
description=Restart the dedicated server if mods are outdated after 60 minutes
require=UpdatePlz

View File

@@ -0,0 +1,3 @@
require "updateplz.lua"
if not getCore():isDedicated() then return end
UpdatePLZ.setRestartDelaySeconds(60 * 60)

View File

@@ -0,0 +1,4 @@
name=Update PLZ (60 minutes)
id=UpdatePlz60
description=Restart the dedicated server if mods are outdated after 60 minutes
require=UpdatePlz