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

Autoyield for server

Started by valithor, 13 October 2015 - 02:21 AM
valithor #1
Posted 13 October 2015 - 04:21 AM
So, with computercraft only one computer can ever be running at a time. This creates the problem of a single computer running code that takes a large amount of time to run, and thus, keeping all of the other computers on the server from running. I have already created a solution for this problem, however, I would like a few comments on my solution to see if there is a better or more efficient way I could be accomplishing this.

Spoiler

local que = os.queueEvent
local coYield = coroutine.yield
local un = unpack
local tostr = tostring
local counter = 0


local function yield()
  que("yield event")
  local event = {coYield()}
  while tostr(event[1]) ~= "yield event" do
	que(unpack(event))
	event = {coYield()}
  end
end

local overwrite = {
  "term",
  "rednet"
}

for k,v in pairs(overwrite) do
  for key, value in pairs(_G[v]) do
	if key ~= "receive" then
	  _G[v][key] = function(...)
		counter = counter+1
		if counter >= 50 then
		  yield()
		  counter = 0
		end
		return value(...)
	  end
	end
  end
end

Essentially what this is doing right now is overwriting all of the functions in the term and rednet api, to where a counter counts anytime any of the functions is used and if the counter is 50, then the function will automatically yield. So far in my tests this works as expected, but I am aware this is a rather hacky solution, and as such might have a flaw in it.

Current problems I have thought of:

- One thing I have already thought of is if the event queue is at the max then an event will be lost through this method, however, very rarely does a computer reach a full event queue.
- If a event is queued while the events are being rotated, then the events will be out of order
Edited on 13 October 2015 - 04:26 AM
Lyqyd #2
Posted 13 October 2015 - 04:41 AM
Uh, yeah. Those functions should already allow yield protection to work, so why is this necessary?
valithor #3
Posted 13 October 2015 - 04:47 AM
Uh, yeah. Those functions should already allow yield protection to work, so why is this necessary?

The yield protection you are talking about, if I understand correctly, is the too long without yielding error (takes roughly 5-10 seconds to kill the program). Often times people find it funny to run code such as:

local counter = 0
while true do
  rednet.broadcast("hello")
  counter = counter+1
  if counter%1000 == 0 then
	sleep()
  end
end

That code wouldn't crash from too long without yielding, but it would bring all of the computers on the entire server to a stand still every time it ran its 1k iterations (In reality it would be more than 1k iterations). This makes the functions automatically yield every 50 times they are called, so people can not do stupid things like this and lag CC for everyone.e

edit:

My current test for this is running this code on 4 computers:


i = 1
while true do
  print(i)
  i = i+1
end

Without this modification CC would be frozen, but instead all 4 computers are running at the same time, and CC is continuing to work on all computers. I know this would normally eventually crash from too long without yielding (The overwritten functions cause it to not crash), but this was just an example of what this accomplishes.
Edited on 13 October 2015 - 03:06 AM
MKlegoman357 #4
Posted 13 October 2015 - 05:33 AM
In your yield function when pulling events don't wait for a specific event. Simply wait for any event, that way the code that is being yielded will run faster.
Lyqyd #5
Posted 13 October 2015 - 05:39 AM
Nah, it should instead start a fifteen-minute timer, and only resume when it's over. :P/>

Or the admin of the server could kick/ban the player causing problems and diminishing everyone else's enjoyment of the server. I guess I can see the use for the edge case where the admin doesn't want to do their job, but otherwise don't see the appeal. To each their own, I suppose!
valithor #6
Posted 13 October 2015 - 05:48 AM
In your yield function when pulling events don't wait for a specific event. Simply wait for any event, that way the code that is being yielded will run faster.

Events would then be discarded, which would lead to things not working as they should.

Nah, it should instead start a fifteen-minute timer, and only resume when it's over. :P/>

Or the admin of the server could kick/ban the player causing problems and diminishing everyone else's enjoyment of the server. I guess I can see the use for the edge case where the admin doesn't want to do their job, but otherwise don't see the appeal. To each their own, I suppose!

I don't know if you have any server management experience… but figuring out who uses a computer is not exactly the easiest thing in the world. Seeing who placed a computer is easy enough with the correct plugin, seeing who has accessed a computer is easy enough with the right plugin, but seeing who actually did the stuff on a computer is near impossible. With that in mind, and the fact that people often ask for help on the server, and thus 3+ people have often used a computer how do you suggest me do my job and ban the person responsible without having the chance of banning someone who is innocent?

The answer is you really can't… So no this isn't the result of me as the administrator not doing my job and just simply banning them.
クデル #7
Posted 13 October 2015 - 05:52 AM
the admin of the server could kick/ban the player causing problems and diminishing everyone else's enjoyment of the server. I guess I can see the use for the edge case where the admin doesn't want to do their job, but otherwise don't see the appeal. To each their own, I suppose!

Maybe the user did it by accident, or didn't realise their program was intensive, or maybe they made a mistake causing a loop to lag the server?
MKlegoman357 #8
Posted 13 October 2015 - 05:55 AM
I don't see a problem with that.


local function yield()
  que("yield event")
  local event = {coYield()}
  que(unpack(event))
end
Lyqyd #9
Posted 13 October 2015 - 05:58 AM
I don't know if you have any server management experience… but figuring out who uses a computer is not exactly the easiest thing in the world. Seeing who placed a computer is easy enough with the correct plugin, seeing who has accessed a computer is easy enough with the right plugin, but seeing who actually did the stuff on a computer is near impossible. With that in mind, and the fact that people often ask for help on the server, and thus 3+ people have often used a computer how do you suggest me do my job and ban the person responsible without having the chance of banning someone who is innocent?

