summaryrefslogblamecommitdiff
path: root/src/char/int_party.cpp
blob: 7b2ecc2b9074a4c7f4cd22f752a5a9e4e6fbde91 (plain) (tree)
1
                        



















                                                                           
 

                          
                                 
                                 

                                 

                            
                             
                            

                         
                          
 
                                 
 
                         




                                  

                   
                      
                    
                         
 

                        


              

               
      
                                                    
      
                                   
      
                                                                                
 
                                                
      
                                      
 



                     
                          
                       

                             
                                       
     
                                       

                           

                         
                           
                                         
                         

     
                        
 
                    
 
      
                                             
 
                       
 






                                                               
                                                      









                                                                         
     
                                       








                                                                                      

     
                

 

               
      
                                          




                                       

                                                                
         
                                                                                    
                                                               
                                         
                                                                                  
                                                               
                                         
                                         



         
                                    
                           
 
                                          
                      
               
 
                                   
                 
              
                            
     


                                                        




                            
                     
                                            
                                              
         



                                                

                                                                        
                                  


            
                                                                                     
                           


            

 
                                          
      
                                                            
 
                                           
                      

 
                                       
                          
 
                                           
                      
     
                                                                       
                                      

                 
                               
     
                                                       

                                      

             

 
                           
      
                                                                                      
 
                       
                       

 
                        
      
                                                 
 
                               
                               
     

                                                       
     
 
             

 
                                          
      
                                      
 

                                      


                                   
                                 








                                
                                                                         

 
                                             
                                        
 
          
 

                                   
                                    



                     
                                  

                                      

             

 
                                                
      
                                          
                                                              
 
          
 
                                                                          
               



                                                 
                                         
         
                                                                              
                                                                           

                                                                    

         

 
                                             
      
                                                                                
 
                               
     
                                                       
                                     
                                            
     


                                                                     
                         
 
                           
      
                                                                                
 

                                     
                     
     












                                                                           
     
                  
                                          

 
                                    
      
                                                     
 



                                                                                            
                                                           

 
                                    
      
                                                    
 



                                         
           





                                                                                                    
        


                                                                                               

 
                                    
      
                                                                                          
 




                                          

 
                                 
      
                                                                             

                                        





                                     
                  





                                                   
        



                                                                        
                                               

 
      
                                                                            











                                               
                           
      
                                                                              
 







                                               
 
                                                                                 

 
                                    
      
                                                  
 
                             
 









                                                    

 
                           
                                                   
 







                                              

                                                     

 
                        
      
                                                                             
 







                                                            


                                                                     
                            
 
               
      
                                                                                             
                            
 
     
                             
         
                                                                     
                                                     
                   


         
                                         
     
                                                                     
                                                 
               
     
 
                   
                                    
                                         
                  


                                        

                            



                           
                                    
 
                                      
                                                 
                            

 
                           
      
                                                        
 
                                                                      











                                                                 

 
                           
      
                                                                                   
                                           
 






                                                                      
 
                                       
     
                                     
         
                         

                                                 

                                     


                                    

                                                                
 
                                                        




                            
                                                                
                   

         
                                                        

 
                                    
      
                                                                                      
                          
 
                                                                         

                 

                                             






                     
                                                      

 
















                                                                                      
                           
                                                                              
 

                                                                         
                                       
     


                                                                    
 
                                     
                                      
                                                                                      
               
     

 
                                    
      
                                                                                   
                                        
 
                                                                         
 
                                       
     


                                                  
 
                               


                                      
 



                                                    
         
                 
                                                            
               
     

 
                                    
      
                                                                                             
 
                                                   
 
 
                                 
      
                                                                                             
 
                                                     

 




                                                                                                               
                                                                     
 

                                      

                    
         









                                                     
                                       




                            
                  
         
                    
         





                                                    
                                                
                  
         
                    
         









                                                     
                                          




                               
                  
         
                    
         








                                                     
                                             



                               
                  
         
                    
         






                                                     
                                      

                                
                  
         
                    
         









                                                     
                                          




                               
                  
         















                                                     
                    
         








                                                               
                                        


                               
                  
         
                    
         







                                                     
                                      


                               
                  
         
                
                                     

     
              

 
                                                         
                                                              
 
                                                          
 
                    
                   
#include "int_party.hpp"
//    int_party.cpp - Internal party handling.
//
//    Copyright © ????-2004 Athena Dev Teams
//    Copyright © 2004-2011 The Mana World Development Team
//    Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//
//    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 "../ints/udl.hpp"

