This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Lion4ever's profile picture

Computer State Restore / Reconstruct

Started by Lion4ever, 05 December 2013 - 12:44 PM
Lion4ever #1
Posted 05 December 2013 - 01:44 PM
Hi everybody :)/>

It was requested many times that dan implements a way to save the computer state when the world is closed, but he said that isn't possible.

So here is my work around:

It stores every event that happens on a computer (since my program ran) to a file. This file is named ".events".
And when the world is closed and opened again, you can run my program again and every event in the ".events" file is send to the computer again. So that the state of the computer is reconstruced!

My Program offers a clearEventLog() function and which is called every time you shut your pc down with os.shutdown or os.reboot.
The clearEventLog function can be called by pressing "F1".

This does work on turtles as long as the world is not closed while they are moving. But they are usually allways moving…

If there is any function that can cause a program to two diffent things (like turtle.detect() or math.random()) and isn't overwriten by my program, please send me a message :)/>

Download:
http://pastebin.com/HJ0YKjwR

Version History:
V1.1 (now)
  • Fixed restoring Crtl+T
  • Fixed bug that was pulling the "isomnia"-Event using math.random
To Do:
  • Get the turtles Movement going (again)
Known Bugs:
  • worm is not working correctly ( timers with other events inbetween in generall)
Edited on 08 December 2013 - 05:17 PM
Coding Brain Activated #2
Posted 06 December 2013 - 02:34 PM
I'm a bit of a programming noob, so can you explain how the system works? I don't see how you are able to run a program and keep it running in background while the computer works like normal. Is there a special API for this, or did you get help from a tutorial?
Bomb Bloke #3
Posted 06 December 2013 - 05:25 PM
It doesn't run in the background. You know how when you fail to localise a variable, it stays loaded in RAM once your script ends? The same is true of functions.

This operates by overwriting existing functions that rely on events, so that once you've run it once, any attempts to call those functions will use the versions created by this script, which save a record of the event queue to disk while running the regular versions of those functions. Running it after a reboot "replays" that event queue, then resumes recording again. It also tries to deal with other factors like whether turtles had blocks in front of them or what random dice rolls returned.

Because near everything that happens relies on events - from the shell you type commands into, to the sleep function, to whatever - this means that if you reboot your computer and run the script again it should be able to take you to the state the computer was in when you rebooted it. It's a rather cool idea.

I've tested it out though, and have a few issues to report:

I see some code in there that tries to wipe the saved event queue when "os.reboot()" is called, but Ctrl+R doesn't work that way so the regular user will probably end up scratching their heads as to how to clear it. It also doesn't seem to pick up on Ctrl+T.

Because the log file grows unchecked, this could cause some serious strain on a server's storage after running a computer for, say, a week. Users would be well-advised to factor in a way to have their scripts "os.reboot()" every few hours instead of relying on this to save everything forever.

The recording code requires some debugging. I can't actually get it to replay the "adventure" or "worm" scripts included with ComputerCraft, let alone my own. Seems it tries to replay events that weren't saved, or skips replaying some that were, so things get out of sync.
Lion4ever #4
Posted 08 December 2013 - 06:07 PM
Thanks, Bomb Bloke,
your feedback is a great help for me :)/>

Clearing the log when the user presses Crtl+R or Crtl+S, is only possible (as far as i know) if i would delete the log when "S" is pressed after "Ctrl", but it would be instant and i can't check wether "Ctrl" is still pressed…
But i could have pointed out, that "F1" clears the log :P/>
And this gives the user the opportunity to switch a pc off and still be able to restore it :)/>

Fixed that Crlt+T is not restored.

I don't see a way of saving/limiting disk size on my side without loosing data. Please tell me if you do :)/>
I guess this has to be the resposibility of the users.

Code has been debugged. "adventure" does now work. (I tried to pull an Event with math.random :D/>)
"worm" is now resumed, because the restore algorithm now looks for an (only one max) unfinshed timer. But i does not restore the position and the score (i have no clue why yet).

And again thanks for your help^^
Bomb Bloke #5
Posted 11 December 2013 - 06:54 AM
Clearing the log when the user presses Crtl+R or Crtl+S, is only possible (as far as i know) if i would delete the log when "S" is pressed after "Ctrl", but it would be instant and i can't check wether "Ctrl" is still pressed…
But i could have pointed out, that "F1" clears the log :P
And this gives the user the opportunity to switch a pc off and still be able to restore it :)
Indeed, I can't really fault the behaviour - but I do feel it's worth documenting.

I don't see a way of saving/limiting disk size on my side without loosing data. Please tell me if you do :)
I guess this has to be the resposibility of the users.
Again I agree - just so long as it's made clear that they should do so.

Speaking of which, fs/io calls may also trip this script up - say a script reads data from a file, deletes that file, then writes a new one on exit. If the MineCraft server shuts down, then there's no way to resume that particular script correctly without recording what the file reads returned yourself. Such a level of recording may be going overboard, however.

"worm" is now resumed, because the restore algorithm now looks for an (only one max) unfinshed timer. But i does not restore the position and the score (i have no clue why yet).
While that's not the behaviour I get, it's certainly not working as expected, so I figured I'd take a bit of a dig into this - even read through the "worm" script (Messy! Declares a non-local function and all!) before figuring it out.

