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

Event Basics

Started by Engineer, 10 August 2013 - 07:23 PM
Engineer #1
Posted 10 August 2013 - 09:23 PM
Introduction
Hello dear community,
Today I'm writing a tutorial about the event system we can use in CC-Lua. I call the language CC-Lua, because normal plain Lua you install on your computer won't be compatible with a few function calls. For those who are not familair, CC stands for ComputerCraft.

So, what are events? Events are things you can trigger by clicking, hitting a key, scrolling your mousewheel and much more. The goal of this tutorial is to let you know how we can get the events, how we can use the events and why you want to use events.

For this tutorial you need to know what:Now, after this short introduction, lets start the tutorial!

Event Basics
So events are things that get triggered when something happens to the computer. For example, it could be your input, but also things like redstone could trigger an event. So almost anything that happens to the computer, can trigger an event. But this does not count for running code, actually, it can but we will get into that later.

To let our program know something is going to happen is using this function:

os.pullEvent()

This function is described on the wiki like so:

Boy, that is a lot of information at once. Lets forget the target-event for now, I want to explain the very basics first.

If we simply put os.pullEvent() plain in our code, it will wait for something to happen and then continue. So you cannot immediately print something after it. That code will look like this:

os.pullEvent()
print( "Hey, something happened to this computer!" )

Now if we want the information that happens, we have to catch that event first. That is going to look like this:

local event = os.pullEvent()

Basically we are creating a variable that catches what the os.pullEvent() returns. Just like you in the image read:
os.pullEvent(target-event) returns the event and any parameters the event may have.

Wait a minute, it can return parameters too? That means that os.pullEvent() can return multiple values! Per event it differs how many values get returned, so we do the max, which is 5 by the default events ( I will get into custom events later ), because we want to catch everything.
Usually, something like this gets done, it could be totally different since it are variables!

local event, value1, value2, value3, value4, value5 = os.pullEvent()

So we catch this in order:
  • The event name
  • The 1st event-value that gets returned
  • The 2nd event-value that gets returned
  • The 3rd event-value that gets returned
  • The 4th event-value that gets returned
  • The 5th event-value that gets returned
To know what actually gets returned as first, second, third or fourth value, we can look that up on this page. A nice table is laid out for us on the bottom so we can see what kind of events happen regularly and what they return with them. They have their own packet so to say.

Now we can get into the argument os.pullEvent takes: string target-event (See the wiki image!)
This will say, that the argument is a string, so we need the quotes. Now what we can do, is pull only one event! So if you do not need an event that says that a peripheral gets attached, this is the solution. But there is one catch, you can only use that one type of event for those variables. So lets say we want the char event ( see the wiki page! ), we can simply do this:

local event, key = os.pullEvent( "char" )

We can actually name our arguments, because we know we are only to get the char event from this.

The point of this event system is, that you can do multiple stuff, without doing complicated things with coroutines (WARNING: This is advanced! Get into it when you're curious enough!).

We mainly use a while-loop to pull constantly events and process them. So, to create an infinite while-loop we do:

while true do

end

And do you remember that os.pullEvent() waits until something happened, or the target event gets hit? So does it here, it literally blocks the loop from looping until something happened.

Now, since we probably want to catch multiple events, it is going to be a plain os.pullEvent() with the catch variables. Like so:

while true do
  local event, value1, value2, value3, value4 = os.pullEvent()
end

At this point, we can process the events, because we know what event happens. You create an if-elseif-end structure, and by the way you do remember you can look up the events with their values, right?
This is going to be your basic structure:

while true do
  local event, value1, value2, value3, value4 = os.pullEvent()
  if event == "eventName" then

  elseif event == "otherEventName" then

  end
end

You actually replace 'eventName' and 'otherEventName' with actual event names you looked up, right? :P/>/>/>/>
Within that if, you can process those values to your wishes.

Now I want to notify you on something that might be usefull to you, if you hate those ugly variable names like value1 etc. We can put the os.pullEvent() in one table, and then use the table to get your values:

while true do
  local eventValues = { os.pullEvent() }
  if eventValues[1] == "eventName" then

  elseif eventValues[1] == "otherEventName" then

  end
end
I will not get deep into this, but it might usefull to you.

Okay, if you are still with me I will show you show some examples and explain the os.queueEvent function.
But first, the examples we can create with what we have learned today. Im going ahead and put the examples in a spoiler, since it will probably get long again. I dont want to over use the space this thread is already taking ;)/>/>/>
Examples:
SpoilerNow first, let's make our own 'press a key to continue'. It is very easy to do, since it can be any key we only have to pull the 'key' event. So we are using the argument off os.pullEvent().

print( "Please press a key to continue!" )
os.pullEvent( "key" )
print( "You pressed a key, so lets continue!" )
There is not much going on in this code. Since we do not need the event name nor the key number we do not have to catch the events. We simply pull the 'key' event and just relax until someone hits the key!

Now let's make a 'press a key to continue within 3 seconds' type of thing. This code is going to be partially commented, a comment in lua is with two dashes everything after that 'does not count for the code'.

And check out the timer event before you start understanding this, it will help you out!

local secondsPast = 0 -- A simple variable that will count for us
local timerID = os.startTimer( 1 ) -- A variable that holds the timer ID and starts the timer

print( "Press a key within 3 seconds" )

