summaryrefslogblamecommitdiff
path: root/src/map/map.cpp
blob: 25933a988a9b7abb12aa51097b6d21350e88cdeb (plain) (tree)
1
                  




















                                                                           
 
                     
                     
 
                  
                   
 
                  
                  
 


                               

                          
                                 


                                 
                                 
 


                                 
                             
                            
                         
                         

                          
 

                            
                                     
 
                                  
                                    
                             
 

                           

                        

                    
                    
                     
                                                            
                         
                       
                  
                  
                        
                    
                 
                             
                    
                      
                    
 

                        


              
                                          
 
                                     
 
      
                                                   


                  
                  


               
      
                                          
 


              
                                                                   
      
                                         
 
                                                     
                           
 
                                     
 
                                                                  
 

                                                                                                                  

                                                
                                                       

 



                                                          
 
                                            

                                       

                                            
                        

              


                                            
                                                           

                                            
                      

                 

 


                        
                                             
 
                                                     

                             
                     
        
                                 
 
 
                            
 
                      

 
                             
 
                                 

                                 

                                                  
                           
     

 


                                                                 

                          

                                            

                                       
  
                                                       

                                            
                                         
 
                    
 
                    

                                    
                                                                      


                 
                              

                     
                                     
                                                   

                 
                               
     
                                                                              
                                                     

                                      
                                                                     


        
                                                                           
                                                     

                                      
                                                                  
                                  
                       


             


                                            

                                                

                                            
                                         
 
                    
 
                                           
                     
     
                        
         
                                                                               
                                        
                                                                          



                 
                              
                          
 

                                           
                                                      
     
                                                                       
                                   
         
                                                                                                       


            
                                                                                                    



         
                                           
     

                          

             


                                            
                                                                    

                                            
                                                         
 
               
                                      
                  
 
                                                       



                        

                                      
     
                                                                    

                    

                                         
     
                                           




                    

 
                                            


                                                

                                            
                                                                      
                              
                                       
                
 
                                              
 




                                               



               



                       
                                           
                                                                   
         
                                                                       
             

                                                                       
                 
                                                               
                                 

                                                            
                                              


                 
                                           
                                                                   
         
                                                                       
             

                                                                          
                 

                                                            
                                              



                 
                      
 
                                           
                        
                     


                                            


                                                         
  
                                                                          

                                            
                                                                          
                              

                                       
                
 
                                              
                                                                  
                                                         

                           

                                                                       


                       
                                 
                
                                 



                         
                                 
                
                                 




                   



                           
                                                                   
         
                                                                       
             

                                                                       
                 
                                                               
                                 

                                                            
                                              
                 

                                                     
                 
                                                               
                                 

                                                            
                                              





                 
                              




                   



                           
                                                                   
         
                                                                       
             

                                                                       
                 
                                                               
                                 

                                                                 
                                 



                                                          
                                              
                 

                                                     
                 
                                                               
                                 

                                                                  
                                 



                                                          
                                              





                 
                      
 
                                           
                        
                     




                                                                                   
  
                                                                      
                              
                     
                
 
                                              

                            
 
                                           
     

                                                               
         
                                                       
                         
                                               
                                      


         
                                           
     

                                                                  
         
                                               
                                      


         
                      
 
                                           
                        
                     


                                            

                                                               
  
                                                    

                                            
                                              
 
              
            
     
                                              
                         
     



                                                                                         
                  
                           

                                    
                                              
                         

                             
                          
                     
             


                                            
                        
                                                    

                                            
                                             
 
                                

                           
 
                                           
     
                                                                    
                     
                                            
                
     
 
                                    
                                          
                                
 
                                  
                                  


                                            


                                                   
  
                                                

                                            
                                       
 
                                
                                                 
 
                       
               
 
                                  
                                                                                                                                    
                                     
 
                                 


                                            
                                     


                                            
                                                                      
                
 
                                              
                                                                      
     
                              
                     
         
                                                                     
                         
                                                

         
 
                      
 
                                           



                                                                      
                                       
                     
     


                                            
                           
  

                                                          
  

                                        

                                            
                                                                 
 
                                
                                                 
                                             
                                                    
             
                                   
                                        
                                          

 
                                                                     
                                                       
 
                                            
     

                         
                                                                      
                                                                                  
     
                          
 
 

                                                                  
                                                                                                    


                                                                               


                                            
                                                       
  
                                        

                                            
                                                         
                                            
                                                                         
                                           
 
                                             
 
                                      

                                                         
                         
 
                 
                              
                                              


                            
                                    
                                     
                                     
                                      
                                    
                                     
 
                                        
                      
     
                        
                         

     
                            

                  
                                               


                                                       
                                                


                                                        
                                               
                                                       
 
                                  
                                     






                                                                   
                                                   
                                                               
                               
 
                        
                              
 
                        

 
                                                     
                                            


                                             
 
                                                                             
                                   
 
     


                                                                                                                

     
                                                           

                                                                          

 
                                            
                                                     

                                            
                                                  
 
                                                     
 
                   
                  


                                            
                      

                                            
                                         
 
                    
 
                             


                                            
                         

                                            
                                         
 
                    
 
                                  


                                            
                        

                                            
                                                 
 
                    
 
                                         


                                            
                              
  
                                                          

                                            
                                            
 
                    
 
                                                        
                              
 
                                                                
                                                            
 
                                                                                     
 
                                                                      
 

                                                           
 
                                                                             


                              
 
                         
 
                                             
 


                                  
                                                         
 
                                                                          
                                
                       
                                    
                                 
 
                             
 
                     
 
                                  

                                              


                                            
                                               

                                            
                                                
 

                                                                   






                                                                                                                             
                                
 
                                  
                                      
                                                    
                       
  
                               
     

                                    
                     
                            
         
                                                                                          
                                
                                                      

         
 
                   


                                            
                                  

                                            
                                   
 
                                                            
 








                                       

 


                                                           
      
                                                    
 
     

                                    
                           
                                                                                     
                               
                                                 
     
 
                   

 
      
                                                             
 
                                              
     
                                                                               


                     
 
                   

 
      
                                                              
 
                                    
     
                                                                               


                     
 
                   

 
                                                      
 
                                      

 
                                                                             
 
                                                                    

 
                                                     
 
                                                  

 
                                                                             
 
                                                                     

 


                                            
                                             

                                            
                                                     
 
                               
     

                                    
                     
                                                                                         
                                       
         
             
                                                   
                                                             


             
                   


                                            

                                                

                                            
                                          
 
                                      

                               
        
                           
 
              


                                            
                                                  

                                            
                                                            
 
          
                                                           
                                 



                                    
                                                                  

                  
                        
     
                     

     
                    
 
                   
              
                             



             
      
                        
 
              
 
                              
     



                                                                   
         
                                     
             



                                                                 
                 

                                                        
                 
                                    



                    
                                        


                                            
                                 

                                            
                                                           
 









                                                    


                                            
                                  

                                            
                                                                                 
 












                                                         

 




                                                             

                       









                                                     


                                            
                           

                                            
                                                        
 
                     
               
 
                             
 

                       
                           
     
                     

                                
     
                      
                            
                         
                        
                         

                                
     
                      
                             
                         
                         
                         

                                
     
                      
                            
                         
                        
                         

        
     
                      
                             
                         
                         
                         

               

 
         
                                            
                               

                                            
                                                        
 
                                                           
                                   
                                 


                                            
                                

                                            
                                                                
 
                                                   
               
                              


                                            
                                         

                                            
                                                        
 
                                                    
                      
     
                             
         
                        
             













                                                                  

             
                           
         



                                                  

                              
                                                      

         
                  
             

 
                                            
                            

                                            
      
                                                      
 
                        



                                                 
 

                                              
                                                           
                                
                        
                   
 
                          
                                       
 

                   
                                  
                              
                                     
                                                            
                                            
 


                                                    
 
                


                                            
                                       

                                            
      
                         
 

                                                                            

                         
 
                             
     
         
             

                                                                           
                 


                                                                  
                                   
                 

                          
             
         

     
                                                        

                     
                                                                               
                              
                     
     

                


                                            
                                 

                                            
                                
 
                             
     

                        
     
 
                                           



                                                          
                                         


                                            
                                 

                                            
                                
 
                           
     

                        

     
                                  

 
                                                   
 
      
                                            
      
                         

                       
 

                            
 

                    
                                                                                        




                             
                   

                                                
 
                            
 




                                 
                      
     

 
      
                                  
 
                              
 
                                     
                         

                               



                                                            
                                         
     

 
      
                                      
 
                      
 
                                           
                               
 
                                                                    
 
                                

 
                          
 

                     
 

                                                  
 
                               
     
                            
                             

     
                                           

 
      
                                                                 
 
                                
 
     
                                  
         
                                                               
                                  
         
                                       
         
                                                               
                                  
         
                                        
         
                                               
                            
                             
             
                                   


                                                           

                                                           
                                                                      
                                       
             
                
             
                                                          
                             
             
                              
         
                                          
         
                                                 
         
                                       
         
                                               
                            
                             
             





                                                           
                                                                
                                       
             
                
             
                                                          
                             
             
                             
         
                                         
         
                                                
         
                                    
         
                                                
                             
         
                                       
         
                                                
                             
         
                                    
         
                                    
         
                                       
         
                                    
         
                                              
         
                                                                        

                                                          
         
                                         
         
                               
         
                                           
         
                                 
         
                                       
         
                                        
         
                                         
         
                                     
         
                                       
         




                                                         

         
 
                

 
      
                                         
 
                    
 
                        
     
                    
                                                              
                  
                     
                                     
                  
                     
                                     
                  
                      
                                          
                  
                       
                                                         
                  
     


                                            
                        

                                            
                    
 
                             
     

                             
                                                                           





                                       

     

                                       
 
                    
 

                    


                       

                        

 
                                  
 
                                    

 
      
                                                                    
 






















                                                                


                 



                                                        
                                
 
                                     
                                    
 
                                   
                
     

                                         
         
                                    
             
                                                                                                            
                               

                        
                                            
             
                                                           

                        
                                                           
             
                          
                 
                                                     

                            
                                                    




                                                 
                                                                     



                                
         
                                     
                                                          
         

     
                           
                                                                       
 

                                
 


                    
                     

                             

                    

                                                                

                                   
                                                                                          
 
                                                                                                                      
                            



             
                                                             
 
                                            



                 
                        
     
                     
                                          
                       
                                                        
                  
     
 
             
 
                   
#include "map.hpp"
//    map.cpp - Core of the map server.
//
//    Copyright © ????-2004 Athena Dev Teams
//    Copyright © 2004-2011 The Mana World Development Team
//    Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//    Copyright © 2013 Freeyorp
//
//    This file is part of The Mana World (Athena server)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <sys/time.h>
#include <sys/wait.h>

