Skip to content

Commit 3b50ddc

Browse files
committed
Added safer lhs writing:
Closes #127
1 parent 13da6d1 commit 3b50ddc

3 files changed

Lines changed: 111 additions & 9 deletions

File tree

levelEditor/levelRoot.lua

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ function UI.loadErrorHandler(message)
1919
local trace = fullTrace:sub(1,index-1)
2020
MainUI:popup("Failed to load level!","Error message: "..message,trace)
2121
end
22+
function UI.saveErrorHandler(message)
23+
message = tostring(message)
24+
--part of snippet yoinked from default löve error handling
25+
local fullTrace = debug.traceback("",2):gsub("\n[^\n]+$", "")
26+
print(message)
27+
print(fullTrace)
28+
--cut of the part of the trace that goes into the code that calls UI:openEditor()
29+
local index = fullTrace:find("%s+%[C%]: in function 'xpcall'")
30+
local trace = fullTrace:sub(1,index-1)
31+
return {message,trace}
32+
end
2233

2334
function UI:initialize(levelPath, workshop)
2435
self.workshop = workshop
@@ -68,9 +79,34 @@ end
6879

6980
function UI:save()
7081
if self:checkLimits("Can't save level:\n") then
71-
self.levelFile:serializeAll(self.level)
72-
self.latestHash = self.levelFile:writeAll()
73-
MainUI:popup("Succesfully saved level!")
82+
local success, hashOrErr = xpcall(
83+
function()
84+
self.levelFile:serializeAll(self.level)
85+
return self.levelFile:writeWithBackup()
86+
end,
87+
self.saveErrorHandler
88+
)
89+
if success then
90+
self.latestHash = hashOrErr
91+
MainUI:popup("Succesfully saved level!")
92+
else
93+
local files = ""
94+
for _,path in ipairs(self.levelFile.tempFiles) do
95+
if files~="" then
96+
files = files .. "\n"
97+
end
98+
files = files .. path
99+
end
100+
MainUI:popup(
101+
"Error saving level:",
102+
hashOrErr[1],
103+
"\nLeft over files:",
104+
files,
105+
"\n",
106+
hashOrErr[2]
107+
)
108+
return
109+
end
74110
end
75111
end
76112

levelhead/lhs/init.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ LHS.tags = {
5151
function LHS:initialize(path)
5252
self:loadFile(path)
5353
self.rawContentEntries = {}
54+
--- Files created while writing
55+
self.tempFiles = {}
5456
end
5557

5658
--load the other files

levelhead/lhs/write.lua

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,6 @@ end
159159

160160

161161
function LHS:writeAll()
162-
local file = NFS.newFile(self.path)
163-
local success,err = file:open("w")
164-
if not success then error(err) end
165-
self.saveHandle = file
166-
167162
self:writeHeaders()
168163

169164
self:writeSingle("singleForeground")
@@ -181,11 +176,80 @@ function LHS:writeAll()
181176
self:writeSingle("singleBackground")
182177
self:writeStructure("backgroundRows")
183178
self:writeStructure("backgroundColumns")
184-
local hash = self:writeHash()
179+
local hash = self:writeHash()
180+
return hash
181+
end
182+
183+
function LHS:writeDirectly()
184+
local file = NFS.newFile(self.path)
185+
local success,err = file:open("w")
186+
if not success then error(err) end
187+
self.saveHandle = file
188+
189+
local hash = self:writeAll()
185190

186191
self.saveHandle:close()
187192
self.saveHandle = nil
188193
return hash
189194
end
190195

196+
function LHS:writeWithBackup()
197+
local basePath, file = self.path:match("^(.+)[/\\]([^/\\]*)$")
198+
if not basePath or not file then
199+
error("Failed getting directory path", 2)
200+
end
201+
local r = string.format("%06i", love.math.random(0,999999))
202+
local backupPath = basePath .. "/" .. "ch-backup-"..r.."-"..file
203+
local _success, err = os.rename(self.path, backupPath)
204+
if err then
205+
error(string.format("Error moving\n%s\nto\n%s\n%s",self.path,backupPath,err),2)
206+
end
207+
table.insert(self.tempFiles, backupPath)
208+
209+
local hash = self:writeDirectly()
210+
211+
local checker = self.class:new(self.path)
212+
local success, err = xpcall(function()
213+
checker:readAll()
214+
end, function(message)
215+
message = tostring(message)
216+
--part of snippet yoinked from default löve error handling
217+
local fullTrace = debug.traceback("",2):gsub("\n[^\n]+$", "")
218+
print(message)
219+
print(fullTrace)
220+
--cut of the part of the trace that goes into the code that calls UI:openEditor()
221+
local index = fullTrace:find("%s+%[C%]: in function 'xpcall'")
222+
local trace = fullTrace:sub(1,index-1)
223+
return {message, trace}
224+
end)
225+
if not success then
226+
error(string.format(
227+
"Verification of written level failed: %s\nBackup of old level should live at: %s\n%s",
228+
err[1],
229+
backupPath,
230+
err[2]
231+
),2)
232+
end
233+
234+
local success, err = NFS.remove(backupPath)
235+
if not success then
236+
error(string.format(
237+
"Error removing backup at %s:\n%s",
238+
backupPath,
239+
err
240+
))
241+
end
242+
243+
local index = -1
244+
for i,v in ipairs(self.tempFiles) do
245+
if v==backupPath then
246+
index = i
247+
break
248+
end
249+
end
250+
table.remove(self.tempFiles,index)
251+
252+
return hash
253+
end
254+
191255
return LHS

0 commit comments

Comments
 (0)