while true do
   local event, value1 = os.pullEvent()
   if event == "timer" and value1 == timerID then
	  secondsPast = secondsPast + 1 -- Increment secondsPast by one, a second has passed
	  if secondsPast == 3 then
		 break
		 -- 3 seconds have passed, lets break the loop and move on with the code.
	  else
		 timerID = os.startTimer( 1 )
		 -- Restart the timer
	  end
   elseif event == "key" then
	 break
	 -- A key event occured, lets move on with the code, once again
   end
end

if secondsPast == 3 then
   print( "You did not press a key within the 3 seconds!" )
else
   print( "You pressed the key!" )
end

If you understand the events by now, this code is a piece of cake! We simply increase a variable that holds the passed seconds, and we start a timer. Then we simply compare events and boom, we move on with the code and can check that variable again if the 3 seconds have passed. It is that simple!

Custom events
It is very simple to create custom events. The function you will use for that, as mentioned earlier, os.queueEvent(), but we need arguments.

Basically, the first argument is your eventname, and after that you pass variables you want to pass. The next time you are trying to os.pullEvent() it will show up!
Warning: functions like: read(), sleep() etc. empty the event stack and you will not be able to pull the event after that

So this is an example:

os.queueEvent( "testevent", 5 )
local event, value = os.pullEvent()
-- event -> testevent
-- value -> 5

Do whatever you need your own special event :)/> Oh, and by the way, you can simulate a keypress:

os.queueEvent( "key", 26 )

This probably wraps up this tutorial… for now.. :P/>

If you have any question please ask them! I will answer them A.S.A.P. and if they are usefull for other users I will put it in the tutorial self.

If I made any mistakes, please correct me! English is not my native language but I try my best :)/>

Thanks for reading,

- Engineer
Bubba #2
Posted 10 August 2013 - 09:53 PM
Looks good! One thing you may want to mention is os.queueEvent, but other than that this is a very good beginner's tutorial. You link anything that might need explaining and provide both explanation and code. Nicely done :)/>

Edit: Oh, and you might want to explain events as anything that computers are notified about rather than anything that the user triggers. The user can trigger events, but for example redstone is not. Just nitpicking here.
Engineer #3
Posted 11 August 2013 - 01:09 AM
Looks good! One thing you may want to mention is os.queueEvent, but other than that this is a very good beginner's tutorial. You link anything that might need explaining and provide both explanation and code. Nicely done :)/>

Edit: Oh, and you might want to explain events as anything that computers are notified about rather than anything that the user triggers. The user can trigger events, but for example redstone is not. Just nitpicking here.
Thanks for the feedback! Actually, I was planning to do the os.queueEvent, but I decided to first wait for feedback. I actually mentioned somewhere something along the lines of custom events. I will write that up later.

I changed it, I hope it is better now :)/>
svdragster #4
Posted 16 August 2013 - 01:26 PM
Nice tutorial!
I didn't really know what the queueEvent does or how it works. Thanks!
Oompf #5
Posted 31 December 2013 - 06:21 AM
I didn't understand if multitasking is able to run code while a coroutine is waiting for a keypress (as i read the code you have to switch to the other "task" by command.

in this case it is not possible e.g. Count up a value and write to the screen until a key is pressed.


local function waitkeypress()
  local e
  while e = {os.pullEvent()} do
    if e[1] == "key" and e[2] == keys.enter then
      return true
    end
  end
end
local i = 0, waitc = coroutine.create(waitkeypress)
while coroutine.status(waitc) ~= "dead" do
  print(i)
  i = i + 1
end

would this work?


Warning: functions like: read(), sleep() etc. empty the event stack and you will not be able to pull the event after that

is this because of these comands using the stack itself?

if i want to count up once a second by adding a sleep(1) to the loop, it might happen that the event itself will be deleted by the sleep?
Engineer #6
Posted 01 January 2014 - 11:03 AM
I didn't understand if multitasking is able to run code while a coroutine is waiting for a keypress (as i read the code you have to switch to the other "task" by command.

in this case it is not possible e.g. Count up a value and write to the screen until a key is pressed.


local function waitkeypress()
  local e
  while e = {os.pullEvent()} do
	if e[1] == "key" and e[2] == keys.enter then
	  return true
	end
  end
end
local i = 0, waitc = coroutine.create(waitkeypress)
while coroutine.status(waitc) ~= "dead" do
  print(i)
  i = i + 1
end

would this work?

No, that wont really work. BUT, for information for os.pullEvent, I would refer to this tutorial: http://www.computerc...ics-coroutines/

is this because of these comands using the stack itself?
if i want to count up once a second by adding a sleep(1) to the loop, it might happen that the event itself will be deleted by the sleep?
Yeah, because if something gets called while sleeping, you really cant process it.
If you want to count up each second, you should use this:

local count = 0

local TIMER = os.startTimer(1)
while true do
	local e = {os.pullEvent()}
	if e[1] == "timer" and e[2] == TIMER then
		  TIMER = os.startTimer(1)
		  count = count + 1
	end
end

This way you can process each event that happens.
ZombieTurtle #7
Posted 13 February 2014 - 06:56 AM
This is really helpfull thank you :D/>
skwerlman #8
Posted 10 March 2014 - 10:54 AM
Is the event stack last-in-first-out or first-in-first-out?
EG:
if I did this
os.queueEvent("event1")
os.queueEvent("event2")
what would os.pullEvent() return?
theoriginalbit #9
Posted 10 March 2014 - 10:56 AM
-snip-
It's an event queue, so its FIFO.
skwerlman #10
Posted 10 March 2014 - 10:57 AM
Thanks! And so fast, too!
theoriginalbit #11
Posted 10 March 2014 - 10:59 AM
you're welcome :)/>