The answer is you really can't… So no this isn't the result of me as the administrator not doing my job and just simply banning them.

You missed the jestful tone, I see. I generally only play on servers where I personally know everyone on the server with me. We know who our troublemakers are, so it's not hard to figure out what's up when something goes wrong. I would not want to deal with the messiness of attempting to admin a public server with ComputerCraft involved. Too much work for too little reward.

Maybe the user did it by accident, or didn't realise their program was intensive, or maybe they made a mistake causing a loop to lag the server?

You missed the jestful tone too. Don't worry, I'm sure you'll pick up on it next time.
valithor #10
Posted 13 October 2015 - 06:00 AM
I don't see a problem with that.


local function yield()
  que("yield event")
  local event = {coYield()}
  que(unpack(event))
end

Honestly, when I was creating this that is what I did the first time as well. However, as people on the server at the time explained to me it is a event queue not a event stack. So, when you call coroutine.yield it takes the oldest event out of the queue, and when you call os.queueEvent it is the newest. This makes it to where if there was already a event in the queue when the yield function runs the "yield event" is not always going to be the only one that is taken out.

The main reason that I am opting for the specific event is I do not want to leave the "yield event" in the queue for someones program to come across. Although a good program would be able to discard it, I would rather the program never have to.

edit:

The only other problem I see with that, is the events would be out of order if there was more than one event in the queue at the time. Things like typing would then be messed up, as the events would not be in the order they were sent in.

edit:

I don't know if you have any server management experience… but figuring out who uses a computer is not exactly the easiest thing in the world. Seeing who placed a computer is easy enough with the correct plugin, seeing who has accessed a computer is easy enough with the right plugin, but seeing who actually did the stuff on a computer is near impossible. With that in mind, and the fact that people often ask for help on the server, and thus 3+ people have often used a computer how do you suggest me do my job and ban the person responsible without having the chance of banning someone who is innocent?

The answer is you really can't… So no this isn't the result of me as the administrator not doing my job and just simply banning them.

You missed the jestful tone, I see. I generally only play on servers where I personally know everyone on the server with me. We know who our troublemakers are, so it's not hard to figure out what's up when something goes wrong. I would not want to deal with the messiness of attempting to admin a public server with ComputerCraft involved. Too much work for too little reward.

Sorry, just wasn't expecting a joking tone in the ask a pro section.
Edited on 13 October 2015 - 04:03 AM
MKlegoman357 #11
Posted 13 October 2015 - 06:06 AM
If a program won't be able to discard your custom event it would probably not work anywhere really. It shouldn't ever happen, and if it will the person will have to fix his/her problem even if there will be no yield protection. Also, there's a problem with event doubling. You might start getting the same event two or more times because of this.
valithor #12
Posted 13 October 2015 - 06:13 AM
If a program won't be able to discard your custom event it would probably not work anywhere really. It shouldn't ever happen, and if it will the person will have to fix his/her problem even if there will be no yield protection. Also, there's a problem with event doubling. You might start getting the same event two or more times because of this.

You may have not seen it as I did not want to make a double post, but: "The only other problem I see with that, is the events would be out of order if there was more than one event in the queue at the time. Things like typing would then be messed up, as the events would not be in the order they were sent in."

I do not see how a event could be doubled with this system. As it is the events will simply be rotated around in the queue until the "yield event" is found, and by then the events are in the original order. However, as I was writing this it hit me if another event was queued while they were being rotated to find the "yield event", then the events would be out of order.
MKlegoman357 #13
Posted 13 October 2015 - 06:40 AM
Event doubling will happen when the coroutine that yields is not the top level one. If a subroutine doubles the events the top level coroutine will get the same events twice. This might happen with multishell. Oh, and good point about events being out of order.
valithor #14
Posted 13 October 2015 - 06:48 AM
Event doubling will happen when the coroutine that yields is not the top level one. If a subroutine doubles the events the top level coroutine will get the same events twice. This might happen with multishell. Oh, and good point about events being out of order.

Looks like coroutines break this system. Just another thing I will have to figure out how to get around. Thanks for the help :D/>
SquidDev #15
Posted 13 October 2015 - 07:44 AM
It may not quite be what you want, but CCTweaks has a computer timeout setting to control how long it takes before a computer times out.

Another alternative would be patching LuaJLuaMachine's debug hook to always yield the computer rather than if it should terminate. I could always look into adding that if you want it?
Edited on 13 October 2015 - 05:44 AM
Bomb Bloke #16
Posted 13 October 2015 - 08:08 AM
  while tostr(event[1]) ~= "yield event" do

If it's somehow not already a string, then chances are it's not going to be a string saying "yield event", either.

	que(unpack(event))

This'll certainly break multishell and the like.

	if key ~= "receive" then

This could perhaps use an additional type(value) == "function" check. It'll fail under pre-CC 1.6 otherwise.

The real issue, the way I see things, is that you don't need to use the term/rednet APIs to perform the type of griefing you wish to deal with. A simple "for" loop with no content will chew processing time.

But, assuming your goal is to sneak something in with the hopes that your offenders won't figure out that the workaround's trivial, I suppose your code's about as good as it can get (without dropping even more hints as to what you're doing, anyway). I'd have it target os.clock()/os.time() as well, though.
Edited on 13 October 2015 - 06:08 AM