#include "../strings/mstring.hpp"
#include "../strings/astring.hpp"
#include "../strings/xstring.hpp"

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

#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"
#include "../io/lock.hpp"
#include "../io/read.hpp"
#include "../io/write.hpp"

#include "../proto2/char-map.hpp"

#include "../mmo/ids.hpp"

#include "../high/extract_mmo.hpp"
#include "../high/mmo.hpp"

#include "../wire/packets.hpp"

#include "char.hpp"
#include "globals.hpp"
#include "inter.hpp"
#include "inter_conf.hpp"

#include "../poison.hpp"


namespace tmwa
{
namespace char_
{
static
void mapif_party_broken(PartyId party_id, int flag);
static
int party_check_empty(PartyPair p);
static
void mapif_parse_PartyLeave(Session *s, PartyId party_id, AccountId account_id);

// パーティデータの文字列への変換
static
AString inter_party_tostr(PartyPair p)
{
    MString str;
    str += STRPRINTF(
            "%d\t"
            "%s\t"
            "%d,%d\t"_fmt,
            p.party_id,
            p->name,
            p->exp, p->item);
    for (int i = 0; i < MAX_PARTY; i++)
    {
        PartyMember *m = &p->member[i];
        if (!m->account_id)
            continue;
        str += STRPRINTF(
                "%d,%d\t"
                "%s\t"_fmt,
                m->account_id, m->leader,
                m->name);
    }

    return AString(str);
}
} // namespace char_

static
bool impl_extract(XString str, PartyPair *pp)
{
    PartyPair& p = *pp;

    // not compatible with the normal extract()ors since it has
    // a variable-size element that uses the same separator
    std::vector<XString> bits;
    if (!extract(str, vrec<'\t'>(&bits)))
        return false;
    auto begin = bits.begin();
    auto end = bits.end();
    if (begin == end || !extract(*begin, &p.party_id))
        return false;
    ++begin;
    if (begin == end || !extract(*begin, &p->name))
        return false;
    ++begin;
    if (begin == end || !extract(*begin, record<','>(&p->exp, &p->item)))
        return false;
    ++begin;

    for (int i = 0; begin != end && i < MAX_PARTY; ++i)
    {
        PartyMember *m = &p->member[i];

        if (begin == end || !extract(*begin, record<','>(&m->account_id, &m->leader)))
            return false;
        ++begin;
        if (begin == end || !extract(*begin, &m->name))
            return false;
        ++begin;
        if (!m->account_id)
            --i;
    }

    return true;
}

namespace char_
{
static
void party_check_deleted_init(PartyPair p)
{
    for (int i = 0; i < MAX_PARTY; i++)
    {
        if (!p->member[i].account_id)
            continue;
        const CharPair *c = search_character(p->member[i].name);
        if (!c || c->key.account_id != p->member[i].account_id)
        {
            CHAR_LOG("WARNING: deleting obsolete party member %d %s of %d %s\n"_fmt,
                    p->member[i].account_id, p->member[i].name,
                    p.party_id, p->name);
            PRINTF("WARNING: deleting obsolete party member %d %s of %d %s\n"_fmt,
                    p->member[i].account_id, p->member[i].name,
                    p.party_id, p->name);
            p->member[i] = PartyMember{};
        }
    }
}

// パーティデータのロード
void inter_party_init(void)
{
    io::ReadFile in(inter_conf.party_txt);
    if (!in.is_open())
        return;

    // TODO: convert to use char_id
    AString line;
    int c = 0;
    while (in.getline(line))
    {
        PartyId i;
        if (extract(line, record<'\t'>(&i, "%newid%"_s))
            && party_newid < i)
        {
            party_newid = i;
            continue;
        }

        PartyMost pm;
        PartyPair pp{PartyId(), borrow(pm)};
        if (extract(line, &pp) && pp.party_id)
        {
            if (party_newid < next(pp.party_id))
                party_newid = next(pp.party_id);
            party_check_deleted_init(pp);
            party_db.insert(pp.party_id, pm);
            // note: this is still referring to the noncanonical copy of
            // the PartyMost pointer. This is okay, though.
            party_check_empty(pp);
        }
        else
        {
            PRINTF("int_party: broken data [%s] line %d\n"_fmt, inter_conf.party_txt,
                    c + 1);
        }
        c++;
    }
}

// パーティーデータのセーブ用
static
void inter_party_save_sub(PartyPair data, io::WriteFile& fp)
{
    AString line = inter_party_tostr(data);
    fp.put_line(line);
}

// パーティーデータのセーブ
int inter_party_save(void)
{
    io::WriteLock fp(inter_conf.party_txt);
    if (!fp.is_open())
    {
        PRINTF("int_party: cant write [%s] !!! data is lost !!!\n"_fmt,
                inter_conf.party_txt);
        return 1;
    }
    for (auto& pair : party_db)
    {
        PartyPair tmp{pair.first, borrow(pair.second)};
        inter_party_save_sub(tmp, fp);
    }

    return 0;
}

// パーティ名検索用
static
void search_partyname_sub(PartyPair p, PartyName str, Borrowed<Option<PartyPair>> dst)
{
    if (p->name == str)
        *dst = Some(p);
}

// パーティ名検索
static
Option<PartyPair> search_partyname(PartyName str)
{
    Option<PartyPair> p = None;
    for (auto& pair : party_db)
    {
        PartyPair tmp{pair.first, borrow(pair.second)};
        search_partyname_sub(tmp, str, borrow(p));
    }

    return p;
}

// EXP公平分配できるかチェック
static
int party_check_exp_share(PartyPair p)
{
    int i;
    int maxlv = 0, minlv = 0x7fffffff;

    for (i = 0; i < MAX_PARTY; i++)
    {
        int lv = p->member[i].lv;
        if (p->member[i].online)
        {
            if (lv < minlv)
                minlv = lv;
            if (maxlv < lv)
                maxlv = lv;
        }
    }

    return (maxlv == 0 || maxlv - minlv <= inter_conf.party_share_level);
}

// パーティが空かどうかチェック
int party_check_empty(const PartyPair p)
{
    int i;

    for (i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id)
        {
            return 0;
        }
    }
    // 誰もいないので解散
    mapif_party_broken(p.party_id, 0);
    party_db.erase(p.party_id);