#include <netdb.h>
#include <unistd.h>

#include <cassert>
#include <cstdlib>

#include "../compat/nullpo.hpp"
#include "../compat/fun.hpp"

#include "../ints/udl.hpp"

#include "../strings/astring.hpp"
#include "../strings/zstring.hpp"
#include "../strings/xstring.hpp"
#include "../strings/vstring.hpp"
#include "../strings/literal.hpp"

#include "../generic/db.hpp"
#include "../generic/random2.hpp"

#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/read.hpp"
#include "../io/span.hpp"
#include "../io/tty.hpp"
#include "../io/write.hpp"

#include "../net/socket.hpp"
#include "../net/timer.hpp"
#include "../net/timestamp-utils.hpp"

#include "../mmo/config_parse.hpp"
#include "../mmo/cxxstdio_enums.hpp"
#include "../mmo/version.hpp"

#include "../high/core.hpp"

#include "atcommand.hpp"
#include "battle.hpp"
#include "chrif.hpp"
#include "clif.hpp"
#include "grfio.hpp"
#include "itemdb.hpp"
#include "magic-interpreter.hpp" // for is_spell inline body
#include "magic-stmt.hpp"
#include "magic-v2.hpp"
#include "mob.hpp"
#include "npc.hpp"
#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
#include "script-startup.hpp"
#include "skill.hpp"
#include "storage.hpp"
#include "trade.hpp"

#include "../poison.hpp"


