ID:1363076
 
(See the best response by Ter13.)
Code:
mob
var
BossN
tmp/Boss=0
EnemyNPC
New()
. = ..()
spawn()
move()
proc
move()
while(src)
var/player_found = 0
for(var/mob/P in oview(6))
if(P.client&&!src.Frozen)
player_found = 1
step_towards(src,P)
if(!player_found)
if(src.Boss||src.QuestNPC)
src.SpAtt()
sleep(10)
sleep(5)

SpAtt()
for(var/mob/P in oview(7))
if(P.client)
src.target=P
if(src.rank=="Mega"||src.rank=="Mega Lv-2"||src.rank=="Mega Lv-3")
random=rand(1,4)
if(random==1)
src.SpAttack1P()
if(random==2||random==3)
src.SpAttack2P()
if(src.rank!="Mega"||src.rank!="Mega Lv-2"||src.rank!="Mega Lv-3")
src.SpAttack1P()

Bump(mob/M)
if(ismob(M))
if(M.client)
if(src.canattack)
AttackP(M)


Problem description:
This code is using a lot of CPU, I don't know why though. Dream Daemon already crashed a few times for unknown reasons. DD says it's running in Task Manager, but when I try to open it from my taskbar, it doesn't work. I check the Task Manager for the CPU usage of DD itself and it's usually around 40-45% then, with a memory usage of 200+- MB and going up. I ran a profile check and it gave me those results:


Is there anything so resource intensive in that part of code? If so, how would I make it more efficient?

Thanks in advance!
Best response
It may actually not be taking up a lot of CPU time. You are Sleep()ing in your proc, so that's going to act like the proc is taking a long time to complete.

One thing I'm noticing, is that you aren't preserving any of the AI state at all. You are hunting for new targets every frame, even after you've found a target and started hunting it.

Preserve the state between calls of move(), also, you might want to disable any mobs that aren't within a certain range of players using some sort of a system that activates hostile enemies when a player enters a certain range of said enemy. This will cut down on target-seeking time a lot.

I really recommend you don't use an unterminated loop like you are, though. I'd recommend using spawn() to call the AI behavior from within the AI loop while the mob is active. The fact that you are sleep()ing is what's causing your Real Time CPU use to be so insanely high.
There are a lot of problems in your replacement, there Kozuma. You are searching targets every operation. In some cases, twice due to the order of the player search loops.
This is just the tip of the iceberg of what can be fixed, Raimo.

mob
var
BossN
tmp/Boss=0
EnemyNPC
var
mob/player_found = null //preserve the state
New()
. = ..()
spawn()
move()
proc
move()
//no need for a loop: make sure to remove indentation
if(!player_found)
if(src.Boss||src.QuestNPC)
src.SpAtt()
spawn(10)
move()
return //stop the regular loop
else
for(var/mob/P in ohearers(7,src))
if(P.client && !src.Frozen)
player_found = P
break //stop checking other mobs in view after finding one.
if(player_found) //instead of else, put another if because target could have changed.
step_towards(src,player_found)
if(get_dist(src,player_found)>7)
player_found = null
spawn(5)
move()

SpAtt()
for(var/mob/P in ohearers(7,src))
if(P.client)
src.target=P
switch(rank)
if("Mega","Mega Lv-2","Mega Lv-3")
random=rand(1,4)
if(random==1)
src.SpAttack1P()

if(random==2||random==3)
src.SpAttack2P()
else
src.SpAttack1P()
break //don't target everyone in range. just one.

Bump(mob/M)
if(ismob(M))
if(M.client && src.canattack)
src.AttackP(M)


@kozuma, then why bother moving a single variable declaration? Why not mention that his methodology is flawed and a large portion of the reason that his CPU time is insanely high?
In response to Ter13
Ter13 wrote:
It may actually not be taking up a lot of CPU time. You are Sleep()ing in your proc, so that's going to act like the proc is taking a long time to complete.

One thing I'm noticing, is that you aren't preserving any of the AI state at all. You are hunting for new targets every frame, even after you've found a target and started hunting it.

Preserve the state between calls of move(), also, you might want to disable any mobs that aren't within a certain range of players using some sort of a system that activates hostile enemies when a player enters a certain range of said enemy. This will cut down on target-seeking time a lot.

