ID:980796
 
(See the best response by Neo Rapes Everything.)
Code:
mob
Monster
var/MonAtk=1
var/AttackDelay=10
var/MoveDelay=10
var/tmp/Attacking=0
var/MonsterLastAttackedBy=""
var/MType=""
var/ExpG=0
var/GoldG=0
var/SearchRange=10
New()
..()
if(MonsterAI)
spawn(10)
src.AI_Search()
Slug
icon='Monsters.dmi'
icon_state="Slug"
name="Slug"
MType="Slug"
ElfBlock=1
density=1
HP = 15 //Monsters HP
MHP = 15 //Monsters MHP. Don't want them to be healed without this var! Bug abuse! Derp!
MonAtk=50
MDef=90
GoldG=1
ExpG=5
AttackDelay=30
MoveDelay=8
Monster=1
proc/AI_Search()
set background=1
sleep(2)
for(var/mob/M in ohearers(src.SearchRange))
if(M.client)
src.Attacking=M
return src.AI_Chase()
return AI_Search()
proc/AI_Chase()
set background=1
sleep(2)
for(var/mob/M in ohearers(src.SearchRange))
if(M.client&&src.Attacking==M)
sleep(5)
step_to(src,M)
sleep(src.MoveDelay)
if(get_dist(src,M)<=1)
return src.AI_Attack()
else return src.AI_Search()
return src.AI_Search()
proc/AI_Attack()
set background=1
sleep(2)
for(var/mob/M in ohearers(src.SearchRange))
if(M.client&&src.Attacking==M)
var/Dmg=round(src.MonAtk-(M.MDef/3+rand(0,10)))
if(Dmg>1)
M.HP-=Dmg
view(src)<<"[src] attacks [M] for [Dmg] damage."
else
view(src)<<"[src]'s attack misses [M]."
M.DeathCheckProc(M,src)
sleep(src.AttackDelay)
if(get_dist(src,M)<=1)
return src.AI_Attack()
else return src.AI_Chase()
return src.AI_Chase()


Problem description:

Alright, so when the AI is active and running on the server, it tends to crash after 15 ish minutes.

While the AI is disabled, the server runs fine with minimal CPU usage. While the AI is active, the average server CPU is 4 or so.

I would like to so that it's because it's looping repeatedly. I was told to use Forum Account's region library, however I'd like all AI to be able to run constantly anywhere in the world.

I could perhaps have AI disabled until a player moves into range, however then again the server would crash if a player went AFK in that zone leaving the AI active.

Is anyone able to see why the server would crash? What I could do to improve my basic and sloppy AI? Help is very much appreciated.

Let me define server crash. I mean that Dream Daemon, or seeker if ran via client, crashes and stops responding. It's not like a run time crash, and there are no run times being produced from what I saw.

Thanks again!
        proc/AI_Search()
set background=1
sleep(2)
var/client/C = locate(/client) in ohearers(SearchRange)
if(C) {Attacking=C.mob;return AI_Chase()}
return AI_Search()
proc/AI_Chase()
set background=1
sleep(2)
if(!src.Attacking){sleep(3);return AI_Search()}
else
sleep(5);step_to(src,Attacking)
sleep(MoveDelay)
if(get_dist(src,Attacking)<=1)return AI_Attack()
else return AI_Search()
return AI_Search()

proc/AI_Attack()
set background=1
sleep(2)
if(!Attacking){sleep(3);return AI_Search()}
else
var/mob/M=Attacking
var/Dmg=round(MonAtk-(M.MDef/3+rand(0,10)))
if(Dmg>1){M.HP-=Dmg;view(src)<<"[src] attacks [M] for [Dmg] damage."}
else view(src)<<"[src]'s attack misses [M]."
M.DeathCheckProc(M,src)
sleep(AttakDelay)
if(get_dist(src,M)<=1)return AI_Attack()
else return AI_Chase()
return AI_Chase()


I think the problem was the constant for() proc calls.
Other problem could be the sucessive calls to AI_Check(), AI_Chase() and AI_Attack() procs, you could want to define a lopp with while() defining everything inside it, it is your choice anyways.

As you may see, theres no need to call for() in any proc. locate() sends the closest target in ohearers() to Enemy.Attacking.
You would want to make the AI only activate when there are clients, so why not put an initialiser proc, AI_Start() that is activated when a mob steps in Enemy's ohearers() list. There are plenty options I hope you understand what I am trying to tell you.
Doing it that way made everything a lot worse and way more unstable.

The AI Search proc was very CPU intensive, and the server CPU kept raising and raising resulting in a crash within minutes. Enemies were barely responsive. Attacking from a distance and not actually causing damage.

I might tweak mine to use while rather than recalling procs, however I'm curious if that will make an efficient difference?
Best response
proc/Enemy_AI()
set background=1
spawn while(src)
var/client/C=locate(/client) in ohearers(searchRange);src.Attacking=C.mob
if(Attacking)
if(get_dist(src,Attacking)>1){sleep(5);step_to(src,Attacking)}
else
src.dir=get_dir(src,Attacking)
var/Dmg=round(src.MonAtk-(Attackig.MDef/3+rand(0,10)))
if(Dmg>1)
M.HP-=Dmg
view(src)<<"[src] attacks [M] for [Dmg] damage."
else view(src)<<"[src]'s attack misses [M]."
Attacking.DeathCheckProc(Attacking,src)
sleep(src.AttackDelay)
sleep(10)//A whole second of sleep. About 2 seconds of delay between each attack.

I should have done the while() before instead of editing your code. I think this will do fine, as it is only 1 Proc that ends at the very beginning. (But also keeps running a while loop while the NPC exists)
There are no calls to secondary procs other than DeathCheckProc()
I hope this helps you.
Yes, someone else also confirmed that using while() will fix my crash issue and that calling procs over and over like that will crash a server. I didn't know that until now, but I guess it makes sense.

Thanks again. =P
No problem man, my bad for the first "attempt" lmao.
In response to Neo Rapes Everything
Does that actually work? I'm pretty sure ohearers() returns a list of mobs; you've put in an extra step.
Either if it returns a list of mobs, a client is active on one of those mobs retrieved by it, -I guess-. I think it takes less time to be done than looping with for() all mobs, and then loop them again, looking for an active client.

I just assumed locate(/cliet) in ohearers() will just look for clients, as stated with locate().

Correct me if I am mistaken.
In response to Neo Rapes Everything
The reference clearly states that ohearers() returns a list of mobs that can hear the center (excluding the center). It is not clear whether it has its own check to only return mobs with clients.

But the reference never said anything about returning any /client objects. Using "locate(/client) in ListOfMobs" will never give you anything.
I will just test it myself because am getting a bit confused in with this. Hahaha.

Edit: My bad. It doesnt, instead just replace it with /mob/player, or the type you have defined (if one), which makes sense, just to difference it from other mobs.
I'm going to assume that's why the Enemy AI wasn't responding when using Neo's method to search for a client.

Will using locate() as Neo did be better than using a for loop and grabbing the first player found?
reokace the locate() line with a for(). Will do fine, as long as it is inside a while()
Also there shouldn't be any need for set background = 1. That just seems unnecessary and makes debugging more difficult.
Would be necessary if while() where called without a spawn, because the proc would never end as long as the mob exists.
That makes a proc more on the scheme.