Jump to content




Melee turtles and os.pullEvent

turtle help

  • You cannot reply to this topic
8 replies to this topic

#1 eastar

  • Members
  • 19 posts

Posted 10 July 2018 - 12:02 AM

Hi

I'm building a mobspawner.
Turtles would do the killing part but I can't seem to tackle an issue.
I just can't figure how to get the turtle to keep swinging his sword until an event occures.
I could probably resume the program by using another very fast timer to skip the waiting, but that's just doesn't seem like a proper way to do.
Could someone help me out with a more elegant solution?

So far this is what I got: (in pastebin)
Spoiler

The code is not perfection and probably still got some (many?) flaws.
Mainly I'm focused on the
attack()

event = os.pullEvent()
part.

Thanks the help in advance!
Cheers! :-)

#2 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 10 July 2018 - 05:07 AM

ComputerCraft is event-driven - whenever your code needs to "wait" for anything, be it a timer or for a sword swing to finish, it "yields" execution and pauses until such time as it's "resumed" with event data.

For example, turtle.attack() works (loosely) along these lines:

function turtle.attack()
	local actionID = native.turtle.attack()  --# Hidden function we don't have access to in our own scripts,
						 --# which starts a sword swing and returns without waiting for it to finish.
	
	--# Then loops until an event with the result appears: 
	repeat
		local event, ID, result = os.pullEvent("turtle_event")
	until ID == actionID

	return result
end

One problem with this is that if a redstone_event occurs while this code is waiting for its turtle_event, it'll pull it from the front of the event queue and then discard it. You won't be able to pull it later in your code, so you'll actually miss some redstone state changes.

Luckily, the parallel API offers an easy workaround to this - it allows you to run multiple functions alongside each other as coroutines, passing copies of all events to each of them separately. While one coroutine is yielding the other can be actively executing code (which solves your waiting issues), and if one coroutine doesn't want a certain event, then that won't stop the other from reading its own copy (which solves your redstone issues).

local function swingSword()
	while true do
		turtle.attack()
	end
end

local function doRedstoneStuff()
	while true do
		local event = os.pullEvent()
		
		if event == "redstone" then  --# The quotes are important here!
			if rs.getInput(inputPos) then
				.
				.
				.
			end
		end
	end
end

parallel.waitForAny(swingSword, doRedstoneStuff)


#3 eastar

  • Members
  • 19 posts

Posted 11 July 2018 - 01:09 AM

Thank you!

I have made a short program to test the parallel workaround with the turtle. Although it is not the real program that I intend to use but the test worked!
Also thank you for pointing out the error I made at the event handling ( redstone / "redstone" ).

I have a couple of questons though...
I started tinkering with this parallel.waitForAny() function and realised that the reason I couldn't use the turtle.attack() is the same why I can't use sleep() with os.pullEvent().
sleep() expects a "timer" event just like turtle.attack() expects a "turtle_event".

So I made a small program using sleep(), os.pullEvent() and parallel.waitForAny() just for the sake of testing:
(pastebin)
Spoiler

When it runs the following happens:
It prints 1
It reacts to the redstone events.
But the sleeper() function stops at sleep()

Why does it stop there?

Thank you again for your help!

Edited by eastar, 11 July 2018 - 01:10 AM.


#4 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 11 July 2018 - 02:03 AM

parallel.waitForAny() runs all the supplied functions together until such time as "any" of them return. Your redstone() function returns after pulling its first event, so waitForAny() returns at that point as well, and the incomplete coroutine handling the sleeper() function is simply discarded.

If you want "all" the coroutines to run to completion, then use waitForAll(). If you want to loop the execution of your coroutines, then you'll probably be better off placing "while" loops inside their functions (so that they can repeat independently of each other).

#5 eastar

  • Members
  • 19 posts

Posted 11 July 2018 - 03:27 PM

Thank you so much! I got it now!
The redstone() function returned becouse it recieved the "timer" event from the sleep() in the sleeper() function. I didn't specify for the redstone() function to only look for "redstone" event. After that since the waitForAny was in a while loop it started all over again and again!

I'm enlightened now! :-)

Thank you again kind sir!

#6 eastar

  • Members
  • 19 posts

Posted 12 July 2018 - 04:37 PM

Hi again!

With your help I managed to write the program I needed. Thank you! :)

Before I finished the code I ran into a very weird problem to which I found an even weirder solution.

When one of the functions in parallel.waitForAny() has nothing to do the turtle just shuts down(!)
Why??? The whole code is in while loops and I don't use any break.
The solution I found for this is to put sleep() inside the functions. I can even put sleep(0) in there so the turtle will keep ...not shutting down.
I really would like to know why is it like this? What's happening here?

The finished code:

Spoiler


#7 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 12 July 2018 - 09:11 PM

You're triggering computercraft's yield prevention because your code is not yielding. This blocks all other parallel functions and all other computers in the world from executing until it is shut down.

#8 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 13 July 2018 - 03:56 AM

To expand on that - in reality, each ComputerCraft system in your Minecraft world is really just one of however many coroutines running within a single Lua VM. Only one coroutine is ever actually executing code at a time, and all the others have to keep on yielding until they "get their turn".

Normally code executes so fast that the switching process allows all your systems to appear to be running "at once" (and most systems spend most of their time yielding anyway). But if you write some code that doesn't yield for an extended period, then all the other systems will "freeze up" until its done.

Obviously that's a bad thing, regardless as to whether you're doing it on purpose or not, and so if ComputerCraft notices a coroutine running for more than about ten seconds it'll kill it. Sometimes it can just kill your script (in which case you'll see a "too long without yielding" error), but sometimes it has to kill your system's entire coroutine (in which case that device will simply turn off).

#9 eastar

  • Members
  • 19 posts

Posted 15 August 2018 - 12:53 PM

View PostBomb Bloke, on 13 July 2018 - 03:56 AM, said:

To expand on that - in reality, each ComputerCraft system in your Minecraft world is really just one of however many coroutines running within a single Lua VM. Only one coroutine is ever actually executing code at a time, and all the others have to keep on yielding until they "get their turn".

Normally code executes so fast that the switching process allows all your systems to appear to be running "at once" (and most systems spend most of their time yielding anyway). But if you write some code that doesn't yield for an extended period, then all the other systems will "freeze up" until its done.

Obviously that's a bad thing, regardless as to whether you're doing it on purpose or not, and so if ComputerCraft notices a coroutine running for more than about ten seconds it'll kill it. Sometimes it can just kill your script (in which case you'll see a "too long without yielding" error), but sometimes it has to kill your system's entire coroutine (in which case that device will simply turn off).

Hello!

Thank you very much!
It absolutely makes sense! :)





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users