I really recommend you don't use an unterminated loop like you are, though. I'd recommend using spawn() to call the AI behavior from within the AI loop while the mob is active. The fact that you are sleep()ing is what's causing your Real Time CPU use to be so insanely high.

Thank you, I analyzed what you said and the piece of code you have posted. Thank you for the learnful experience. I will test this and come back if the high CPU usage still exists.
Sorry for the double post. What I noticed now is that there are a huge amount of calls.

Yep, that's expected, but notice how much less CPU time they are using?

The reason the calls are so much higher is because we're terminating the proc when it's done working, and calling it again.

How many enemy AI do you have working at a time, and how long did this test run for? You should be getting 1 call every 7 ticks on average per enemy.
I have a lot. I can't say a number. Currently, the world is running for 9 minutes and 30 seconds now.

Okay, 9 minutes, 30 seconds is 570 seconds. Let's multiply that by 10. If your world.FPS is 10, your mob will be running 814 of these procs by 9 mins 30 seconds, give or take 50% (400 - 1200). If your world.fps is 30, it's going to be three times that.

So... Let's figure out how many enemies you have in your world using these numbers:

3195367 calls at 10 fps = ~3925 mobs active at a time.
at 30 fps that's ~1300 mobs active at a time.

This is too many mobs to have active at once reasonably.

Albeit, my AI system that I wrote a few months ago was running 40,000 NPC AI active at a time using a singleton-driven AI state machine, and was chugging along at around 75% CPU usage on a Core i7 920 (3.6ghz). Though, my design was completely different than yours, and a large part of your problem stems from the fact that you have made a very not-scalable design decision to have them search their view for mobs even if nobody is around, which is going to be fairly slow. Try thinking of ways to slim down how many mobs are active at once by disabling ones on different parts of the map with zero players on them.
Oh thank you, I'll look into that. Another problem came up, normal enemies are able to move and fight you, but the bosses won't. They just stand still and don't do anything else. This problem didn't exist with the old code. This is what I have at the moment:
mob
var
BossN
tmp/Boss=0
EnemyNPC
var
mob/player_found = null
New()
. = ..()
spawn()
move()
proc
move()
if(!player_found)
if(src.Boss||src.QuestNPC)
src.SpAtt()
spawn(10)
move()
return //stop the regular loop
else
for(var/mob/P in ohearers(7,src))
if(P.client && !src.Frozen)
player_found = P
break
if(player_found)
step_towards(src,player_found)
if(get_dist(src,player_found)>7)
player_found = null
spawn(5)
move()

SpAtt()
for(var/mob/P in ohearers(7,src))
if(P.client)
src.target=P
switch(src.rank)
if("Mega","Mega Lv-2","Mega Lv-3")
random=rand(1,4)
if(random==1)
src.SpAttack1P()

if(random==2||random==3)
src.SpAttack2P()
else
src.SpAttack1P()
break //don't target everyone in range. just one.

Bump(mob/M)
if(ismob(M))
if(M.client && src.canattack)
src.AttackP(M)

I don't see anything wrong with it and I also compared it to my old code, still can't see the problem.
        proc
move()
if(!player_found)
for(var/mob/P in ohearers(7,src))
if(P.client && !src.Frozen)
player_found = P
break
if(src.Boss||src.QuestNPC)
src.SpAtt()
spawn(10)
move()
return //stop the regular loop
if(player_found)
step_towards(src,player_found)
if(get_dist(src,player_found)>7)
player_found = null
spawn(5)
move()




There you go, the else stopped bosses from ever getting a chance to find a player to let them move and attack.
Thanks Baka, that helped out.
Baka, you just introduced two loops per iteration on bosses now, due to poor design on the attack loop. Notice special attack tries to find its own target, while regular mobs also try to find their own target?

This entire AI system is really questionably designed. No offense, Raimo, but you need to write down how it should work in plan english, to explain what you are trying to do before writing the code. It looks like you were really confused when you wrote the boss logic. Once you write down what you are trying to do, then look at what you have, you'll understand where your logic is flawed.
In response to Ter13
Ter13 wrote:
Baka, you just introduced two loops per iteration on bosses now, due to poor design on the attack loop. Notice special attack tries to find its own target, while regular mobs also try to find their own target?

This entire AI system is really questionably designed. No offense, Raimo, but you need to write down how it should work in plan english, to explain what you are trying to do before writing the code. It looks like you were really confused when you wrote the boss logic. Once you write down what you are trying to do, then look at what you have, you'll understand where your logic is flawed.

