doomsday: Multicore pathfinding for Cogs.

This commit is contained in:
Sam Edwards 2014-04-19 11:51:10 -06:00
parent 1a80ead191
commit 8f29e76d12
3 changed files with 117 additions and 17 deletions

View file

@ -1,7 +1,7 @@
import random
from pandac.PandaModules import *
from direct.fsm.FSM import FSM
from InvasionPathDataAI import pathfinder
from PathPlannerPoolAI import pool
# Individual suit behaviors...
@ -45,7 +45,8 @@ class AttackBehavior(FSM):
# We can only update our walk-to if we're walking to begin with:
if self.state == 'Walk':
nav = self.brain.navigateTo(toonPos.getX(), toonPos.getY(), attackPrefer)
nav = True
self.brain.navigateTo(toonPos.getX(), toonPos.getY(), attackPrefer)
else:
nav = False
@ -58,6 +59,16 @@ class AttackBehavior(FSM):
else:
self.demand('Walk', toonPos.getX(), toonPos.getY())
def onNavFailed(self):
if self.state == 'Walk':
self.demand('Attack')
else:
# Can't get there, Captain!
self.brain.master.toonUnreachable(self.toonId)
self.brain.demand('Idle')
return
def enterAttack(self):
# Attack the Toon.
self.brain.suit.attack(self.toonId)
@ -66,11 +77,7 @@ class AttackBehavior(FSM):
# Walk state -- we try to get closer to the Toon. When we're
# close enough, we switch to 'Attack'
attackPrefer, attackMax = self.brain.getAttackRange()
if not self.brain.navigateTo(x, y, attackMax):
# Can't get there, Captain!
self.brain.master.toonUnreachable(self.toonId)
self.brain.demand('Idle')
return
self.brain.navigateTo(x, y, attackMax)
if self._walkTask:
self._walkTask.remove()
@ -157,12 +164,13 @@ class UnclumpBehavior(FSM):
moveVector.normalize()
x, y = ourPos + (moveVector * self.UNCLUMP_MOVE_DISTANCE)
if self.brain.navigateTo(x, y):
# And we're walking!
self.demand('Walking')
else:
# Hmm... Can't walk there. Let's just idle for a bit instead.
self.demand('Wait')
self.brain.navigateTo(x, y)
# And we're walking!
self.demand('Walking')
def onNavFailed(self):
# Hmm... Can't walk there. Let's just idle for a bit instead.
self.demand('Wait')
def enterWalking(self):
pass # Do nothing, we just wait for onArrive and exit the behavior.
@ -312,15 +320,18 @@ class InvasionSuitBrainAI(FSM):
# Navigation:
def navigateTo(self, x, y, closeEnough=0):
self.__waypoints = pathfinder.planPath(self.suit.getCurrentPos(),
(x, y), closeEnough)
pool.plan(self.__navCallback, self.suit.getCurrentPos(), (x, y),
closeEnough)
def __navCallback(self, result):
self.__waypoints = result
if self.__waypoints:
self.finalWaypoint = Point2(self.__waypoints[-1])
self.__walkToNextWaypoint()
return True
else:
self.finalWaypoint = None
return False
if hasattr(self.behavior, 'onNavFailed'):
self.behavior.onNavFailed()
def suitFinishedWalking(self):
# The suit finished walking. If there's another waypoint, go to it.

View file

@ -0,0 +1,78 @@
import subprocess
import os
import atexit
import thread
from panda3d.core import *
class PlanD:
def __init__(self, pool):
self.pool = pool
# I couldn't resist the name. :)
pathPath = os.path.join(os.path.dirname(__file__), 'pathd.py')
self.sp = subprocess.Popen(pathPath, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
atexit.register(self.sp.kill)
self.callback = None
self.pool.addWorker(self)
def plan(self, callback, navFrom, navTo, radius):
self.callback = callback
params = (tuple(navFrom), tuple(navTo), radius)
self.sp.stdin.write('%r\n' % (params,))
self.sp.stdin.flush()
thread.start_new_thread(self.__read, ())
def __read(self):
line = self.sp.stdout.readline()
taskMgr.doMethodLater(0.1, self.__handle, 'inject-%d' % id(self),
extraArgs=[line])
def __handle(self, line):
x = eval(line)
if self.callback:
self.callback(x)
self.callback = None
self.pool.addWorker(self)
class PlanJob:
def __init__(self, callback, navFrom, navTo, radius):
self.callback = callback
self.navFrom = navFrom
self.navTo = navTo
self.radius = radius
def assign(self, pland):
pland.plan(self.callback, self.navFrom, self.navTo, self.radius)
class PlannerPool:
def __init__(self, workerCount):
self.workers = []
self.jobs = []
for x in xrange(workerCount):
PlanD(self) # Registration is its responsibility
def addWorker(self, worker):
self.workers.append(worker)
self.__flushQueues()
def addJob(self, job):
self.jobs.append(job)
self.__flushQueues()
def __flushQueues(self):
while self.workers and self.jobs:
worker = self.workers.pop(0)
job = self.jobs.pop(0)
job.assign(worker)
def plan(self, callback, navFrom, navTo, radius):
job = PlanJob(callback, navFrom, navTo, radius)
self.addJob(job)
pool = PlannerPool(8)

11
toontown/election/pathd.py Executable file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env python2
# This is a "pathfinding daemon" for parallelism in the invasion.
import sys
from InvasionPathDataAI import pathfinder
while True:
navFrom, navTo, radius = input()
path = pathfinder.planPath(navFrom, navTo, radius)
print path
sys.stdout.flush()