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

[1.31] Redstone-Event Extension

Started by Espen, 21 March 2012 - 01:03 PM
Espen #1
Posted 21 March 2012 - 02:03 PM
Description
It extends the "redstone" event that os.pullEvent() returns with the sides that have changed.



The API keeps track of the redstone input states on all 6 sides of the computer.
If any of the redstone input states change, the event "redstone" will return a string denoting the type of redstone-input, as well as a table of all the sides that have changed.
If vanilla redstone was changed, type will be "vanilla".
If bundled cable was changed, type will be "bundled_cable".
http://en.wikipedia.org/wiki/WTFPL

Exposed Function
pullEvent( event, side )
  • event is an optional event filter. ( Just like the native event filter, so nothing new. )
  • side is a table of strings and acts as a side filter, i.e. you can choose to listen to only specific sides and ignore all others.
  • Returns: event, type, changes
  • [indent=1]event == "redstone"[/indent]
  • [indent=1]type == "vanilla" (for vanilla redstone) or "bundled_cable" (for redpower bundled cable)[/indent]
  • [indent=1]changes = table of strings denoting the changed sides, e.g. { "back", "bottom }[/indent]
Color Legend:
String
Table




How to use?
  1. Save the API Code and make note of the filename, e.g. 're'
  2. Load the API within the program where you want to make use of it via os.loadAPI, e.g. os.loadAPI( "re" )
  3. Then call the API's pullEvent by accessing it via the API name, e.g. re.pullEvent()



API - Code
Pastebin: http://pastebin.com/egdXMS6X
Direct-View:
Spoiler

local tRedstoneState = {}  -- Holds the state of all redstone sides on the computer.
local tCableState = {}  -- Holds the state of all bundled cables on the computer.

-- Get initial redstone state for all 6 sides.
for _, side in pairs( rs.getSides() ) do
  tRedstoneState[ side ] = rs.getInput( side )
end

-- Get initial bundled cable state for all 6 sides.
for _, v in pairs( rs.getSides() ) do
  tCableState[ v ] = rs.getBundledInput( v )
end

-- If a redstone side changes its state, then this function returns the event 'redstone' plus the string 'vanilla' and a table.
-- The returned table contains only sides that have changed and it has this format: { side = new_value }
-- If a bundled cable changes its state, then this function returns the event 'redstone' plus the string 'bundled_cable' and a table.
-- The returned table contains only sides that have changed and it has this format: { side = { new_value, old_value } }
function pullEvent( _sEvent, _tSide )
  local bRedstoneChanged = false
  local bCableChanged = false
  local tChangedCable = {}
  local tChangedRedstone = {}
  local event, p1, p2, p3, p4, p5 = os.pullEvent( _sEventFilter )

  local function checkRedstoneSides( _tSide )
	-- Iterate over all _tSide sides of the computer.
	-- If _tSide is not given, iterate over all 6 sides.
	for _, side in pairs( _tSide or rs.getSides() ) do
	  local bCurrentInput = rs.getInput( side )  -- Get redstone input for current side.

	  if bCurrentInput ~= tRedstoneState[ side ] then  -- Has the state changed?
		tChangedRedstone[ side ] = bCurrentInput  -- Add the changed side + its new value to the return table.
		tRedstoneState[ side ] = bCurrentInput  -- Update the local state table.
		bRedstoneChanged = true
	  end
	end
	return bRedstoneChanged, tChangedRedstone
  end

  local function checkBundledCableSides( _tSide )
	-- Iterate over all _tSide sides of the computer.
	-- If _tSide is not given, iterate over all 6 sides.
	for _, side in pairs( _tSide or rs.getSides() ) do
	  local nCableInput = rs.getBundledInput( side )  -- Get cable input for current side.

	  if nCableInput ~= tCableState[ side ] then  -- Has the state changed?
		tChangedCable[ side ] = { nCableInput, tCableState[ side ] }  -- Add the changed side + its new and old values to the return table.
		tCableState[ side ] = nCableInput  -- Update the local state table.
		bCableChanged = true
	  end
	end
	return bCableChanged, tChangedCable
  end

  if event == "redstone" then
	local bChanged, tSides = checkRedstoneSides( _tSide )
	if bChanged then return event, "vanilla", tSides, p2, p3, p4, p5 end

	local bChanged, tSides = checkBundledCableSides( _tSide )
	if bChanged then return event, "bundled_cable", tSides, p2, p3, p4, p5 end
  end

  return event, p1, p2, p3, p4, p5
end



Example Program to try out the API
Save the API as "re".
Then run the program below, change some redstone inputs and see the results on screen.
Pressing "e" will exit the program.
Pastebin: http://pastebin.com/SpSbfa0h
Direct-View:
Spoiler

term.clear()
term.setCursorPos(1, 1)

-- Load API
os.loadAPI("re")
if not re then
  error( "Redstone Event Extension API not loaded!" )
end

--[[ === MAIN LOOP === ]]
while true do
  local event, param1, param2 = re.pullEvent()

  if event == "redstone" then
	write( event..", "..param1..", " )
	if param1 == "vanilla" then
	  for sSide, bChanged in pairs( param2 ) do
		write( "{ "..sSide.." = "..tostring( bChanged ).." }" )
	  end
	end
	if param1 == "bundled_cable" then
	  local newValue = 1
	  local oldValue = 2
	  for sSide, tChanged in pairs( param2 ) do
		write( "{ "..sSide.." = { "..tostring( tChanged[newValue] )..", "..tostring( tChanged[oldValue] ).." } }" )
	  end
	end
	write( "n" )
  end

  if event == "char" and string.lower( param1 ) == "e" then break end  -- Pressing 'e' will exit the program.
end

-- Cleanup
os.unloadAPI( re )
Liraal #2
Posted 21 March 2012 - 02:08 PM
quite useful IMO, but one thing is bugging me: why didn't you modify the default os.pullEvent()?
Espen #3
Posted 21 March 2012 - 02:19 PM
quite useful IMO, but one thing is bugging me: why didn't you modify the default os.pullEvent()?
For SSP it would be relatively easy to change this via the 'bios.lua', but you don't necessarily have the same luxury on SMP.
Also the global table in CC is protected, so you can't easily change os.pullEvent during runtime.

But regardless of this, the most important reason I preserved the native os.pullEvent() is because this way you can use both versions concurrently. It might not be necessary, but I'm all for choice, so there you go.
Think of it as a kind of Java 'extend' on os.pullEvent. :(/>/>
Liraal #4
Posted 21 March 2012 - 04:28 PM
there is an easy way. :(/>/>

and when I am adding functionality, i never worry about compatibility :)/>/>
Espen #5
Posted 21 March 2012 - 06:33 PM
You mean somthing like I did with os.pullEvent here? http://www.computercraft.info/forums2/index.php?/topic/393-terminate-safe-doublelock/page__view__findpost__p__2514

Yes, it is an easy workaround, but as I was saying the crucial point for me was to make it optional, so you can use both.
This way you can still do the workaround whenever and wherever you choose to, but are not forced to use it.
It's really just a personal design choice of mine. I don't want to force anything on the user. :(/>/>
Liraal #6
Posted 21 March 2012 - 07:11 PM
No I don't, I mean it more like:

t[1]=turtle.up
function up()
local state = t[1]()
if state then y=y+1 os.queueEvent("turtleUp") end
return state
end
rawset(turtle,"up",up)

which design I use for overwriting global variables/APIs.
But when I do so, I too always leave a way not to use it. :(/>/>
FuzzyPurp #7
Posted 21 March 2012 - 07:40 PM
Nice work Espen.
Wolvan #8
Posted 01 September 2012 - 12:48 PM
Am I allowed to add this in one of my APIs maybe and giving credit?
Espen #9
Posted 16 September 2012 - 03:04 PM
Am I allowed to add this in one of my APIs maybe and giving credit?
Sure, go right ahead.
Everybody can do with my stuff what they want. That's why I use the WTF "license": http://en.wikipedia.org/wiki/WTFPL :)/>/>