    return 1;
}

// キャラの競合がないかチェック用
static
void party_check_conflict_sub(PartyPair p,
        PartyId party_id, AccountId account_id, CharName nick)
{
    int i;

    if (p.party_id == party_id)    // 本来の所属なので問題なし
        return;

    for (i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id == account_id
            && p->member[i].name == nick)
        {
            // 別のパーティに偽の所属データがあるので脱退
            PRINTF("int_party: party conflict! %d %d %d\n"_fmt, account_id,
                    party_id, p.party_id);
            mapif_parse_PartyLeave(nullptr, p.party_id, account_id);
        }
    }
}

// キャラの競合がないかチェック
static
void party_check_conflict(PartyId party_id, AccountId account_id, CharName nick)
{
    for (auto& pair : party_db)
    {
        PartyPair tmp{pair.first, borrow(pair.second)};
        party_check_conflict_sub(tmp,
                party_id, account_id, nick);
    }
}

//-------------------------------------------------------------------
// map serverへの通信

// パーティ作成可否
static
void mapif_party_created(Session *s, AccountId account_id, Option<PartyPair> p_)
{
    Packet_Fixed<0x3820> fixed_20;
    fixed_20.account_id = account_id;
    OMATCH_BEGIN (p_)
    {
        OMATCH_CASE_SOME (p)
        {
            fixed_20.error = 0;
            fixed_20.party_id = p.party_id;
            fixed_20.party_name = p->name;
            PRINTF("int_party: created! %d %s\n"_fmt, p.party_id, p->name);
        }
        OMATCH_CASE_NONE ()
        {
            fixed_20.error = 1;
            fixed_20.party_id = PartyId();
            fixed_20.party_name = stringish<PartyName>("error"_s);
        }
    }
    OMATCH_END ();
    send_fpacket<0x3820, 35>(s, fixed_20);
}

// パーティ情報見つからず
static
void mapif_party_noinfo(Session *s, PartyId party_id)
{
    Packet_Head<0x3821> head_21;
    Packet_Option<0x3821> option_21;
    head_21.party_id = party_id;
    send_opacket<0x3821, 8, sizeof(NetPacket_Option<0x3821>)>(s, head_21, false, option_21);
    PRINTF("int_party: info not found %d\n"_fmt, party_id);
}

