ToME: the Tales of Maj'Eyal

Everything about ToME
It is currently Mon May 29, 2017 9:24 am

All times are UTC




Post new topic Reply to topic  [ 2 posts ] 
Author Message
PostPosted: Fri Mar 08, 2013 10:57 pm 
Offline
Uruivellas

Joined: Thu Nov 18, 2010 6:42 pm
Posts: 741
I've been working on a fairly complex event that includes a number of xorns and umber-hulks burrowing through rock and walls to engage the player. The walls are designed to block actors with the can_pass.pass_wall = xx attribute but to crumble in the process. If the can_pass attribute of the walls is higher than that of the actors, there is a substantial slowdown in the AI.

Digging (oops!) into this I think the source of the problem lies with the way Path Strings (a function in text form that generates a table of movement attributes) and level.map._fovcache are used to determine viable paths. The purpose of the fovcache code is to precompute the ability of all terrain on the map to block the movement of actors with a specified path string, and then to retrieve that information via a simple lookup function. _M:addPathString(ps) in engine.Map.lua creates and indexes each cache with a call to core.fov.newCache) as needed in order to speed up actor pathfinding.

From tome/class/Grid.lua:
Code:
function _M:block_move(x, y, e, act, couldpass)
   -- Path strings
   if not e then e = {}
   elseif type(e) == "string" then
      e = loadstring(e)()
   end

   -- Open doors
   if self.door_opened and e.open_door and act then
      if self.door_player_check then
         if e.player then
            Dialog:yesnoPopup(self.name, self.door_player_check, function(ret)
               if ret then
                  game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list[self.door_opened])
                  game:playSoundNear({x=x,y=y}, {"ambient/door_creaks/creak_%d",1,4})

                  if game.level.map.attrs(x, y, "vault_id") and e.openVault then e:openVault(game.level.map.attrs(x, y, "vault_id")) end
               end
            end, "Open", "Leave")
         end
      elseif self.door_player_stop then
         if e.player then
            Dialog:simplePopup(self.name, self.door_player_stop)
         end
      else
         game.level.map(x, y, engine.Map.TERRAIN, game.zone.grid_list[self.door_opened])
         game:playSoundNear({x=x,y=y}, {"ambient/door_creaks/creak_%d",1,4})

         if game.level.map.attrs(x, y, "vault_id") and e.openVault then e:openVault(game.level.map.attrs(x, y, "vault_id")) end
      end
      return true
   elseif self.door_opened and not couldpass then
      return true
   elseif self.door_opened and couldpass and not e.open_door then
      return true
   end

   -- Pass walls
   if self.can_pass and e.can_pass then
      for what, check in pairs(e.can_pass) do
         if self.can_pass[what] and self.can_pass[what] <= check then return false end
      end
   end

   -- Huge hack, if we are an actor without position this means we are not yet put on the map
   -- If so make sure we can only go where we can breathe
   if e.__is_actor and not e.x and not e:attr("no_breath") then
      local air_level, air_condition = self:check("air_level"), self:check("air_condition")
      if air_level and (not air_condition or not e.can_breath[air_condition] or e.can_breath[air_condition] <= 0) then
         return true
      end
   end

   if e and act and self.does_block_move and e.player and game.level.map.attrs(x, y, "on_block_change") then
      local ng = game.zone:makeEntityByName(game.level, "terrain", game.level.map.attrs(x, y, "on_block_change"))
      if ng then
         game.zone:addEntity(game.level, ng, "terrain", x, y)
         game.nicer_tiles:updateAround(game.level, x, y)
         if game.level.map.attrs(x, y, "on_block_change_msg") then game.logSeen({x=x, y=y}, "%s", game.level.map.attrs(x, y, "on_block_change_msg")) end
         game.level.map.attrs(x, y, "on_block_change", false)
         game.level.map.attrs(x, y, "on_block_change_msg", false)
      end
   end

   return self.does_block_move
end
where e is the actor or other criteria to determine whether the terrain (self) can be crossed. I haven't tracked down all of the issues, but a (substantial) part of the slowdown seems be caused when path strings are passed to this function invoking a loadstring call at the first elseif statement. This statement requires the string to be compiled, and the resulting function executed to return a table of values that is referenced in the rest of the function. This section of code is callled frequently during regular actor movement. Here is an example of a series of stack traces taken during routine movement of 32 actors on a 92Wx61H level (out of combat) where this section of code was called more than 150 times in one turn:
Attachment:
mod.classGridstacktrace.txt [97.85 KiB]
Downloaded 46 times

(The Infinite500 code called here is the same as that of the non-modified game - SVN 6523.) Most of the calls are from updateMap as a result of normal movement of an actor from one tile to the next or from pathfinding. Some of the path strings (like "{can_pass={pass_wall=0,}}", which just means the actor has no special movement capabilities) appear to be incorrect or superfluous. I think performance could be improved by making more use of the fovpathcaches for routine movement, short-circuiting "do-nothing" path strings, and only recompiling the path strings as a fallback when other methods fail. It might also be advantageous to add a link to the associated pathcache code directly into each actor instance, reducing call stack depth appreciably.

In addition, path strings are only generated during level changes (_M:changeLevelReal in mod/class/Game.lua) and not for new actors that are added to a level later (like summons). This could be added to the add entity code.

Has anyone had similar experiences with this or have other suggestions on how to improve things here?

_________________
Author of the Infinite 500 and PlenumTooltip addons, and the joys of Scaling in ToME.


Top
 Profile  
 
PostPosted: Sun Mar 10, 2013 3:16 pm 
Offline
Master of Eyal

Joined: Wed Jul 24, 2002 9:26 pm
Posts: 10114
Location: Angolwen
fixed

_________________
[tome] joylove: You can't just release an expansion like one would release a Kraken XD
--
[tome] phantomfrettchen: your ability not to tease anyone is simply stunning ;)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group