1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
local lfs = require("lfs")
local enableDebug = true
local enableImGui = false
local flatpakSteam = true
-- Name of your local workshop container
local workshopName = "MyModWorkshop"
-- Mods live here inside the git repo
local repoModsDir = "mods"
local isWindows = package.config:sub(1, 1) == "\\"
local sep = isWindows and "\\" or "/"
local homeDir = isWindows and os.getenv("userprofile") or os.getenv("HOME")
local flatpakSteamVar = not isWindows and os.getenv("HOME") .. "/.var/app/com.valvesoftware.Steam"
-- Launch via Steam
local pzCacheRoot
local pzExecutable
if isWindows then
pzCacheRoot = (homeDir .. "\\Zomboid")
pzExecutable = "steam.exe -applaunch 108600"
else
pzExecutable = flatpakSteam and "flatpak run com.valvesoftware.Steam -applaunch 108600" or "steam -applaunch 108600"
pzCacheRoot = flatpakSteam and (flatpakSteamVar .. "/Zomboid") or (homeDir .. "/Zomboid")
end
-- Final destination: Workshop/<Name>/Contents/mods
local pzModsDir = pzCacheRoot .. sep .. "Workshop" .. sep .. workshopName .. sep .. "Contents" .. sep .. "mods"
-- =========================
-- File helpers
-- =========================
local function copyFile(src, dst)
local inFile = assert(io.open(src, "rb"))
local data = inFile:read("*all")
inFile:close()
local outFile = assert(io.open(dst, "wb"))
outFile:write(data)
outFile:close()
end
local function mkdir(path)
if isWindows then
os.execute(string.format('mkdir "%s" 2>nul', path))
else
os.execute(string.format('mkdir -p "%s"', path))
end
end
local function copyDir(src, dst)
mkdir(dst)
for name in lfs.dir(src) do
if name ~= "." and name ~= ".." then
local srcPath = src .. sep .. name
local dstPath = dst .. sep .. name
local attr = lfs.attributes(srcPath)
if attr.mode == "directory" then
copyDir(srcPath, dstPath)
else
copyFile(srcPath, dstPath)
end
end
end
end
-- =========================
-- Safety helpers
-- =========================
local function normalizePath(path)
path = path:gsub("\\", "/")
return path:gsub("/+$", "")
end
local function isRootPath(path)
if path == "/" then
return true
end
if path:match("^[A-Za-z]:$") then
return true
end
return false
end
local function isSubPath(child, parent)
child = normalizePath(child)
parent = normalizePath(parent)
return child:sub(1, #parent + 1) == parent .. "/"
end
local function validatePath(target, root, home)
local t = normalizePath(target)
local r = normalizePath(root)
local h = normalizePath(home)
assert(t ~= "", "Refusing to delete empty path")
assert(not isRootPath(t), "Refusing to delete root path")
assert(t ~= h, "Refusing to delete home directory")
assert(isSubPath(t, r), "Refusing to delete outside workshop mods")
assert(#t > #r + 1, "Refusing to delete mods root")
end
-- =========================
-- Discover mods dynamically
-- =========================
local mods = {}
for name in lfs.dir(repoModsDir) do
if name ~= "." and name ~= ".." then
local path = repoModsDir .. sep .. name
local attr = lfs.attributes(path)
if attr and attr.mode == "directory" then
table.insert(mods, name)
end
end
end
assert(#mods > 0, "No mods found in ./mods directory")
-- =========================
-- Copy mods
-- =========================
print("Installing mods into workshop:")
mkdir(pzModsDir)
for _, modName in ipairs(mods) do
local src = repoModsDir .. sep .. modName
local dst = pzModsDir .. sep .. modName
print(" - " .. modName)
print(" - copied" .. src .. " to " .. dst)
validatePath(dst, pzModsDir, homeDir)
copyDir(src, dst)
end
print("All mods copied successfully!")
-- =========================
-- Launch Project Zomboid
-- =========================
local params = {}
table.insert(params, "-modfolders workshop,mods")
if enableDebug then
table.insert(params, "-debug")
end
if enableImGui then
table.insert(params, "-imgui")
end
local cmd = string.format("%s %s", pzExecutable, table.concat(params, " "))
print("Launching:")
print(cmd)
os.execute(cmd)
|