// パーティ情報まとめ送り
static
void mapif_party_info(Session *s, const PartyPair p)
{
    Packet_Head<0x3821> head_21;
    head_21.party_id = p.party_id;
    Packet_Option<0x3821> option_21;
    option_21.party_most = *p.party_most;
    if (!s)
    {
        for (Session *ss : iter_map_sessions())
        {
            send_opacket<0x3821, 8, sizeof(NetPacket_Option<0x3821>)>(ss, head_21, true, option_21);
        }
    }
    else
    {
        send_opacket<0x3821, 8, sizeof(NetPacket_Option<0x3821>)>(s, head_21, true, option_21);
    }
}

// パーティメンバ追加可否
static
void mapif_party_memberadded(Session *s, PartyId party_id, AccountId account_id, int flag)
{
    Packet_Fixed<0x3822> fixed_22;
    fixed_22.party_id = party_id;
    fixed_22.account_id = account_id;
    fixed_22.flag = flag;
    send_fpacket<0x3822, 11>(s, fixed_22);
}

// パーティ設定変更通知
static
void mapif_party_optionchanged(Session *s, PartyPair p, AccountId account_id,
                               int flag)
{
    Packet_Fixed<0x3823> fixed_23;
    fixed_23.party_id = p.party_id;
    fixed_23.account_id = account_id;
    fixed_23.exp = p->exp;
    fixed_23.item = p->item;
    fixed_23.flag = flag;
    if (flag == 0)
    {
        for (Session *ss : iter_map_sessions())
        {
            send_fpacket<0x3823, 15>(ss, fixed_23);
        }
    }
    else
    {
        send_fpacket<0x3823, 15>(s, fixed_23);
    }
    PRINTF("int_party: option changed %d %d %d %d %d\n"_fmt, p.party_id,
            account_id, p->exp, p->item, flag);
}

static
void mapif_party_leaderchanged(Session *, PartyPair p, AccountId account_id,
                               int leader)
{
    Packet_Fixed<0x3828> fixed_28;
    fixed_28.party_id = p.party_id;
    fixed_28.account_id = account_id;
    fixed_28.leader = leader;
    for (Session *ss : iter_map_sessions())
    {
        send_fpacket<0x3828, 11>(ss, fixed_28);
    }
}

// パーティ脱退通知
static
void mapif_party_leaved(PartyId party_id, AccountId account_id, CharName name)
{
    Packet_Fixed<0x3824> fixed_24;
    fixed_24.party_id = party_id;
    fixed_24.account_id = account_id;
    fixed_24.char_name = name;
    for (Session *ss : iter_map_sessions())
    {
        send_fpacket<0x3824, 34>(ss, fixed_24);
    }

    PRINTF("int_party: party leaved %d %d %s\n"_fmt, party_id, account_id, name);
}

// パーティマップ更新通知
static
void mapif_party_membermoved(PartyPair p, int idx)
{
    assert (idx < MAX_PARTY);

    Packet_Fixed<0x3825> fixed_25;
    fixed_25.party_id = p.party_id;
    fixed_25.account_id = p->member[idx].account_id;
    fixed_25.map_name = p->member[idx].map;
    fixed_25.online = p->member[idx].online;
    fixed_25.level = p->member[idx].lv;
    for (Session *ss : iter_map_sessions())
    {
        send_fpacket<0x3825, 29>(ss, fixed_25);
    }
}

// パーティ解散通知
void mapif_party_broken(PartyId party_id, int flag)
{
    Packet_Fixed<0x3826> fixed_26;
    fixed_26.party_id = party_id;
    fixed_26.flag = flag;
    for (Session *ss : iter_map_sessions())
    {
        send_fpacket<0x3826, 7>(ss, fixed_26);
    }

    PRINTF("int_party: broken %d\n"_fmt, party_id);
    CHAR_LOG("int_party: broken %d\n"_fmt, party_id);
}

// パーティ内発言
static
void mapif_party_message(PartyId party_id, AccountId account_id, XString mes)
{
    Packet_Head<0x3827> head_27;
    XString repeat_27 = mes;
    head_27.party_id = party_id;
    head_27.account_id = account_id;
    for (Session *ss : iter_map_sessions())
    {
        send_vpacket<0x3827, 12, 1>(ss, head_27, repeat_27);
    }
}

//-------------------------------------------------------------------
// map serverからの通信