I'll take that in consideration and most likely do it as well. Thanks for your help.
I have currently moved to Undefeated Saiyans his hosting service and he reported to me that the game is using 80-90% of his CPU. Now I'm wondering why that is, can u tell me it's because of the enemy AI?
In response to Raimo
Raimo wrote:
I have currently moved to Undefeated Saiyans his hosting service and he reported to me that the game is using 80-90% of his CPU. Now I'm wondering why that is, can u tell me it's because of the enemy AI?

Probably because he is hosting 10 - 20 games from his house laptop....
So, the AI is still using a lot of CPU. I've taken a look at FA's Enemy AI library but I can't really see how I could mix my AI with his library. Can anyone help me out here? My current AI is:
mob
var
BossN
tmp/Boss=0
EnemyNPC
var
mob/player_found = null
New()
. = ..()
spawn()
move()
proc
move()
if(!player_found)
for(var/mob/P in ohearers(7,src))
if(P.client && !src.Frozen)
player_found = P
break
if(src.Boss||src.QuestNPC)
src.SpAtt()
spawn(10)
move()
return //stop the regular loop
if(player_found)
step_towards(src,player_found)
if(get_dist(src,player_found)>7)
player_found = null
spawn(5)
move()

SpAtt()
for(var/mob/P in ohearers(7,src))
if(P.client)
src.target=P
switch(src.rank)
if("Mega","Mega Lv-2","Mega Lv-3")
random=rand(1,4)
if(random==1)
src.SpAttack1P()

if(random==2||random==3)
src.SpAttack2P()
else
src.SpAttack1P()
break //don't target everyone in range. just one.

Bump(mob/M)
if(ismob(M))
if(M.client && src.canattack)
src.AttackP(M)
In response to A.T.H.K
A.T.H.K wrote:
Raimo wrote:
I have currently moved to Undefeated Saiyans his hosting service and he reported to me that the game is using 80-90% of his CPU. Now I'm wondering why that is, can u tell me it's because of the enemy AI?

Probably because he is hosting 10 - 20 games from his house laptop....

^

I doubt he's using Linux, so it may be even slower.
I'm sorry, but I don't recommend his services.
In response to Eternal_Memories
Eternal_Memories wrote:
A.T.H.K wrote:
Raimo wrote:
I have currently moved to Undefeated Saiyans his hosting service and he reported to me that the game is using 80-90% of his CPU. Now I'm wondering why that is, can u tell me it's because of the enemy AI?

Probably because he is hosting 10 - 20 games from his house laptop....

^

I doubt he's using Linux, so it may be even slower.
I'm sorry, but I don't recommend his services.

Okay, I get that idea now, but could we stay on topic?
In response to Raimo
Raimo wrote:
So, the AI is still using a lot of CPU. I've taken a look at FA's Enemy AI library but I can't really see how I could mix my AI with his library. Can anyone help me out here?

Well, I'd say that your first decision needs to be whether or not all of your enemy mobs need to be actively running a loop at every moment?

As it currently stands, every single enemy in your game will be constantly spawning calls to move(), and running a check on ohearers(), even when there are no players within proverbial miles!

There's probably no need for that.

In some game designs, a constantly active AI is necessary. This is the case where the AI is designed to be doing something in their "spare time" (are they designed to fight each other when no one's around? do they need to forage for food? is there some goal that they need to reach? etc.), but in the case of some randomly generated roaming monsters that the player will stumble across in his travels, and whose sole job is to attack players in range, then they likely do not need to be constantly active.

AI enemies such as these are better set up to be triggered by player actions, rather than running a constant search for the player. Have the player "broadcast" his presence whenever he moves, and only activate the enemy mobs when he comes near them (likely a larger range than his view; so they'll "fire up" out of his sight, and to him, it'll look like they've always been moving around. He'll never know that they were just standing there until he came along.

Alternatively, you could even set it up so that enemy mobs don't even spawn until a player moves into that area of the map. No need to even have them created and wasting space until they're needed!

This will greatly cut down your CPU use.

It's not so much that your procedure itself is CPU intensive (if you look at the average time per call, it's really short), it's that you're just calling it way too many times (over 1000 enemies, calling that proc over and over and over, etc.)
Page: 1 2