Line 46 returns "fakeTable[2]" instead of [1]. This means that during "playback" scripts may start waiting for timer events with an ID of "timerStart" (which'll never come).

However, fixing that still leaves the problem that if "playback" finishes and the recorded script is still waiting for a timer event to come back, then that event never gets queued and so it waits forever. If there were no timers hanging at the time of the shutdown it'll otherwise work correctly.

This script could os.queueEvent() one for it, though you'll indeed need to keep track of how many timers are still supposed to be "running" when playback finishes to do it correctly. Perhaps keep tabs on os.clock() in order to figure out when the "fake" timer event should be returned… On the other hand, if you're going to do that, there's no need to actually queue the event properly at all, I suppose.

Speaking of which, os.clock() and the other time-returning functions are others that should be overridden. Perhaps results from things like peripheral.isPresent(), too. It's also missing a method of dealing with os.pullEventRaw()'s being used directly by recorded scripts, which is probably most important.
Wojbie #6
Posted 11 December 2013 - 07:35 AM
Clearing the log when the user presses Crtl+R or Crtl+S, is only possible (as far as i know) if i would delete the log when "S" is pressed after "Ctrl", but it would be instant and i can't check wether "Ctrl" is still pressed…
But i could have pointed out, that "F1" clears the log :P/>
And this gives the user the opportunity to switch a pc off and still be able to restore it :)/>

I have created this code when working on my swarm miner - its designed to detect if a key was pressed while ctrl was still hold based on situation that system will not give char event after key event if ctrl is held down when key was pressed.

Sadly this requires to look trough whole event queue and i think it wouldn't work good with your program. Still i am gonna leave it here for you to analize and hopefully you will find some use in this. Also it only detects if ctrl+s was held down - it will not tell you if it was held required 2 sec :)/>
I use it to force program to save its state to file and pause operating for 3 sec (cause if its still online after 3 sec then player stopped holding it down before termination/shutdown/reset heppend :P/> ). its also useful for making costum ctrl+key shortcuts.

You give it number from key event and it will return true if it was pressed with ctrl held down, false if not.
It gets all event from event queue until "testchar"..test event. Then it queueEvent s all way back to point where it was called. Its not supposed to be used inside parrarel functions cause of way it works can mess up event queue a lot.


local function testforchar(A)
		local out=false
		local test=keys.getName(A)
		local tempT={}
		os.queueEvent("testchar"..test) -- possible loose of event? Tested - 100% operational
	  
		while true do
				table.insert(tempT,{os.pullEvent()})
				--print(unpack(tempT[#tempT]))
				if tempT[#tempT][1]=="char" then
						if tempT[#tempT][2]==test then out=true end
				elseif tempT[#tempT][1]=="testchar"..test then
						table.remove(tempT,#tempT) break
				end
		end

		while #tempT >0 do
				os.queueEvent(unpack(table.remove(tempT)))
		end

		return out
end

Hope you find it usefull!!
Edited on 11 December 2013 - 06:39 AM
apemanzilla #7
Posted 12 December 2013 - 08:21 AM
This sounds really interesting, I'm looking forward to trying it later today.
Lion4ever #8
Posted 12 December 2013 - 09:26 AM
Speaking of which, fs/io calls may also trip this script up - say a script reads data from a file, deletes that file, then writes a new one on exit. If the MineCraft server shuts down, then there's no way to resume that particular script correctly without recording what the file reads returned yourself. Such a level of recording may be going overboard, however.
Noted for Version 1.2

Line 46 returns "fakeTable[2]" instead of [1]. This means that during "playback" scripts may start waiting for timer events with an ID of "timerStart" (which'll never come).
Fixed (line 45 as well)

Speaking of which, os.clock() and the other time-returning functions are others that should be overridden. Perhaps results from things like peripheral.isPresent(), too. It's also missing a method of dealing with os.pullEventRaw()'s being used directly by recorded scripts, which is probably most important.
I am going to add os.clock and peripheral.isPresent in V1.2, too. :)/>

os.pullEventRaw however is difficult to overwrite, because rednet uses it and queue it again. So that if os.pullEventRaw is overwritten, everything is recording twice…
But i really need to do that somehow :(/>

… system will not give char event after key event if ctrl is held down …
That is nice to know :)/>


Thanks for all of your help^^
TheOddByte #9
Posted 12 December 2013 - 01:41 PM
This sounds interesting, Might try it out a little later ;)/>
KidBrine #10
Posted 07 April 2017 - 12:54 AM
i'm confused how does it work(looking for examples)
Lupus590 #11
Posted 07 April 2017 - 10:22 AM
i'm confused how does it work(looking for examples)
It stores every event that happens on a computer (since my program ran) to a file. This file is named ".events".
And when the world is closed and opened again, you can run my program again and every event in the ".events" file is send to the computer again. So that the state of the computer is reconstruced!

The requested example:
  • I run this program, it starts listening for events (how exactly is does this is not important)
  • I run the program which needs to be able to preserve its state
  • an event is received (let's call it event1), the event recorder writes it to the file and then lets it be passed to my program, which handles it normally
  • another event is received (event2), this is also recorded to the file and passed to my program which again handles it normally
  • this continues for event3, event4, event5, etc. until the computer is unloaded from the world
  • Later the computer is loaded again, it does its normal boot sequence until it gets to running the startup file (which I made before leaving the computer). the startup file runs the event listener followed by my program which needed to preserve its state
  • the event recorder notices that the file it writes to already exists and has data in it, it then reads the file one event at a time and queues the event again. it does this for every event in the file
  • my program does its normal setup and starts pulling events, unaware that they were queued by the event recorder, it receives and handles event1
  • my program then gets ready to pull another event and is given event2 from the recorder, this continues until the last event from the file is pulled. hopefully, all this is happening faster than those events originally came in
  • with all of the events from the file pulled, the event recorder returns to listening mode and starts recording events and passing them to my program again
I don't know what happens if it receives 'live' events while in 'replay' mode

Also, you've done a bit of thread necomancy