// パーティ
static
void mapif_parse_CreateParty(Session *s, AccountId account_id, PartyName name, CharName nick,
        MapName map, int lv)
{
    {
        if (!name.is_print())
        {
            PRINTF("int_party: illegal party name [%s]\n"_fmt, name);
            mapif_party_created(s, account_id, None);
            return;
        }
    }

    if (search_partyname(name).is_some())
    {
        PRINTF("int_party: same name party exists [%s]\n"_fmt, name);
        mapif_party_created(s, account_id, None);
        return;
    }

    PartyMost p {};
    party_newid = next(party_newid);
    PartyPair pp{party_newid, borrow(p)};
    p.name = name;
    p.exp = 0;
    p.item = 0;
    p.member[0].account_id = account_id;
    p.member[0].name = nick;
    p.member[0].map = map;
    p.member[0].leader = 1;
    p.member[0].online = 1;
    p.member[0].lv = lv;

    party_db.insert(pp.party_id, p);

    // pointer to noncanonical version
    mapif_party_created(s, account_id, Some(pp));
    mapif_party_info(s, pp);
}

// パーティ情報要求
static
void mapif_parse_PartyInfo(Session *s, PartyId party_id)
{
    Option<P<PartyMost>> maybe_party_most = party_db.search(party_id);
    OMATCH_BEGIN (maybe_party_most)
    {
        OMATCH_CASE_SOME (party_most)
        {
            mapif_party_info(s, PartyPair{party_id, party_most});
        }
        OMATCH_CASE_NONE ()
        {
            mapif_party_noinfo(s, party_id);
        }
    }
    OMATCH_END ();
}

// パーティ追加要求
static
void mapif_parse_PartyAddMember(Session *s, PartyId party_id, AccountId account_id,
        CharName nick, MapName map, int lv)
{
    Option<P<PartyMost>> maybe_party_most = party_db.search(party_id);
    P<PartyMost> party_most = TRY_UNWRAP(maybe_party_most,
            {
                mapif_party_memberadded(s, party_id, account_id, 1);
                return;
            });
    PartyPair p{party_id, party_most};

    for (int i = 0; i < MAX_PARTY; i++)
    {
        if (!p->member[i].account_id)
        {
            int flag = 0;

            p->member[i].account_id = account_id;
            p->member[i].name = nick;
            p->member[i].map = map;
            p->member[i].leader = 0;
            p->member[i].online = 1;
            p->member[i].lv = lv;
            mapif_party_memberadded(s, party_id, account_id, 0);
            mapif_party_info(nullptr, p);

            if (p->exp > 0 && !party_check_exp_share(p))
            {
                p->exp = 0;
                flag = 0x01;
            }
            if (flag)
                mapif_party_optionchanged(s, p, AccountId(), 0);
            return;
        }
    }
    mapif_party_memberadded(s, party_id, account_id, 1);
}

// パーティー設定変更要求
static
void mapif_parse_PartyChangeOption(Session *s, PartyId party_id, AccountId account_id,
        int exp, int item)
{
    PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};

    p->exp = exp;
    int flag = 0;
    if (exp > 0 && !party_check_exp_share(p))
    {
        flag |= 0x01;
        p->exp = 0;
    }

    p->item = item;

    mapif_party_optionchanged(s, p, account_id, flag);
}

static
void mapif_parse_PartyChangeLeader(Session *s, PartyId party_id, AccountId account_id,
        int leader)
{
    PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};

    for (int i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id != account_id)
            continue;

        mapif_party_leaderchanged(s, p, account_id, leader);
        p->member[i].leader = leader;
        return;
    }
}

// パーティ脱退要求
void mapif_parse_PartyLeave(Session *, PartyId party_id, AccountId account_id)
{
    PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};

    for (int i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id != account_id)
            continue;
        mapif_party_leaved(party_id, account_id, p->member[i].name);

        p->member[i] = PartyMember{};
        if (party_check_empty(p) == 0)
            mapif_party_info(nullptr, p);   // まだ人がいるのでデータ送信
        return;
    }
}

// パーティマップ更新要求
static
void mapif_parse_PartyChangeMap(Session *s, PartyId party_id, AccountId account_id,
        MapName map, int online, int lv)
{
    PartyPair p{party_id, TRY_UNWRAP(party_db.search(party_id), return)};

    for (int i = 0; i < MAX_PARTY; i++)
    {
        if (p->member[i].account_id != account_id)
            continue;
        int flag = 0;

        p->member[i].map = map;
        p->member[i].online = online;
        p->member[i].lv = lv;
        mapif_party_membermoved(p, i);

        if (p->exp > 0 && !party_check_exp_share(p))
        {
            p->exp = 0;
            flag = 1;
        }
        if (flag)
            mapif_party_optionchanged(s, p, AccountId(), 0);
        return;
    }
}