namespace tmwa
{
DMap<BlockId, dumb_ptr<block_list>> id_db;

UPMap<MapName, map_abstract> maps_db;

static
DMap<CharName, dumb_ptr<map_session_data>> nick_db;

struct charid2nick
{
    CharName nick;
    int req_id;
};

static
Map<CharId, struct charid2nick> charid_db;

static
int users = 0;
static
Array<dumb_ptr<block_list>, unwrap<BlockId>(MAX_FLOORITEM)> object;
static
BlockId first_free_object_id = BlockId();

interval_t autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
int save_settings = 0xFFFF;

AString motd_txt = "conf/motd.txt"_s;

const CharName WISP_SERVER_NAME = stringish<CharName>("Server"_s);

map_local undefined_gat = [](){ map_local rv {}; rv.name_ = stringish<MapName>("undefined.gat"_s); return rv; }();

void SessionDeleter::operator()(SessionData *sd)
{
    really_delete1 static_cast<map_session_data *>(sd);
}

VString<49> convert_for_printf(NpcEvent ev)
{
    return STRNPRINTF(50, "%s::%s"_fmt, ev.npc, ev.label);
}

/*==========================================
 * 全map鯖総計での接続数設定
 * (char鯖から送られてくる)
 *------------------------------------------
 */
void map_setusers(int n)
{
    users = n;
}

/*==========================================
 * 全map鯖総計での接続数取得 (/wへの応答用)
 *------------------------------------------
 */
int map_getusers(void)
{
    return users;
}

static
int block_free_lock = 0;
static
std::vector<dumb_ptr<block_list>> block_free;

void MapBlockLock::freeblock(dumb_ptr<block_list> bl)
{
    if (block_free_lock == 0)
        bl.delete_();
    else
        block_free.push_back(bl);
}

MapBlockLock::MapBlockLock()
{
    ++block_free_lock;
}

MapBlockLock::~MapBlockLock()
{
    assert (block_free_lock > 0);
    if ((--block_free_lock) == 0)
    {
        for (dumb_ptr<block_list> bl : block_free)
            bl.delete_();
        block_free.clear();
    }
}

/// This is a dummy entry that is shared by all the linked lists,
/// so that any entry can unlink itself without worrying about
/// whether it was the the head of the list.
static
struct block_list bl_head;

/*==========================================
 * map[]のblock_listに追加
 * mobは数が多いので別リスト
 *
 * 既にlink済みかの確認が無い。危険かも
 *------------------------------------------
 */
int map_addblock(dumb_ptr<block_list> bl)
{
    nullpo_retz(bl);

    if (bl->bl_prev)
    {
        if (battle_config.error_log)
            PRINTF("map_addblock error : bl->bl_prev!=nullptr\n"_fmt);
        return 0;
    }

    P<map_local> m = bl->bl_m;
    int x = bl->bl_x;
    int y = bl->bl_y;
    if (m == borrow(undefined_gat) ||
        x < 0 || x >= m->xs || y < 0 || y >= m->ys)
        return 1;

    if (bl->bl_type == BL::MOB)
    {
        bl->bl_next = m->blocks.ref(x / BLOCK_SIZE, y / BLOCK_SIZE).mobs_only;
        bl->bl_prev = dumb_ptr<block_list>(&bl_head);
        if (bl->bl_next)
            bl->bl_next->bl_prev = bl;
        m->blocks.ref(x / BLOCK_SIZE, y / BLOCK_SIZE).mobs_only = bl;
    }
    else
    {
        bl->bl_next = m->blocks.ref(x / BLOCK_SIZE, y / BLOCK_SIZE).normal;
        bl->bl_prev = dumb_ptr<block_list>(&bl_head);
        if (bl->bl_next)
            bl->bl_next->bl_prev = bl;
        m->blocks.ref(x / BLOCK_SIZE, y / BLOCK_SIZE).normal = bl;
        if (bl->bl_type == BL::PC)
            m->users++;
    }

    return 0;
}

/*==========================================
 * map[]のblock_listから外す
 * prevがNULLの場合listに繋がってない
 *------------------------------------------
 */
int map_delblock(dumb_ptr<block_list> bl)
{
    nullpo_retz(bl);

    // 既にblocklistから抜けている
    if (!bl->bl_prev)
    {
        if (bl->bl_next)
        {
            // prevがNULLでnextがNULLでないのは有ってはならない
            if (battle_config.error_log)
                PRINTF("map_delblock error : bl->bl_next!=nullptr\n"_fmt);
        }
        return 0;
    }

    if (bl->bl_type == BL::PC)
        bl->bl_m->users--;

    if (bl->bl_next)
        bl->bl_next->bl_prev = bl->bl_prev;
    if (bl->bl_prev == dumb_ptr<block_list>(&bl_head))
    {
        // リストの頭なので、map[]のblock_listを更新する
        if (bl->bl_type == BL::MOB)
        {
            bl->bl_m->blocks.ref(bl->bl_x / BLOCK_SIZE, bl->bl_y / BLOCK_SIZE).mobs_only = bl->bl_next;
        }
        else
        {
            bl->bl_m->blocks.ref(bl->bl_x / BLOCK_SIZE, bl->bl_y / BLOCK_SIZE).normal = bl->bl_next;
        }
    }
    else
    {
        bl->bl_prev->bl_next = bl->bl_next;
    }
    bl->bl_next = nullptr;
    bl->bl_prev = nullptr;

    return 0;
}

/*==========================================
 * セル上のPCとMOBの数を数える (グランドクロス用)
 *------------------------------------------
 */
int map_count_oncell(Borrowed<map_local> m, int x, int y)
{
    int bx, by;
    dumb_ptr<block_list> bl = nullptr;
    int count = 0;

    if (x < 0 || y < 0 || (x >= m->xs) || (y >= m->ys))
        return 1;
    bx = x / BLOCK_SIZE;
    by = y / BLOCK_SIZE;

    bl = m->blocks.ref(bx, by).normal;
    for (; bl; bl = bl->bl_next)
    {
        if (bl->bl_x == x && bl->bl_y == y && bl->bl_type == BL::PC)
            count++;
    }
    bl = m->blocks.ref(bx, by).mobs_only;
    for (; bl; bl = bl->bl_next)
    {
        if (bl->bl_x == x && bl->bl_y == y)
            count++;
    }
    if (!count)
        count = 1;
    return count;
}

/*==========================================
 * map m (x0,y0)-(x1,y1)内の全objに対して
 * funcを呼ぶ
 * type!=0 ならその種類のみ
 *------------------------------------------
 */
void map_foreachinarea(std::function<void(dumb_ptr<block_list>)> func,
        Borrowed<map_local> m,
        int x0, int y0, int x1, int y1,
        BL type)
{
    std::vector<dumb_ptr<block_list>> bl_list;

    // there are some broadcasts during startup
    // disable then
    if (m == borrow(undefined_gat))
        abort();

    if (x0 < 0)
        x0 = 0;
    if (y0 < 0)
        y0 = 0;
    if (x1 >= m->xs)
        x1 = m->xs - 1;
    if (y1 >= m->ys)
        y1 = m->ys - 1;
    if (type == BL::NUL || type != BL::MOB)
        for (int by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++)
        {
            for (int bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++)
            {
                dumb_ptr<block_list> bl = m->blocks.ref(bx, by).normal;
                for (; bl; bl = bl->bl_next)
                {
                    if (type != BL::NUL && bl->bl_type != type)
                        continue;
                    if (bl->bl_x >= x0 && bl->bl_x <= x1
                        && bl->bl_y >= y0 && bl->bl_y <= y1)
                        bl_list.push_back(bl);
                }
            }
        }
    if (type == BL::NUL || type == BL::MOB)
        for (int by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++)
        {
            for (int bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++)
            {
                dumb_ptr<block_list> bl = m->blocks.ref(bx, by).mobs_only;
                for (; bl; bl = bl->bl_next)
                {
                    if (bl->bl_x >= x0 && bl->bl_x <= x1
                        && bl->bl_y >= y0 && bl->bl_y <= y1)
                        bl_list.push_back(bl);
                }
            }
        }

    MapBlockLock lock;

    for (dumb_ptr<block_list> bl : bl_list)
        if (bl->bl_prev)
            func(bl);
}

/*==========================================
 * 矩形(x0,y0)-(x1,y1)が(dx,dy)移動した時の
 * 領域外になる領域(矩形かL字形)内のobjに
 * 対してfuncを呼ぶ
 *
 * dx,dyは-1,0,1のみとする(どんな値でもいいっぽい?)
 *------------------------------------------
 */
void map_foreachinmovearea(std::function<void(dumb_ptr<block_list>)> func,
        Borrowed<map_local> m,
        int x0, int y0, int x1, int y1,
        int dx, int dy,
        BL type)
{
    std::vector<dumb_ptr<block_list>> bl_list;
    // Note: the x0, y0, x1, y1 are bl.bl_x, bl.bl_y ± AREA_SIZE,
    // but only a small subset actually needs to be done.
    if (dx == 0 || dy == 0)
    {
        assert (dx || dy);
        // for aligned movement, only needs to check a rectangular area
        if (dx == 0)
        {
            if (dy < 0)
                y0 = y1 + dy + 1;
            else
                y1 = y0 + dy - 1;
        }
        else if (dy == 0)
        {
            if (dx < 0)
                x0 = x1 + dx + 1;
            else
                x1 = x0 + dx - 1;
        }
        if (x0 < 0)
            x0 = 0;
        if (y0 < 0)
            y0 = 0;
        if (x1 >= m->xs)
            x1 = m->xs - 1;
        if (y1 >= m->ys)
            y1 = m->ys - 1;
        for (int by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++)
        {
            for (int bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++)
            {
                dumb_ptr<block_list> bl = m->blocks.ref(bx, by).normal;
                for (; bl; bl = bl->bl_next)
                {
                    if (type != BL::NUL && bl->bl_type != type)
                        continue;
                    if (bl->bl_x >= x0 && bl->bl_x <= x1
                        && bl->bl_y >= y0 && bl->bl_y <= y1)
                        bl_list.push_back(bl);
                }
                bl = m->blocks.ref(bx, by).mobs_only;
                for (; bl; bl = bl->bl_next)
                {
                    if (type != BL::NUL && bl->bl_type != type)
                        continue;
                    if (bl->bl_x >= x0 && bl->bl_x <= x1
                        && bl->bl_y >= y0 && bl->bl_y <= y1)
                        bl_list.push_back(bl);
                }
            }
        }
    }
    else
    {
        // L字領域の場合

        if (x0 < 0)
            x0 = 0;
        if (y0 < 0)
            y0 = 0;
        if (x1 >= m->xs)
            x1 = m->xs - 1;
        if (y1 >= m->ys)
            y1 = m->ys - 1;
        for (int by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++)
        {
            for (int bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++)
            {
                dumb_ptr<block_list> bl = m->blocks.ref(bx, by).normal;
                for (; bl; bl = bl->bl_next)
                {
                    if (type != BL::NUL && bl->bl_type != type)
                        continue;
                    if (!(bl->bl_x >= x0 && bl->bl_x <= x1
                            && bl->bl_y >= y0 && bl->bl_y <= y1))
                        continue;
                    if ((dx > 0 && bl->bl_x < x0 + dx)
                        || (dx < 0 && bl->bl_x > x1 + dx)
                        || (dy > 0 && bl->bl_y < y0 + dy)
                        || (dy < 0 && bl->bl_y > y1 + dy))
                        bl_list.push_back(bl);
                }
                bl = m->blocks.ref(bx, by).mobs_only;
                for (; bl; bl = bl->bl_next)
                {
                    if (type != BL::NUL && bl->bl_type != type)
                        continue;
                    if (!(bl->bl_x >= x0 && bl->bl_x <= x1
                             && bl->bl_y >= y0 && bl->bl_y <= y1))
                        continue;
                    if ((dx > 0 && bl->bl_x < x0 + dx)
                        || (dx < 0 && bl->bl_x > x1 + dx)
                        || (dy > 0 && bl->bl_y < y0 + dy)
                        || (dy < 0 && bl->bl_y > y1 + dy))
                        bl_list.push_back(bl);
                }
            }
        }

    }

    MapBlockLock lock;

    for (dumb_ptr<block_list> bl : bl_list)
        if (bl->bl_prev)
            func(bl);
}

// -- moonsoul  (added map_foreachincell which is a rework of map_foreachinarea but
//           which only checks the exact single x/y passed to it rather than an
//           area radius - may be more useful in some instances)
//
void map_foreachincell(std::function<void(dumb_ptr<block_list>)> func,
        Borrowed<map_local> m,
        int x, int y,
        BL type)
{
    std::vector<dumb_ptr<block_list>> bl_list;
    int by = y / BLOCK_SIZE;
    int bx = x / BLOCK_SIZE;

    if (type == BL::NUL || type != BL::MOB)
    {
        dumb_ptr<block_list> bl = m->blocks.ref(bx, by).normal;
        for (; bl; bl = bl->bl_next)
        {
            if (type != BL::NUL && bl->bl_type != type)
                continue;
            if (bl->bl_x == x && bl->bl_y == y)
                bl_list.push_back(bl);
        }
    }

    if (type == BL::NUL || type == BL::MOB)
    {
        dumb_ptr<block_list> bl = m->blocks.ref(bx, by).mobs_only;
        for (; bl; bl = bl->bl_next)
        {
            if (bl->bl_x == x && bl->bl_y == y)
                bl_list.push_back(bl);
        }
    }

    MapBlockLock lock;

    for (dumb_ptr<block_list> bl : bl_list)
        if (bl->bl_prev)
            func(bl);
}

/*==========================================
 * 床アイテムやエフェクト用の一時obj割り当て
 * object[]への保存とid_db登録まで
 *
 * bl->bl_idもこの中で設定して問題無い?
 *------------------------------------------
 */
BlockId map_addobject(dumb_ptr<block_list> bl)
{
    BlockId i;
    if (!bl)
    {
        PRINTF("map_addobject nullpo?\n"_fmt);
        return BlockId();
    }
    if (first_free_object_id < wrap<BlockId>(2) || first_free_object_id == MAX_FLOORITEM)
        first_free_object_id = wrap<BlockId>(2);
    for (i = first_free_object_id; i < MAX_FLOORITEM; i = next(i))
        if (!object[i._value])
            break;
    if (i == MAX_FLOORITEM)
    {
        if (battle_config.error_log)
            PRINTF("no free object id\n"_fmt);
        return BlockId();
    }
    first_free_object_id = i;
    object[i._value] = bl;
    id_db.put(i, bl);
    return i;
}

/*==========================================
 * 一時objectの解放
 *      map_delobjectのfreeしないバージョン
 *------------------------------------------
 */
void map_delobjectnofree(BlockId id, BL type)
{
    assert (id < MAX_FLOORITEM);
    if (!object[id._value])
        return;

    if (object[id._value]->bl_type != type)
    {
        FPRINTF(stderr, "Incorrect type: expected %d, got %d\n"_fmt,
                type,
                object[id._value]->bl_type);
        abort();
    }

    map_delblock(object[id._value]);
    id_db.put(id, dumb_ptr<block_list>());
    object[id._value] = nullptr;

    if (id < first_free_object_id)
        first_free_object_id = id;
}

/*==========================================
 * 一時objectの解放
 * block_listからの削除、id_dbからの削除
 * object dataのfree、object[]へのNULL代入
 *
 * addとの対称性が無いのが気になる
 *------------------------------------------
 */
void map_delobject(BlockId id, BL type)
{
    assert (id < MAX_FLOORITEM);
    dumb_ptr<block_list> obj = object[id._value];

    if (obj == nullptr)
        return;

    map_delobjectnofree(id, type);
    if (obj->bl_type == BL::PC)     // [Fate] Not sure where else to put this... I'm not sure where delobject for PCs is called from
        pc_cleanup(obj->is_player());

    MapBlockLock::freeblock(obj);
}

/*==========================================
 * 全一時obj相手にfuncを呼ぶ
 *
 *------------------------------------------
 */
void map_foreachobject(std::function<void(dumb_ptr<block_list>)> func,
        BL type)
{
    std::vector<dumb_ptr<block_list>> bl_list;
    for (BlockId i = wrap<BlockId>(2); i < MAX_FLOORITEM; i = next(i))
    {
        if (!object[i._value])
            continue;
        {
            if (type != BL::NUL && object[i._value]->bl_type != type)
                continue;
            bl_list.push_back(object[i._value]);
        }
    }

    MapBlockLock lock;

    for (dumb_ptr<block_list> bl : bl_list)
    {
        // TODO figure out if the second branch can happen
        // bl_prev is non-null for all that are on a map (see bl_head)
        // bl_next is only meaningful for objects that are on a map
        if (bl->bl_prev || bl->bl_next)
            func(bl);
    }
}

/*==========================================
 * 床アイテムを消す
 *
 * data==0の時はtimerで消えた時
 * data!=0の時は拾う等で消えた時として動作
 *
 * 後者は、map_clearflooritem(id)へ
 * map.h内で#defineしてある
 *------------------------------------------
 */
void map_clearflooritem_timer(TimerData *tid, tick_t, BlockId id)
{
    assert (id < MAX_FLOORITEM);
    dumb_ptr<block_list> obj = object[id._value];
    assert (obj && obj->bl_type == BL::ITEM);
    dumb_ptr<flooritem_data> fitem = obj->is_item();
    if (!tid)
        fitem->cleartimer.cancel();
    clif_clearflooritem(fitem, nullptr);
    map_delobject(fitem->bl_id, BL::ITEM);
}

std::pair<uint16_t, uint16_t> map_randfreecell(Borrowed<map_local> m,
        uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
    for (int itr : random_::iterator(w * h))
    {
        int dx = itr % w;
        int dy = itr / w;
        if (!bool(read_gatp(m, x + dx, y + dy) & MapCell::UNWALKABLE))
            return {static_cast<uint16_t>(x + dx), static_cast<uint16_t>(y + dy)};
    }
    return {0_u16, 0_u16};
}

/// Return a randomly selected passable cell within a given range.
static
std::pair<uint16_t, uint16_t> map_searchrandfreecell(Borrowed<map_local> m, int x, int y, int range)
{
    int whole_range = 2 * range + 1;
    return map_randfreecell(m, x - range, y - range, whole_range, whole_range);
}

/*==========================================
 * (m,x,y)を中心に3x3以内に床アイテム設置
 *
 * item_dataはamount以外をcopyする
 *------------------------------------------
 */
BlockId map_addflooritem_any(Item *item_data, int amount,
        Borrowed<map_local> m, int x, int y,
        dumb_ptr<map_session_data> *owners, interval_t *owner_protection,
        interval_t lifetime, int dispersal)
{
    dumb_ptr<flooritem_data> fitem = nullptr;

    nullpo_retr(BlockId(), item_data);
    auto xy = map_searchrandfreecell(m, x, y, dispersal);
    if (xy.first == 0 && xy.second == 0)
        return BlockId();

    fitem.new_();
    fitem->bl_type = BL::ITEM;
    fitem->bl_prev = fitem->bl_next = nullptr;
    fitem->bl_m = m;
    fitem->bl_x = xy.first;
    fitem->bl_y = xy.second;
    fitem->first_get_id = BlockId();
    fitem->first_get_tick = tick_t();
    fitem->second_get_id = BlockId();
    fitem->second_get_tick = tick_t();
    fitem->third_get_id = BlockId();
    fitem->third_get_tick = tick_t();

    fitem->bl_id = map_addobject(fitem);
    if (!fitem->bl_id)
    {
        fitem.delete_();
        return BlockId();
    }

    tick_t tick = gettick();

    if (owners[0])
        fitem->first_get_id = owners[0]->bl_id;
    fitem->first_get_tick = tick + owner_protection[0];

    if (owners[1])
        fitem->second_get_id = owners[1]->bl_id;
    fitem->second_get_tick = tick + owner_protection[1];

    if (owners[2])
        fitem->third_get_id = owners[2]->bl_id;
    fitem->third_get_tick = tick + owner_protection[2];

    fitem->item_data = *item_data;
    fitem->item_data.amount = amount;
    // TODO - talk to 4144 about maybe removing this.
    // It has no effect on the server itself, it is visual only.
    // If it is desirable to prevent items from visibly stacking
    // on the ground, that can be done with client-side randomness.
    // Currently, it yields the numbers {3 6 9 12}.
    fitem->subx = random_::in(1, 4) * 3;
    fitem->suby = random_::in(1, 4) * 3;
    fitem->cleartimer = Timer(gettick() + lifetime,
            std::bind(map_clearflooritem_timer, ph::_1, ph::_2,
                fitem->bl_id));

    map_addblock(fitem);
    clif_dropflooritem(fitem);

    return fitem->bl_id;
}

BlockId map_addflooritem(Item *item_data, int amount,
        Borrowed<map_local> m, int x, int y,
        dumb_ptr<map_session_data> first_sd,
        dumb_ptr<map_session_data> second_sd,
        dumb_ptr<map_session_data> third_sd)
{
    dumb_ptr<map_session_data> owners[3] = { first_sd, second_sd, third_sd };
    interval_t owner_protection[3];

    {
        owner_protection[0] = static_cast<interval_t>(battle_config.item_first_get_time);
        owner_protection[1] = owner_protection[0] + static_cast<interval_t>(battle_config.item_second_get_time);
        owner_protection[2] = owner_protection[1] + static_cast<interval_t>(battle_config.item_third_get_time);
    }

    return map_addflooritem_any(item_data, amount, m, x, y,
            owners, owner_protection,
            static_cast<interval_t>(battle_config.flooritem_lifetime), 1);
}

/*==========================================
 * charid_dbへ追加(返信待ちがあれば返信)
 *------------------------------------------
 */
void map_addchariddb(CharId charid, CharName name)
{
    P<struct charid2nick> p = charid_db.init(charid);

    p->nick = name;
    p->req_id = 0;
}

/*==========================================
 * id_dbへblを追加
 *------------------------------------------
 */
void map_addiddb(dumb_ptr<block_list> bl)
{
    nullpo_retv(bl);

    id_db.put(bl->bl_id, bl);
}

/*==========================================
 * id_dbからblを削除
 *------------------------------------------
 */
void map_deliddb(dumb_ptr<block_list> bl)
{
    nullpo_retv(bl);

    id_db.put(bl->bl_id, nullptr);
}

/*==========================================
 * nick_dbへsdを追加
 *------------------------------------------
 */
void map_addnickdb(dumb_ptr<map_session_data> sd)
{
    nullpo_retv(sd);

    nick_db.put(sd->status_key.name, sd);
}

/*==========================================
 * PCのquit処理 map.c内分
 *
 * quit処理の主体が違うような気もしてきた
 *------------------------------------------
 */
void map_quit(dumb_ptr<map_session_data> sd)
{
    nullpo_retv(sd);

    if (sd->trade_partner)      // 取引を中断する
        trade_tradecancel(sd);

    if (sd->party_invite)   // パーティ勧誘を拒否する
        party_reply_invite(sd, sd->party_invite_account, 0);

    party_send_logout(sd);     // パーティのログアウトメッセージ送信

    pc_cleareventtimer(sd);    // イベントタイマを破棄する

    skill_castcancel(sd, 0);  // 詠唱を中断する
    skill_stop_dancing(sd, 1);    // ダンス/演奏中断

    skill_status_change_clear(sd, 1); // ステータス異常を解除する
    pc_stop_walking(sd, 0);
    pc_stopattack(sd);
    pc_delinvincibletimer(sd);

    pc_calcstatus(sd, 4);

    clif_clearchar(sd, BeingRemoveWhy::QUIT);

    if (pc_isdead(sd))
        pc_setrestartvalue(sd, 2);
    pc_makesavestatus(sd);
    //クローンスキルで覚えたスキルは消す

    //The storage closing routines will save the char if needed. [Skotlex]
    if (!sd->state.storage_open)
        chrif_save(sd);
    else if (sd->state.storage_open)
        storage_storage_quit(sd);

    sd->npc_stackbuf.clear();

    map_delblock(sd);

    id_db.put(sd->bl_id, nullptr);
    nick_db.put(sd->status_key.name, nullptr);
    charid_db.erase(sd->status_key.char_id);
}

/*==========================================
 * id番号のPCを探す。居なければNULL
 *------------------------------------------
 */
dumb_ptr<map_session_data> map_id2sd(BlockId id)
{
    // This is bogus.
    // However, there might be differences for de-auth'ed accounts.
// remove search from db, because:
// 1 - all players, npc, items and mob are in this db (to search, it's not speed, and search in session is more sure)
// 2 - DB seems not always correct. Sometimes, when a player disconnects, its id (account value) is not removed and structure
//     point to a memory area that is not more a session_data and value are incorrect (or out of available memory) -> crash
// replaced by searching in all session.
// by searching in session, we are sure that fd, session, and account exist.
/*
        dumb_ptr<block_list> bl;

        bl=numdb_search(id_db,id);
        if (bl && bl->bl_type==BL::PC)
                return (struct map_session_data*)bl;
        return nullptr;
*/
    for (io::FD i : iter_fds())
    {
        Session *s = get_session(i);
        if (!s)
            continue;
        if (s->session_data)
        {
            map_session_data *sd = static_cast<map_session_data *>(s->session_data.get());
            if (sd->bl_id == id)
                return dumb_ptr<map_session_data>(sd);
        }
    }

    return nullptr;
}

/*==========================================
 * char_id番号の名前を探す
 *------------------------------------------
 */
CharName map_charid2nick(CharId id)
{
    Option<P<struct charid2nick>> p_ = charid_db.search(id);

    return p_.cmap(
            [](P<struct charid2nick> p)
            {
                return p->req_id == 0;
            },
            [](P<struct charid2nick> p)
            {
                return p->nick;
            }).move_or(CharName());
}

/*========================================*/
/* [Fate] Operations to iterate over active map sessions */

static
dumb_ptr<map_session_data> map_get_session(io::FD i)
{
    {
        Session *s = get_session(i);
        if (!s)
            return nullptr;
        map_session_data *d = static_cast<map_session_data *>(s->session_data.get());
        if (d && d->state.auth)
            return dumb_ptr<map_session_data>(d);
    }

    return nullptr;
}

static
dumb_ptr<map_session_data> map_get_session_forward(int start)
{
    for (int i = start; i < get_fd_max(); i++)
    {
        dumb_ptr<map_session_data> d = map_get_session(io::FD::cast_dammit(i));
        if (d)
            return d;
    }

    return nullptr;
}

static
dumb_ptr<map_session_data> map_get_session_backward(int start)
{
    for (int i = start; i >= 0; i--)
    {
        dumb_ptr<map_session_data> d = map_get_session(io::FD::cast_dammit(i));
        if (d)
            return d;
    }

    return nullptr;
}

dumb_ptr<map_session_data> map_get_first_session(void)
{
    return map_get_session_forward(0);
}

dumb_ptr<map_session_data> map_get_next_session(dumb_ptr<map_session_data> d)
{
    return map_get_session_forward(d->sess->fd.uncast_dammit() + 1);
}

dumb_ptr<map_session_data> map_get_last_session(void)
{
    return map_get_session_backward(get_fd_max());
}

dumb_ptr<map_session_data> map_get_prev_session(dumb_ptr<map_session_data> d)
{
    return map_get_session_backward(d->sess->fd.uncast_dammit() - 1);
}

/*==========================================
 * Search session data from a nick name
 * (without sensitive case if necessary)
 * return map_session_data pointer or nullptr
 *------------------------------------------
 */
dumb_ptr<map_session_data> map_nick2sd(CharName nick)
{
    for (io::FD i : iter_fds())
    {
        Session *s = get_session(i);
        if (!s)
            continue;
        map_session_data *pl_sd = static_cast<map_session_data *>(s->session_data.get());
        if (pl_sd && pl_sd->state.auth)
        {
            {
                if (pl_sd->status_key.name == nick)
                    return dumb_ptr<map_session_data>(pl_sd);
            }
        }
    }
    return nullptr;
}

/*==========================================
 * id番号の物を探す
 * 一時objectの場合は配列を引くのみ
 *------------------------------------------
 */
dumb_ptr<block_list> map_id2bl(BlockId id)
{
    dumb_ptr<block_list> bl = nullptr;
    if (id < MAX_FLOORITEM)
        bl = object[id._value];
    else
        bl = id_db.get(id);

    return bl;
}

/*==========================================
 * map.npcへ追加 (warp等の領域持ちのみ)
 *------------------------------------------
 */
int map_addnpc(Borrowed<map_local> m, dumb_ptr<npc_data> nd)
{
    int i;
    for (i = 0; i < m->npc_num && i < MAX_NPC_PER_MAP; i++)
        if (m->npc[i] == nullptr)
            break;
    if (i == MAX_NPC_PER_MAP)
    {
        if (battle_config.error_log)
            PRINTF("too many NPCs in one map %s\n"_fmt, m->name_);
        return -1;
    }
    if (i == m->npc_num)
    {
        m->npc_num++;
    }

    nullpo_retz(nd);

    m->npc[i] = nd;
    nd->n = i;
    id_db.put(nd->bl_id, nd);

    return i;
}

static
void map_removenpc(void)
{
    int n = 0;

    for (auto& mitp : maps_db)
    {
        if (!mitp.second->gat)
            continue;
        map_local *m = static_cast<map_local *>(mitp.second.get());
        for (int i = 0; i < m->npc_num && i < MAX_NPC_PER_MAP; i++)
        {
            if (m->npc[i] != nullptr)
            {
                clif_clearchar(m->npc[i], BeingRemoveWhy::QUIT);
                map_delblock(m->npc[i]);
                id_db.put(m->npc[i]->bl_id, nullptr);
                if (m->npc[i]->npc_subtype == NpcSubtype::SCRIPT)
                {
//                    free(m->npc[i]->u.scr.script);
//                    free(m->npc[i]->u.scr.label_list);
                }
                m->npc[i].delete_();
                n++;
            }
        }
    }
    PRINTF("%d NPCs removed.\n"_fmt, n);
}

/*==========================================
 * map名からmap番号へ変換
 *------------------------------------------
 */
Option<Borrowed<map_local>> map_mapname2mapid(MapName name)
{
    Option<P<map_abstract>> md_ = maps_db.get(name);
    return md_.cmap(
            [](P<map_abstract> md)
            {
                return bool(md->gat);
            },
            [](P<map_abstract> md)
            {
                return md.downcast_to<map_local>();
            });
}

/*==========================================
 * 他鯖map名からip,port変換
 *------------------------------------------
 */
int map_mapname2ipport(MapName name, Borrowed<IP4Address> ip, Borrowed<int> port)
{
    Option<P<map_abstract>> md_ = maps_db.get(name);
    return md_.cmap(
            [](P<map_abstract> md)
            {
                return !md->gat;
            },
            [ip, port](P<map_abstract> md)
            {
                auto mdos = md.downcast_to<map_remote>();
                *ip = mdos->ip;
                *port = mdos->port;
                return 0;
            }).copy_or(-1);
}

/// Check compatibility of directions.
/// Directions are compatible if they are at most 45° apart.
///
/// @return false if compatible, true if incompatible.
bool map_check_dir(const DIR s_dir, const DIR t_dir)
{
    if (s_dir == t_dir)
        return false;

    const uint8_t sdir = static_cast<uint8_t>(s_dir);
    const uint8_t tdir = static_cast<uint8_t>(t_dir);
    if ((sdir + 1) % 8 == tdir)
        return false;
    if (sdir == (tdir + 1) % 8)
        return false;

    return true;
}

/*==========================================
 * 彼我の方向を計算
 *------------------------------------------
 */
DIR map_calc_dir(dumb_ptr<block_list> src, int x, int y)
{
    DIR dir = DIR::S;
    int dx, dy;

    nullpo_retr(DIR::S, src);

    dx = x - src->bl_x;
    dy = y - src->bl_y;
    if (dx == 0 && dy == 0)
    {
        dir = DIR::S;
    }
    else if (dx >= 0 && dy >= 0)
    {
        dir = DIR::SE;
        if (dx * 3 - 1 < dy)
            dir = DIR::S;
        if (dx > dy * 3)
            dir = DIR::E;
    }
    else if (dx >= 0 && dy <= 0)
    {
        dir = DIR::NE;
        if (dx * 3 - 1 < -dy)
            dir = DIR::N;
        if (dx > -dy * 3)
            dir = DIR::E;
    }
    else if (dx <= 0 && dy <= 0)
    {
        dir = DIR::NW;
        if (dx * 3 + 1 > dy)
            dir = DIR::N;
        if (dx < dy * 3)
            dir = DIR::W;
    }
    else
    {
        dir = DIR::SW;
        if (-dx * 3 - 1 < dy)
            dir = DIR::S;
        if (-dx > dy * 3)
            dir = DIR::W;
    }
    return dir;
}

// gat系
/*==========================================
 * (m,x,y)の状態を調べる
 *------------------------------------------
 */
MapCell map_getcell(Borrowed<map_local> m, int x, int y)
{
    if (x < 0 || x >= m->xs - 1 || y < 0 || y >= m->ys - 1)
        return MapCell::UNWALKABLE;
    return m->gat[x + y * m->xs];
}

/*==========================================
 * (m,x,y)の状態をtにする
 *------------------------------------------
 */
void map_setcell(Borrowed<map_local> m, int x, int y, MapCell t)
{
    if (x < 0 || x >= m->xs || y < 0 || y >= m->ys)
        return;
    m->gat[x + y * m->xs] = t;
}

/*==========================================
 * 他鯖管理のマップをdbに追加
 *------------------------------------------
 */
int map_setipport(MapName name, IP4Address ip, int port)
{
    Option<P<map_abstract>> md_ = maps_db.get(name);
    OMATCH_BEGIN (md_)
    {
        OMATCH_CASE_SOME (md)
        {
            if (md->gat)
            {
                // local -> check data
                if (ip != clif_getip() || port != clif_getport())
                {
                    PRINTF("from char server : %s -> %s:%d\n"_fmt,
                            name, ip, port);
                    return 1;
                }
            }
            else
            {
                // update
                P<map_remote> mdos = md.downcast_to<map_remote>();
                mdos->ip = ip;
                mdos->port = port;
            }
        }
        OMATCH_CASE_NONE ()
        {
            // not exist -> add new data
            auto mdos = make_unique<map_remote>();
            mdos->name_ = name;
            mdos->gat = nullptr;
            mdos->ip = ip;
            mdos->port = port;
            maps_db.put(mdos->name_, std::move(mdos));
        }
    }
    OMATCH_END ();
    return 0;
}

/*==========================================
 * マップ1枚読み込み
 *------------------------------------------
 */
static
bool map_readmap(map_local *m, size_t num, MapName fn)
{
    // read & convert fn
    std::vector<uint8_t> gat_v = grfio_reads(fn);
    if (gat_v.empty())
        return false;
    size_t s = gat_v.size() - 4;

    int xs = m->xs = gat_v[0] | gat_v[1] << 8;
    int ys = m->ys = gat_v[2] | gat_v[3] << 8;
    PRINTF("Loading Maps [%zu/%zu]: %-30s  (%i, %i)\r"_fmt,
            num, maps_db.size(),
            fn, xs, ys);
    fflush(stdout);

    assert (s == xs * ys);
    m->gat = make_unique<MapCell[]>(s);

    m->npc_num = 0;
    m->users = 0;
    really_memzero_this(&m->flag);
    if (battle_config.pk_mode)
        m->flag.set(MapFlag::PVP, 1);
    MapCell *gat_m = reinterpret_cast<MapCell *>(&gat_v[4]);
    std::copy(gat_m, gat_m + s, &m->gat[0]);

    size_t bxs = (xs + BLOCK_SIZE - 1) / BLOCK_SIZE;
    size_t bys = (ys + BLOCK_SIZE - 1) / BLOCK_SIZE;
    m->blocks.reset(bxs, bys);

    return true;
}

/*==========================================
 * 全てのmapデータを読み込む
 *------------------------------------------
 */
static
bool map_readallmap(void)
{
    // I am increasingly of the opinion that this needs to be moved earlier.

    int maps_removed = 0;
    int num = 0;

    for (auto& mit : maps_db)
    {
        {
            {
                map_local *ml = static_cast<map_local *>(mit.second.get());
                if (!map_readmap(ml, num, mit.first))
                {
                    // Can't remove while implicitly iterating,
                    // and I don't feel like explicitly iterating.
                    //map_delmap(map[i].name);
                    maps_removed++;
                }
                else
                    num++;
            }
        }
    }

    PRINTF("Maps Loaded: %-65zu\n"_fmt, maps_db.size());
    if (maps_removed)
    {
        PRINTF("Cowardly refusing to keep going after removing %d maps.\n"_fmt,
                maps_removed);
        return false;
    }

    return true;
}

/*==========================================
 * 読み込むmapを追加する
 *------------------------------------------
 */
void map_addmap(MapName mapname)
{
    if (mapname == "clear"_s)
    {
        maps_db.clear();
        return;
    }

    auto newmap = make_unique<map_local>();
    newmap->name_ = mapname;
    // novice challenge: figure out why this is necessary,
    // and why the previous version worked
    MapName name = newmap->name_;
    maps_db.put(name, std::move(newmap));
}

/*==========================================
 * 読み込むmapを削除する
 *------------------------------------------
 */
void map_delmap(MapName mapname)
{
    if (mapname == "all"_s)
    {
        maps_db.clear();
        return;
    }

    maps_db.put(mapname, nullptr);
}

constexpr int LOGFILE_SECONDS_PER_CHUNK_SHIFT = 10;

static
std::unique_ptr<io::AppendFile> map_logfile;
static
AString map_logfile_name;
static
long map_logfile_index;

static
void map_close_logfile(void)
{
    if (map_logfile)
    {
        AString filename = STRPRINTF("%s.%ld"_fmt, map_logfile_name, map_logfile_index);
        const char *args[] =
        {
            "gzip",
            "-f",
            filename.c_str(),
            nullptr
        };
        char **argv = const_cast<char **>(args);

        map_logfile.reset();

        if (!fork())
        {
            execvp("gzip", argv);
            _exit(1);
        }
        wait(nullptr);
    }
}

static
void map_start_logfile(long index)
{
    map_logfile_index = index;

    AString filename_buf = STRPRINTF(
            "%s.%ld"_fmt,
            map_logfile_name,
            map_logfile_index);
    map_logfile = make_unique<io::AppendFile>(filename_buf);
    if (!map_logfile->is_open())
    {
        map_logfile.reset();
        perror(map_logfile_name.c_str());
    }
}

static
void map_set_logfile(AString filename)
{
    struct timeval tv;

    map_logfile_name = std::move(filename);
    gettimeofday(&tv, nullptr);

    map_start_logfile(tv.tv_sec >> LOGFILE_SECONDS_PER_CHUNK_SHIFT);

    MAP_LOG("log-start v5"_fmt);
}

void map_log(XString line)
{
    if (!map_logfile)
        return;

    time_t t = TimeT::now();
    long i = t >> LOGFILE_SECONDS_PER_CHUNK_SHIFT;

    if (i != map_logfile_index)
    {
        map_close_logfile();
        map_start_logfile(i);
    }

    log_with_timestamp(*map_logfile, line);
}

static
bool map_config(io::Spanned<XString> w1, io::Spanned<ZString> w2)
{
    struct hostent *h = nullptr;

    {
        if (w1.data == "userid"_s)
        {
            AccountName name = stringish<AccountName>(w2.data);
            chrif_setuserid(name);
        }
        else if (w1.data == "passwd"_s)
        {
            AccountPass pass = stringish<AccountPass>(w2.data);
            chrif_setpasswd(pass);
        }
        else if (w1.data == "char_ip"_s)
        {
            h = gethostbyname(w2.data.c_str());
            IP4Address w2ip;
            if (h != nullptr)
            {
                w2ip = IP4Address({
                        static_cast<uint8_t>(h->h_addr[0]),
                        static_cast<uint8_t>(h->h_addr[1]),
                        static_cast<uint8_t>(h->h_addr[2]),
                        static_cast<uint8_t>(h->h_addr[3]),
                });
                PRINTF("Character server IP address : %s -> %s\n"_fmt,
                        w2.data, w2ip);
            }
            else
            {
                PRINTF("Bad IP value: %s\n"_fmt, w2.data);
                return false;
            }
            chrif_setip(w2ip);
        }
        else if (w1.data == "char_port"_s)
        {
            chrif_setport(atoi(w2.data.c_str()));
        }
        else if (w1.data == "map_ip"_s)
        {
            h = gethostbyname(w2.data.c_str());
            IP4Address w2ip;
            if (h != nullptr)
            {
                w2ip = IP4Address({
                        static_cast<uint8_t>(h->h_addr[0]),
                        static_cast<uint8_t>(h->h_addr[1]),
                        static_cast<uint8_t>(h->h_addr[2]),
                        static_cast<uint8_t>(h->h_addr[3]),
                });
                PRINTF("Map server IP address : %s -> %s\n"_fmt,
                        w2.data, w2ip);
            }
            else
            {
                PRINTF("Bad IP value: %s\n"_fmt, w2.data);
                return false;
            }
            clif_setip(w2ip);
        }
        else if (w1.data == "map_port"_s)
        {
            clif_setport(atoi(w2.data.c_str()));
        }
        else if (w1.data == "map"_s)
        {
            MapName name = VString<15>(w2.data);
            map_addmap(name);
        }
        else if (w1.data == "delmap"_s)
        {
            MapName name = VString<15>(w2.data);
            map_delmap(name);
        }
        else if (w1.data == "npc"_s)
        {
            npc_addsrcfile(w2.data);
        }
        else if (w1.data == "delnpc"_s)
        {
            npc_delsrcfile(w2.data);
        }
        else if (w1.data == "autosave_time"_s)
        {
            autosave_time = std::chrono::seconds(atoi(w2.data.c_str()));
            if (autosave_time <= interval_t::zero())
                autosave_time = DEFAULT_AUTOSAVE_INTERVAL;
        }
        else if (w1.data == "motd_txt"_s)
        {
            motd_txt = w2.data;
        }
        else if (w1.data == "mapreg_txt"_s)
        {
            mapreg_txt = w2.data;
        }
        else if (w1.data == "gm_log"_s)
        {
            gm_log = std::move(w2.data);
        }
        else if (w1.data == "log_file"_s)
        {
            map_set_logfile(w2.data);
        }
        else if (w1.data == "import"_s)
        {
            return load_config_file(w2.data, map_config);
        }
        else
        {
            return false;
        }
    }

    return true;
}

static
void cleanup_sub(dumb_ptr<block_list> bl)
{
    nullpo_retv(bl);

    switch (bl->bl_type)
    {
        case BL::PC:
            map_delblock(bl);  // There is something better...
            break;
        case BL::NPC:
            npc_delete(bl->is_npc());
            break;
        case BL::MOB:
            mob_delete(bl->is_mob());
            break;
        case BL::ITEM:
            map_clearflooritem(bl->bl_id);
            break;
        case BL::SPELL:
            magic::spell_free_invocation(bl->is_spell());
            break;
    }
}

/*==========================================
 * map鯖終了時処理
 *------------------------------------------
 */
void term_func(void)
{
    for (auto& mit : maps_db)
    {
        if (!mit.second->gat)
            continue;
        P<map_local> map_id = borrow(*mit.second).downcast_to<map_local>();

        map_foreachinarea(cleanup_sub,
                map_id,
                0, 0,
                map_id->xs, map_id->ys,
                BL::NUL);
    }

    for (io::FD i : iter_fds())
        delete_session(get_session(i));

    map_removenpc();

    maps_db.clear();

    do_final_script();
    do_final_itemdb();
    do_final_storage();

    map_close_logfile();
}

int compare_item(Item *a, Item *b)
{
    return (a->nameid == b->nameid);
}

static
bool map_confs(io::Spanned<XString> key, io::Spanned<ZString> value)
{
    if (key.data == "map_conf"_s)
        return load_config_file(value.data, map_config);
    if (key.data == "battle_conf"_s)
        return battle_config_read(value.data);
    if (key.data == "atcommand_conf"_s)
        return atcommand_config_read(value.data);

    if (key.data == "item_db"_s)
        return itemdb_readdb(value.data);
    if (key.data == "mob_db"_s)
        return mob_readdb(value.data);
    if (key.data == "mob_skill_db"_s)
        return mob_readskilldb(value.data);
    if (key.data == "skill_db"_s)
        return skill_readdb(value.data);
    if (key.data == "magic_conf"_s)
        return magic::load_magic_file_v2(value.data);

    if (key.data == "resnametable"_s)
        return load_resnametable(value.data);
    if (key.data == "const_db"_s)
        return read_constdb(value.data);
    PRINTF("unknown map conf key: %s\n"_fmt, AString(key.data));
    return false;
}

/*======================================================
 * Map-Server Init and Command-line Arguments [Valaris]
 *------------------------------------------------------
 */
int do_init(Slice<ZString> argv)
{
    ZString argv0 = argv.pop_front();
    runflag &= magic::magic_init0();

    bool loaded_config_yet = false;
    while (argv)
    {
        ZString argvi = argv.pop_front();
        if (argvi.startswith('-'))
        {
            if (argvi == "--help"_s)
            {
                PRINTF("Usage: %s [--help] [--version] [--write_atcommand_config outfile] [files...]\n"_fmt,
                        argv0);
                exit(0);
            }
            else if (argvi == "--version"_s)
            {
                PRINTF("%s\n"_fmt, CURRENT_VERSION_STRING);
                exit(0);
            }
            else if (argvi == "--write-atcommand-config"_s)
            {
                if (!argv)
                {
                    PRINTF("Missing argument\n"_fmt);
                    exit(1);
                }
                ZString filename = argv.pop_front();
                atcommand_config_write(filename);
                exit(0);
            }
            else
            {
                FPRINTF(stderr, "Unknown argument: %s\n"_fmt, argvi);
                runflag = false;
            }
        }
        else
        {
            loaded_config_yet = true;
            runflag &= load_config_file(argvi, map_confs);
        }
    }

    if (!loaded_config_yet)
        runflag &= load_config_file("conf/tmwa-map.conf"_s, map_confs);

    battle_config_check();
    runflag &= map_readallmap();

    do_init_chrif();
    do_init_clif();
    do_init_mob2();
    do_init_script();

    runflag &= do_init_npc();
    do_init_pc();
    do_init_party();

    npc_event_do_oninit();     // npcのOnInitイベント実行

    if (battle_config.pk_mode == 1)
        PRINTF("The server is running in " SGR_BOLD SGR_RED "PK Mode" SGR_RESET "\n"_fmt);

    PRINTF("The map-server is " SGR_BOLD SGR_GREEN "ready" SGR_RESET " (Server is listening on the port %d).\n\n"_fmt,
            clif_getport());

    return 0;
}

int map_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
{
    dumb_ptr<block_list> bl = map_id2bl(id);

    if (!bl)
        return 0;

    switch (bl->bl_type)
    {
        case BL::NPC:
            return npc_scriptcont(sd, id);
        case BL::SPELL:
            magic::spell_execute_script(bl->is_spell());
            break;
    }

    return 0;
}
} // namespace tmwa