// パーティメッセージ送信
static
void mapif_parse_PartyMessage(Session *, PartyId party_id, AccountId account_id, XString mes)
{
    mapif_party_message(party_id, account_id, mes);
}

// パーティチェック要求
static
void mapif_parse_PartyCheck(Session *, PartyId party_id, AccountId account_id, CharName nick)
{
    party_check_conflict(party_id, account_id, nick);
}

// map server からの通信
// ・1パケットのみ解析すること
// ・パケット長データはinter.cにセットしておくこと
// ・パケット長チェックや、RFIFOSKIPは呼び出し元で行われるので行ってはならない
// ・エラーなら0(false)、そうでないなら1(true)をかえさなければならない
RecvResult inter_party_parse_frommap(Session *ms, uint16_t packet_id)
{
    RecvResult rv = RecvResult::Error;
    switch (packet_id)
    {
        case 0x3020:
        {
            Packet_Fixed<0x3020> fixed;
            rv = recv_fpacket<0x3020, 72>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            AccountId account = fixed.account_id;
            PartyName name = fixed.party_name;
            CharName nick = fixed.char_name;
            MapName map = fixed.map_name;
            uint16_t lv = fixed.level;
            mapif_parse_CreateParty(ms,
                    account,
                    name,
                    nick,
                    map,
                    lv);
            break;
        }
        case 0x3021:
        {
            Packet_Fixed<0x3021> fixed;
            rv = recv_fpacket<0x3021, 6>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            mapif_parse_PartyInfo(ms, party_id);
            break;
        }
        case 0x3022:
        {
            Packet_Fixed<0x3022> fixed;
            rv = recv_fpacket<0x3022, 52>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            CharName nick = fixed.char_name;
            MapName map = fixed.map_name;
            uint16_t lv = fixed.level;
            mapif_parse_PartyAddMember(ms,
                    party_id,
                    account_id,
                    nick,
                    map,
                    lv);
            break;
        }
        case 0x3023:
        {
            Packet_Fixed<0x3023> fixed;
            rv = recv_fpacket<0x3023, 14>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            uint16_t exp = fixed.exp;
            uint16_t item = fixed.item;
            mapif_parse_PartyChangeOption(ms,
                    party_id,
                    account_id,
                    exp,
                    item);
            break;
        }
        case 0x3024:
        {
            Packet_Fixed<0x3024> fixed;
            rv = recv_fpacket<0x3024, 10>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            mapif_parse_PartyLeave(ms,
                    party_id,
                    account_id);
            break;
        }
        case 0x3025:
        {
            Packet_Fixed<0x3025> fixed;
            rv = recv_fpacket<0x3025, 29>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            MapName map = fixed.map_name;
            uint8_t online = fixed.online;
            uint16_t lv = fixed.level;
            mapif_parse_PartyChangeMap(ms,
                    party_id,
                    account_id,
                    map,
                    online,
                    lv);
            break;
        }
        case 0x3026:
        {
            Packet_Fixed<0x3026> fixed;
            rv = recv_fpacket<0x3026, 11>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            uint8_t leader = fixed.leader;
            mapif_parse_PartyChangeLeader(ms,
                    party_id,
                    account_id,
                    leader);
            break;
        }
        case 0x3027:
        {
            Packet_Head<0x3027> head;
            AString repeat;
            rv = recv_vpacket<0x3027, 12, 1>(ms, head, repeat);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = head.party_id;
            AccountId account_id = head.account_id;
            AString& mes = repeat;
            mapif_parse_PartyMessage(ms,
                    party_id,
                    account_id,
                    mes);
            break;
        }
        case 0x3028:
        {
            Packet_Fixed<0x3028> fixed;
            rv = recv_fpacket<0x3028, 34>(ms, fixed);
            if (rv != RecvResult::Complete)
                break;

            PartyId party_id = fixed.party_id;
            AccountId account_id = fixed.account_id;
            CharName nick = fixed.char_name;
            mapif_parse_PartyCheck(ms,
                    party_id,
                    account_id,
                    nick);
            break;
        }
        default:
            return RecvResult::Error;
    }

    return rv;
}

// サーバーから脱退要求(キャラ削除用)
void inter_party_leave(PartyId party_id, AccountId account_id)
{
    mapif_parse_PartyLeave(nullptr, party_id, account_id);
}
} // namespace char_
} // namespace tmwa