From da6769f929be02a0b3b4d6c52b79922104cdd053 Mon Sep 17 00:00:00 2001
From: Ben Longbons <b.r.longbons@gmail.com>
Date: Mon, 23 Jun 2014 20:40:46 -0700
Subject: Use the generated char server protocol in the map server

---
 src/admin/ladmin.cpp |   6 +
 src/char/char.cpp    |  14 +-
 src/login/login.cpp  |   3 +-
 src/map/chrif.cpp    | 525 +++++++++++++++++++++++++++++----------------------
 src/map/intif.cpp    | 511 +++++++++++++++++++++++++------------------------
 src/map/intif.hpp    |   2 +-
 src/map/pc.cpp       |  14 +-
 src/map/pc.hpp       |   4 +-
 src/net/packets.hpp  |   2 +-
 9 files changed, 597 insertions(+), 484 deletions(-)

diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp
index 7b702ed..ba55d73 100644
--- a/src/admin/ladmin.cpp
+++ b/src/admin/ladmin.cpp
@@ -2815,6 +2815,12 @@ void parse_fromlogin(Session *s)
         }
     }
 
+    if (rv == RecvResult::Error)
+    {
+        s->set_eof();
+        return;
+    }
+
     // The following was almost certainly wrong and only worked because
     // localhost is a fast enough network to never split byte 1 vs byte 2
 
diff --git a/src/char/char.cpp b/src/char/char.cpp
index 33ee2b9..7bc17ba 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -1188,13 +1188,7 @@ int char_delete(CharPair *cp)
 static
 void parse_tologin(Session *ls)
 {
-    // only login-server can have an access to here.
-    // so, if it isn't the login-server, we disconnect the session (fd != login_fd).
-    if (ls != login_session)
-    {
-        ls->set_eof();
-        return;
-    }
+    assert (ls == login_session);
 
     char_session_data *sd = static_cast<char_session_data *>(ls->session_data.get());
 
@@ -1665,6 +1659,8 @@ void parse_tologin(Session *ls)
             }
         }
     }
+    if (rv == RecvResult::Error)
+        ls->set_eof();
 }
 
 //--------------------------------
@@ -2281,6 +2277,8 @@ void parse_frommap(Session *ms)
             }
         }
     }
+    if (rv == RecvResult::Error)
+        ms->set_eof();
 }
 
 static
@@ -2770,6 +2768,8 @@ void parse_char(Session *s)
                 return;
         }
     }
+    if (rv == RecvResult::Error)
+        s->set_eof();
 }
 
 static
diff --git a/src/login/login.cpp b/src/login/login.cpp
index 6edaeef..7537927 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -3245,7 +3245,8 @@ void parse_login(Session *s)
             }
         }
     }
-    return;
+    if (rv == RecvResult::Error)
+        s->set_eof();
 }
 
 //----------------------------------
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index a6c11b9..2998516 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -29,9 +29,11 @@
 #include "../io/cxxstdio.hpp"
 
 #include "../net/ip.hpp"
+#include "../net/packets.hpp"
 #include "../net/socket.hpp"
 #include "../net/timer.hpp"
-#include "../net/vomit.hpp"
+
+#include "../proto2/char-map.hpp"
 
 #include "../mmo/human_time_diff.hpp"
 #include "../mmo/mmo.hpp"
@@ -48,14 +50,6 @@
 
 #include "../poison.hpp"
 
-static
-const int packet_len_table[0x20] =
-{
-    60, 3, 10, 27, 22, -1, 6, -1,   // 2af8-2aff
-    6, -1, 18, 7, -1, 49, 44, 0,    // 2b00-2b07
-    6, 30, -1, 10, 86, 7, 44, 34,   // 2b08-2b0f
-    -1, -1, 10, 6, 11, -1, 0, 0,    // 2b10-2b17
-};
 
 Session *char_session;
 static
@@ -133,13 +127,12 @@ int chrif_save(dumb_ptr<map_session_data> sd)
 
     pc_makesavestatus(sd);
 
-    WFIFOW(char_session, 0) = 0x2b01;
-    WFIFOW(char_session, 2) = sizeof(sd->status_key) + sizeof(sd->status) + 12;
-    WFIFOL(char_session, 4) = unwrap<BlockId>(sd->bl_id);
-    WFIFOL(char_session, 8) = unwrap<CharId>(sd->char_id_);
-    WFIFO_STRUCT(char_session, 12, sd->status_key);
-    WFIFO_STRUCT(char_session, 12 + sizeof(sd->status_key), sd->status);
-    WFIFOSET(char_session, WFIFOW(char_session, 2));
+    Packet_Payload<0x2b01> payload_01;
+    payload_01.account_id = block_to_account(sd->bl_id);
+    payload_01.char_id = sd->char_id_;
+    payload_01.char_key = sd->status_key;
+    payload_01.char_data = sd->status;
+    send_ppacket<0x2b01>(char_session, payload_01);
 
     //For data sync
     if (sd->state.storage_open)
@@ -155,13 +148,13 @@ int chrif_save(dumb_ptr<map_session_data> sd)
 static
 int chrif_connect(Session *s)
 {
-    WFIFOW(s, 0) = 0x2af8;
-    WFIFO_STRING(s, 2, userid, 24);
-    WFIFO_STRING(s, 26, passwd, 24);
-    WFIFOL(s, 50) = 0;
-    WFIFOIP(s, 54) = clif_getip();
-    WFIFOW(s, 58) = clif_getport();  // [Valaris] thanks to fov
-    WFIFOSET(s, 60);
+    Packet_Fixed<0x2af8> fixed_f8;
+    fixed_f8.account_name = userid;
+    fixed_f8.account_pass = passwd;
+    fixed_f8.unused = 0;
+    fixed_f8.ip = clif_getip();
+    fixed_f8.port = clif_getport();
+    send_fpacket<0x2af8, 60>(s, fixed_f8);
 
     return 0;
 }
@@ -173,19 +166,17 @@ int chrif_connect(Session *s)
 static
 int chrif_sendmap(Session *s)
 {
-    int i = 0;
-
-    WFIFOW(s, 0) = 0x2afa;
+    std::vector<Packet_Repeat<0x2afa>> repeat_fa;
     for (auto& pair : maps_db)
     {
         map_abstract *ma = pair.second.get();
         if (!ma->gat)
             continue;
-        WFIFO_STRING(s, 4 + i * 16, ma->name_, 16);
-        i++;
+        Packet_Repeat<0x2afa> info;
+        info.map_name = ma->name_;
+        repeat_fa.push_back(info);
     }
-    WFIFOW(s, 2) = 4 + i * 16;
-    WFIFOSET(s, WFIFOW(s, 2));
+    send_packet_repeatonly<0x2afa, 4, 16>(s, repeat_fa);
 
     return 0;
 }
@@ -195,23 +186,21 @@ int chrif_sendmap(Session *s)
  *------------------------------------------
  */
 static
-int chrif_recvmap(Session *s)
+int chrif_recvmap(Session *, Packet_Head<0x2b04> head, const std::vector<Packet_Repeat<0x2b04>>& repeat)
 {
-    int i, j;
-
     if (chrif_state < 2)        // まだ準備中
         return -1;
 
-    IP4Address ip = RFIFOIP(s, 4);
-    uint16_t port = RFIFOW(s, 8);
-    for (i = 10, j = 0; i < RFIFOW(s, 2); i += 16, j++)
+    IP4Address ip = head.ip;
+    uint16_t port = head.port;
+    for (const Packet_Repeat<0x2b04>& i : repeat)
     {
-        MapName map = RFIFO_STRING<16>(s, i);
+        MapName map = i.map_name;
         map_setipport(map, ip, port);
     }
     if (battle_config.etc_log)
-        PRINTF("recv map on %s:%d (%d maps)\n"_fmt,
-                ip, port, j);
+        PRINTF("recv map on %s:%d (%zu maps)\n"_fmt,
+                ip, port, repeat.size());
 
     return 0;
 }
@@ -239,19 +228,19 @@ int chrif_changemapserver(dumb_ptr<map_session_data> sd,
         }
     }
 
-    WFIFOW(char_session, 0) = 0x2b05;
-    WFIFOL(char_session, 2) = unwrap<BlockId>(sd->bl_id);
-    WFIFOL(char_session, 6) = sd->login_id1;
-    WFIFOL(char_session, 10) = sd->login_id2;
-    WFIFOL(char_session, 14) = unwrap<CharId>(sd->status_key.char_id);
-    WFIFO_STRING(char_session, 18, name, 16);
-    WFIFOW(char_session, 34) = x;
-    WFIFOW(char_session, 36) = y;
-    WFIFOIP(char_session, 38) = ip;
-    WFIFOL(char_session, 42) = port;
-    WFIFOB(char_session, 44) = static_cast<uint8_t>(sd->status.sex);
-    WFIFOIP(char_session, 45) = s_ip;
-    WFIFOSET(char_session, 49);
+    Packet_Fixed<0x2b05> fixed_05;
+    fixed_05.account_id = block_to_account(sd->bl_id);
+    fixed_05.login_id1 = sd->login_id1;
+    fixed_05.login_id2 = sd->login_id2;
+    fixed_05.char_id = sd->status_key.char_id;
+    fixed_05.map_name = name;
+    fixed_05.x = x;
+    fixed_05.y = y;
+    fixed_05.map_ip = ip;
+    fixed_05.map_port = port;
+    fixed_05.sex = sd->status.sex;
+    fixed_05.client_ip = s_ip;
+    send_fpacket<0x2b05, 49>(char_session, fixed_05);
 
     return 0;
 }
@@ -261,26 +250,26 @@ int chrif_changemapserver(dumb_ptr<map_session_data> sd,
  *------------------------------------------
  */
 static
-int chrif_changemapserverack(Session *s)
+int chrif_changemapserverack(Session *, const Packet_Fixed<0x2b06>& fixed)
 {
-    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(wrap<AccountId>(RFIFOL(s, 2))));
+    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(fixed.account_id));
 
-    if (sd == NULL || sd->status_key.char_id != wrap<CharId>(RFIFOL(s, 14)))
+    if (sd == NULL || sd->status_key.char_id != fixed.char_id)
         return -1;
 
     // I am fairly certain that this is not possible
-    if (RFIFOL(s, 6) == 1)
+    if (fixed.error == 1)
     {
         if (battle_config.error_log)
             PRINTF("map server change failed.\n"_fmt);
         pc_authfail(sd->status_key.account_id);
         return 0;
     }
-    MapName mapname = RFIFO_STRING<16>(s, 18);
-    uint16_t x = RFIFOW(s, 34);
-    uint16_t y = RFIFOW(s, 36);
-    IP4Address ip = RFIFOIP(s, 38);
-    uint16_t port = RFIFOW(s, 42);
+    MapName mapname = fixed.map_name;
+    uint16_t x = fixed.x;
+    uint16_t y = fixed.y;
+    IP4Address ip = fixed.map_ip;
+    uint16_t port = fixed.map_port;
     clif_changemapserver(sd, mapname, x, y, ip, port);
 
     return 0;
@@ -291,11 +280,11 @@ int chrif_changemapserverack(Session *s)
  *------------------------------------------
  */
 static
-int chrif_connectack(Session *s)
+int chrif_connectack(Session *s, const Packet_Fixed<0x2af9>& fixed)
 {
-    if (RFIFOB(s, 2))
+    if (fixed.code)
     {
-        PRINTF("Connected to char-server failed %d.\n"_fmt, RFIFOB(s, 2));
+        PRINTF("Connected to char-server failed %d.\n"_fmt, fixed.code);
         exit(1);
     }
     PRINTF("Connected to char-server (connection #%d).\n"_fmt, s);
@@ -316,16 +305,16 @@ int chrif_connectack(Session *s)
  *------------------------------------------
  */
 static
-int chrif_sendmapack(Session *s)
+int chrif_sendmapack(Session *, Packet_Fixed<0x2afb> fixed)
 {
-    if (RFIFOB(s, 2))
+    if (fixed.unknown) //impossible
     {
         PRINTF("chrif : send map list to char server failed %d\n"_fmt,
-                RFIFOB(s, 2));
+                fixed.unknown);
         exit(1);
     }
 
-    wisp_server_name = stringish<CharName>(RFIFO_STRING<24>(s, 3));
+    wisp_server_name = fixed.whisper_name;
 
     chrif_state = 2;
 
@@ -351,13 +340,13 @@ int chrif_authreq(dumb_ptr<map_session_data> sd)
         if (dumb_ptr<map_session_data>(static_cast<map_session_data *>(s->session_data.get())) == sd)
         {
             assert (s == sd->sess);
-            WFIFOW(char_session, 0) = 0x2afc;
-            WFIFOL(char_session, 2) = unwrap<BlockId>(sd->bl_id);
-            WFIFOL(char_session, 6) = unwrap<CharId>(sd->char_id_);
-            WFIFOL(char_session, 10) = sd->login_id1;
-            WFIFOL(char_session, 14) = sd->login_id2;
-            WFIFOIP(char_session, 18) = s->client_ip;
-            WFIFOSET(char_session, 22);
+            Packet_Fixed<0x2afc> fixed_fc;
+            fixed_fc.account_id = block_to_account(sd->bl_id);
+            fixed_fc.char_id = sd->char_id_;
+            fixed_fc.login_id1 = sd->login_id1;
+            fixed_fc.login_id2 = sd->login_id2;
+            fixed_fc.ip = s->client_ip;
+            send_fpacket<0x2afc, 22>(char_session, fixed_fc);
             break;
         }
     }
@@ -390,12 +379,12 @@ int chrif_charselectreq(dumb_ptr<map_session_data> sd)
         }
     }
 
-    WFIFOW(char_session, 0) = 0x2b02;
-    WFIFOL(char_session, 2) = unwrap<BlockId>(sd->bl_id);
-    WFIFOL(char_session, 6) = sd->login_id1;
-    WFIFOL(char_session, 10) = sd->login_id2;
-    WFIFOIP(char_session, 14) = s_ip;
-    WFIFOSET(char_session, 18);
+    Packet_Fixed<0x2b02> fixed_02;
+    fixed_02.account_id = block_to_account(sd->bl_id);
+    fixed_02.login_id1 = sd->login_id1;
+    fixed_02.login_id2 = sd->login_id2;
+    fixed_02.ip = s_ip;
+    send_fpacket<0x2b02, 18>(char_session, fixed_02);
 
     return 0;
 }
@@ -409,12 +398,9 @@ void chrif_changegm(AccountId id, ZString pass)
     if (battle_config.etc_log)
         PRINTF("chrif_changegm: account: %d, password: '%s'.\n"_fmt, id, pass);
 
-    size_t len = pass.size() + 1;
-    WFIFOW(char_session, 0) = 0x2b0a;
-    WFIFOW(char_session, 2) = len + 8;
-    WFIFOL(char_session, 4) = unwrap<AccountId>(id);
-    WFIFO_STRING(char_session, 8, pass, len);
-    WFIFOSET(char_session, len + 8);
+    Packet_Head<0x2b0a> head_0a;
+    head_0a.account_id = id;
+    send_vpacket<0x2b0a, 8, 1>(char_session, head_0a, pass);
 }
 
 /*==========================================
@@ -428,11 +414,11 @@ void chrif_changeemail(AccountId id, AccountEmail actual_email,
         PRINTF("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n"_fmt,
                 id, actual_email, new_email);
 
-    WFIFOW(char_session, 0) = 0x2b0c;
-    WFIFOL(char_session, 2) = unwrap<AccountId>(id);
-    WFIFO_STRING(char_session, 6, actual_email, 40);
-    WFIFO_STRING(char_session, 46, new_email, 40);
-    WFIFOSET(char_session, 86);
+    Packet_Fixed<0x2b0c> fixed_0c;
+    fixed_0c.account_id = id;
+    fixed_0c.old_email = actual_email;
+    fixed_0c.new_email = new_email;
+    send_fpacket<0x2b0c, 86>(char_session, fixed_0c);
 }
 
 /*==========================================
@@ -449,14 +435,14 @@ void chrif_changeemail(AccountId id, AccountEmail actual_email,
 void chrif_char_ask_name(AccountId id, CharName character_name, short operation_type,
         HumanTimeDiff modif)
 {
-    WFIFOW(char_session, 0) = 0x2b0e;
-    WFIFOL(char_session, 2) = unwrap<AccountId>(id);   // account_id of who ask (for answer) -1 if nobody
-    WFIFO_STRING(char_session, 6, character_name.to__actual(), 24);
-    WFIFOW(char_session, 30) = operation_type;  // type of operation
+    Packet_Fixed<0x2b0e> fixed_0e;
+    fixed_0e.account_id = id;   // who ask, or nobody
+    fixed_0e.char_name = character_name;
+    fixed_0e.operation = operation_type;  // type of operation
     if (operation_type == 2)
-        WFIFO_STRUCT(char_session, 32, modif);
+        fixed_0e.ban_add = modif;
     PRINTF("chrif : sended 0x2b0e\n"_fmt);
-    WFIFOSET(char_session, 44);
+    send_fpacket<0x2b0e, 44>(char_session, fixed_0e);
 }
 
 /*==========================================
@@ -476,24 +462,24 @@ void chrif_char_ask_name(AccountId id, CharName character_name, short operation_
  *------------------------------------------
  */
 static
-int chrif_char_ask_name_answer(Session *s)
+int chrif_char_ask_name_answer(Session *, const Packet_Fixed<0x2b0f>& fixed)
 {
-    AccountId acc = wrap<AccountId>(RFIFOL(s, 2));       // account_id of who has asked (-1 if nobody)
-    CharName player_name = stringish<CharName>(RFIFO_STRING<24>(s, 6));
+    AccountId acc = fixed.account_id;       // who asked, or nobody
+    CharName player_name = fixed.char_name;
 
     dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(acc));
     if (acc && sd != NULL)
     {
         AString output;
-        if (RFIFOW(s, 32) == 1)   // player not found
+        if (fixed.error == 1)   // player not found
             output = STRPRINTF("The player '%s' doesn't exist."_fmt,
                     player_name);
         else
         {
-            switch (RFIFOW(s, 30))
+            switch (fixed.operation)
             {
                 case 1:        // block
-                    switch (RFIFOW(s, 32))
+                    switch (fixed.error)
                     {
                         case 0:    // login-server resquest done
                             output = STRPRINTF(
@@ -514,7 +500,7 @@ int chrif_char_ask_name_answer(Session *s)
                     }
                     break;
                 case 2:        // ban
-                    switch (RFIFOW(s, 32))
+                    switch (fixed.error)
                     {
                         case 0:    // login-server resquest done
                             output = STRPRINTF(
@@ -535,7 +521,7 @@ int chrif_char_ask_name_answer(Session *s)
                     }
                     break;
                 case 3:        // unblock
-                    switch (RFIFOW(s, 32))
+                    switch (fixed.error)
                     {
                         case 0:    // login-server resquest done
                             output = STRPRINTF(
@@ -556,7 +542,7 @@ int chrif_char_ask_name_answer(Session *s)
                     }
                     break;
                 case 4:        // unban
-                    switch (RFIFOW(s, 32))
+                    switch (fixed.error)
                     {
                         case 0:    // login-server resquest done
                             output = STRPRINTF(
@@ -577,7 +563,7 @@ int chrif_char_ask_name_answer(Session *s)
                     }
                     break;
                 case 5:        // changesex
-                    switch (RFIFOW(s, 32))
+                    switch (fixed.error)
                     {
                         case 0:    // login-server resquest done
                             output = STRPRINTF(
@@ -613,10 +599,10 @@ int chrif_char_ask_name_answer(Session *s)
  *------------------------------------------
  */
 static
-void chrif_changedgm(Session *s)
+void chrif_changedgm(Session *, const Packet_Fixed<0x2b0b>& fixed)
 {
-    AccountId acc = wrap<AccountId>(RFIFOL(s, 2));
-    GmLevel level = GmLevel::from(RFIFOL(s, 6));
+    AccountId acc = fixed.account_id;
+    GmLevel level = fixed.gm_level;
 
     dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(acc));
 
@@ -637,13 +623,13 @@ void chrif_changedgm(Session *s)
  *------------------------------------------
  */
 static
-void chrif_changedsex(Session *s)
+void chrif_changedsex(Session *, const Packet_Fixed<0x2b0d>& fixed)
 {
     int i;
     dumb_ptr<map_session_data> sd;
 
-    AccountId acc = wrap<AccountId>(RFIFOL(s, 2));
-    SEX sex = static_cast<SEX>(RFIFOB(s, 6));
+    AccountId acc = fixed.account_id;
+    SEX sex = fixed.sex;
     if (battle_config.etc_log)
         PRINTF("chrif_changedsex %d.\n"_fmt, acc);
     sd = map_id2sd(account_to_block(acc));
@@ -686,24 +672,24 @@ void chrif_changedsex(Session *s)
  */
 int chrif_saveaccountreg2(dumb_ptr<map_session_data> sd)
 {
-    int p, j;
     nullpo_retr(-1, sd);
 
-    p = 8;
-    for (j = 0; j < sd->status.account_reg2_num; j++)
+    std::vector<Packet_Repeat<0x2b10>> repeat_10;
+    for (size_t j = 0; j < sd->status.account_reg2_num; j++)
     {
         struct global_reg *reg = &sd->status.account_reg2[j];
         if (reg->str && reg->value != 0)
         {
-            WFIFO_STRING(char_session, p, reg->str, 32);
-            WFIFOL(char_session, p + 32) = reg->value;
-            p += 36;
+            Packet_Repeat<0x2b10> info;
+            info.name = reg->str;
+            info.value = reg->value;
+            repeat_10.push_back(info);
         }
     }
-    WFIFOW(char_session, 0) = 0x2b10;
-    WFIFOW(char_session, 2) = p;
-    WFIFOL(char_session, 4) = unwrap<BlockId>(sd->bl_id);
-    WFIFOSET(char_session, p);
+
+    Packet_Head<0x2b10> head_10;
+    head_10.account_id = block_to_account(sd->bl_id);
+    send_vpacket<0x2b10, 8, 36>(char_session, head_10, repeat_10);
 
     return 0;
 }
@@ -713,20 +699,19 @@ int chrif_saveaccountreg2(dumb_ptr<map_session_data> sd)
  *------------------------------------------
  */
 static
-int chrif_accountreg2(Session *s)
+int chrif_accountreg2(Session *, const Packet_Head<0x2b11>& head, const std::vector<Packet_Repeat<0x2b11>>& repeat)
 {
-    int j, p;
-    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(wrap<AccountId>(RFIFOL(s, 4))));
+    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(head.account_id));
     if (sd == NULL)
         return 1;
 
-    for (p = 8, j = 0; p < RFIFOW(s, 2) && j < ACCOUNT_REG2_NUM;
-         p += 36, j++)
+    size_t jlim = std::min(ACCOUNT_REG2_NUM, repeat.size());
+    for (size_t j = 0; j < jlim; j++)
     {
-        sd->status.account_reg2[j].str = stringish<VarName>(RFIFO_STRING<32>(s, p));
-        sd->status.account_reg2[j].value = RFIFOL(s, p + 32);
+        sd->status.account_reg2[j].str = repeat[j].name;
+        sd->status.account_reg2[j].value = repeat[j].value;
     }
-    sd->status.account_reg2_num = j;
+    sd->status.account_reg2_num = jlim;
 
     return 0;
 }
@@ -775,9 +760,9 @@ int chrif_send_divorce(CharId char_id)
     if (!char_session)
         return -1;
 
-    WFIFOW(char_session, 0) = 0x2b16;
-    WFIFOL(char_session, 2) = unwrap<CharId>(char_id);
-    WFIFOSET(char_session, 6);
+    Packet_Fixed<0x2b16> fixed_16;
+    fixed_16.char_id = char_id;
+    send_fpacket<0x2b16, 6>(char_session, fixed_16);
     return 0;
 }
 
@@ -786,11 +771,11 @@ int chrif_send_divorce(CharId char_id)
  *------------------------------------------
  */
 static
-int chrif_accountdeletion(Session *s)
+int chrif_accountdeletion(Session *, const Packet_Fixed<0x2b13>& fixed)
 {
     dumb_ptr<map_session_data> sd;
 
-    AccountId acc = wrap<AccountId>(RFIFOL(s, 2));
+    AccountId acc = fixed.account_id;
     if (battle_config.etc_log)
         PRINTF("chrif_accountdeletion %d.\n"_fmt, acc);
     sd = map_id2sd(account_to_block(acc));
@@ -818,11 +803,11 @@ int chrif_accountdeletion(Session *s)
  *------------------------------------------
  */
 static
-int chrif_accountban(Session *s)
+int chrif_accountban(Session *, const Packet_Fixed<0x2b14>& fixed)
 {
     dumb_ptr<map_session_data> sd;
 
-    AccountId acc = wrap<AccountId>(RFIFOL(s, 2));
+    AccountId acc = fixed.account_id;
     if (battle_config.etc_log)
         PRINTF("chrif_accountban %d.\n"_fmt, acc);
     sd = map_id2sd(account_to_block(acc));
@@ -831,9 +816,9 @@ int chrif_accountban(Session *s)
         if (sd != NULL)
         {
             sd->login_id1++;    // change identify, because if player come back in char within the 5 seconds, he can change its characters
-            if (RFIFOB(s, 6) == 0)
+            if (fixed.ban_not_status == 0)
             {                   // 0: change of statut, 1: ban
-                switch (RFIFOL(s, 7))
+                switch (static_cast<time_t>(fixed.status_or_ban_until))
                 {               // status or final date of a banishment
                     case 1:    // 0 = Unregistered ID
                         clif_displaymessage(sd->sess,
@@ -881,10 +866,10 @@ int chrif_accountban(Session *s)
                         break;
                 }
             }
-            else if (RFIFOB(s, 6) == 1)
+            else if (fixed.ban_not_status == 1)
             {
                 // 0: change of statut, 1: ban
-                const TimeT timestamp = static_cast<time_t>(RFIFOL(s, 7));    // status or final date of a banishment
+                const TimeT timestamp = fixed.status_or_ban_until;    // status or final date of a banishment
                 timestamp_seconds_buffer buffer;
                 stamp_time(buffer, &timestamp);
                 AString tmpstr = STRPRINTF("Your account has been banished until %s"_fmt, buffer);
@@ -907,10 +892,10 @@ int chrif_accountban(Session *s)
  *------------------------------------------
  */
 static
-int chrif_recvgmaccounts(Session *s)
+int chrif_recvgmaccounts(Session *s, const std::vector<Packet_Repeat<0x2b15>>& repeat)
 {
     PRINTF("From login-server: receiving of %d GM accounts information.\n"_fmt,
-            pc_read_gm_account(s));
+            pc_read_gm_account(s, repeat));
 
     return 0;
 }
@@ -921,9 +906,8 @@ int chrif_recvgmaccounts(Session *s)
  */
 int chrif_reloadGMdb(void)
 {
-
-    WFIFOW(char_session, 0) = 0x2af7;
-    WFIFOSET(char_session, 2);
+    Packet_Fixed<0x2af7> fixed_f7;
+    send_fpacket<0x2af7, 2>(char_session, fixed_f7);
 
     return 0;
 }
@@ -1014,10 +998,10 @@ void ladmin_itemfrob_c(dumb_ptr<block_list> bl, ItemNameId source_id, ItemNameId
 }
 
 static
-void ladmin_itemfrob(Session *s)
+void ladmin_itemfrob(Session *, const Packet_Fixed<0x2afa>& fixed)
 {
-    ItemNameId source_id = wrap<ItemNameId>(static_cast<uint16_t>(RFIFOL(s, 2)));
-    ItemNameId dest_id = wrap<ItemNameId>(static_cast<uint16_t>(RFIFOL(s, 6)));
+    ItemNameId source_id = fixed.source_item_id;
+    ItemNameId dest_id = fixed.dest_item_id;
     dumb_ptr<block_list> bl = map_get_first_session();
 
     // flooritems
@@ -1048,121 +1032,213 @@ void chrif_delete(Session *s)
 static
 void chrif_parse(Session *s)
 {
-    int packet_len, cmd;
-
-    // only char-server can have an access to here.
-    // so, if it isn't the char-server, we disconnect the session (fd != char_fd).
-    if (s != char_session)
-    {
-        s->set_eof();
-        return;
-    }
+    assert (s == char_session);
 
-    while (RFIFOREST(s) >= 2)
+    RecvResult rv = RecvResult::Complete;
+    uint16_t packet_id;
+    while (rv == RecvResult::Complete && packet_peek_id(s, &packet_id))
     {
-        cmd = RFIFOW(s, 0);
-        if (cmd < 0x2af8
-            || cmd >=
-            0x2af8 +
-            (sizeof(packet_len_table) / sizeof(packet_len_table[0]))
-            || packet_len_table[cmd - 0x2af8] == 0)
-        {
-
-            int r = intif_parse(s);  // intifに渡す
-
-            if (r == 1)
-                continue;       // intifで処理した
-            if (r == 2)
-                return;       // intifで処理したが、データが足りない
-
-            s->set_eof();
-            return;
-        }
-        packet_len = packet_len_table[cmd - 0x2af8];
-        if (packet_len == -1)
-        {
-            if (RFIFOREST(s) < 4)
-                return;
-            packet_len = RFIFOW(s, 2);
-        }
-        if (RFIFOREST(s) < packet_len)
-            return;
-
-        switch (cmd)
+        switch (packet_id)
         {
             case 0x2af9:
-                chrif_connectack(s);
+            {
+                Packet_Fixed<0x2af9> fixed;
+                rv = recv_fpacket<0x2af9, 3>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_connectack(s, fixed);
                 break;
+            }
             case 0x2afa:
-                ladmin_itemfrob(s);
+            {
+                Packet_Fixed<0x2afa> fixed;
+                rv = recv_fpacket<0x2afa, 10>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                ladmin_itemfrob(s, fixed);
                 break;
+            }
             case 0x2afb:
-                chrif_sendmapack(s);
+            {
+                Packet_Fixed<0x2afb> fixed;
+                rv = recv_fpacket<0x2afb, 27>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_sendmapack(s, fixed);
                 break;
+            }
             case 0x2afd:
             {
-                AccountId id = wrap<AccountId>(RFIFOL(s, 4));
-                int login_id2 = RFIFOL(s, 8);
-                TimeT connect_until_time = static_cast<time_t>(RFIFOL(s, 12));
-                short tmw_version = RFIFOW(s, 16);
-                CharKey st_key;
-                CharData st_data;
-                RFIFO_STRUCT(s, 18, st_key);
-                RFIFO_STRUCT(s, 18 + sizeof(st_key), st_data);
+                Packet_Payload<0x2afd> payload;
+                rv = recv_ppacket<0x2afd>(s, payload);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                AccountId id = payload.account_id;
+                int login_id2 = payload.login_id2;
+                TimeT connect_until_time = payload.connect_until;
+                short tmw_version = payload.packet_tmw_version;
+                CharKey st_key = payload.char_key;
+                CharData st_data = payload.char_data;
                 pc_authok(id, login_id2,
                         connect_until_time, tmw_version,
                         &st_key, &st_data);
-            }
                 break;
+            }
             case 0x2afe:
-                pc_authfail(wrap<AccountId>(RFIFOL(s, 2)));
+            {
+                Packet_Fixed<0x2afe> fixed;
+                rv = recv_fpacket<0x2afe, 6>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                pc_authfail(fixed.account_id);
                 break;
+            }
             case 0x2b00:
-                map_setusers(RFIFOL(s, 2));
+            {
+                Packet_Fixed<0x2b00> fixed;
+                rv = recv_fpacket<0x2b00, 6>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                map_setusers(fixed.users);
                 break;
+            }
             case 0x2b03:
-                clif_charselectok(wrap<BlockId>(RFIFOL(s, 2)));
+            {
+                Packet_Fixed<0x2b03> fixed;
+                rv = recv_fpacket<0x2b03, 7>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                clif_charselectok(account_to_block(fixed.account_id));
                 break;
+            }
             case 0x2b04:
-                chrif_recvmap(s);
+            {
+                Packet_Head<0x2b04> head;
+                std::vector<Packet_Repeat<0x2b04>> repeat;
+                rv = recv_vpacket<0x2b04, 10, 16>(s, head, repeat);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_recvmap(s, head, repeat);
                 break;
+            }
             case 0x2b06:
-                chrif_changemapserverack(s);
+            {
+                Packet_Fixed<0x2b06> fixed;
+                rv = recv_fpacket<0x2b06, 44>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_changemapserverack(s, fixed);
                 break;
+            }
             case 0x2b0b:
-                chrif_changedgm(s);
+            {
+                Packet_Fixed<0x2b0b> fixed;
+                rv = recv_fpacket<0x2b0b, 10>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_changedgm(s, fixed);
                 break;
+            }
             case 0x2b0d:
-                chrif_changedsex(s);
+            {
+                Packet_Fixed<0x2b0d> fixed;
+                rv = recv_fpacket<0x2b0d, 7>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_changedsex(s, fixed);
                 break;
+            }
             case 0x2b0f:
-                chrif_char_ask_name_answer(s);
+            {
+                Packet_Fixed<0x2b0f> fixed;
+                rv = recv_fpacket<0x2b0f, 34>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_char_ask_name_answer(s, fixed);
                 break;
+            }
             case 0x2b11:
-                chrif_accountreg2(s);
+            {
+                Packet_Head<0x2b11> head;
+                std::vector<Packet_Repeat<0x2b11>> repeat;
+                rv = recv_vpacket<0x2b11, 8, 36>(s, head, repeat);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_accountreg2(s, head, repeat);
                 break;
+            }
             case 0x2b12:
-                chrif_divorce(wrap<CharId>(RFIFOL(s, 2)), wrap<CharId>(RFIFOL(s, 6)));
+            {
+                Packet_Fixed<0x2b12> fixed;
+                rv = recv_fpacket<0x2b12, 10>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_divorce(fixed.char_id, fixed.partner_id);
                 break;
+            }
             case 0x2b13:
-                chrif_accountdeletion(s);
+            {
+                Packet_Fixed<0x2b13> fixed;
+                rv = recv_fpacket<0x2b13, 6>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_accountdeletion(s, fixed);
                 break;
+            }
             case 0x2b14:
-                chrif_accountban(s);
+            {
+                Packet_Fixed<0x2b14> fixed;
+                rv = recv_fpacket<0x2b14, 11>(s, fixed);
+                if (rv != RecvResult::Complete)
+                    break;
+
+                chrif_accountban(s, fixed);
                 break;
+            }
             case 0x2b15:
-                chrif_recvgmaccounts(s);
-                break;
+            {
+                std::vector<Packet_Repeat<0x2b15>> repeat;
+                rv = recv_packet_repeatonly<0x2b15, 4, 5>(s, repeat);
+                if (rv != RecvResult::Complete)
+                    break;
 
+                chrif_recvgmaccounts(s, repeat);
+                break;
+            }
             default:
+            {
+                RecvResult r = intif_parse(s, packet_id);
+
+                if (r == RecvResult::Complete)
+                    break;
+                if (r == RecvResult::Incomplete)
+                    return;
+
                 if (battle_config.error_log)
                     PRINTF("chrif_parse : unknown packet %d %d\n"_fmt, s,
-                            RFIFOW(s, 0));
+                            packet_id);
                 s->set_eof();
                 return;
+            }
         }
-        RFIFOSKIP(s, packet_len);
     }
+    if (rv == RecvResult::Error)
+        s->set_eof();
 }
 
 /*==========================================
@@ -1178,7 +1254,8 @@ void send_users_tochar(TimerData *, tick_t)
     if (!char_session)
         return;
 
-    WFIFOW(char_session, 0) = 0x2aff;
+    Packet_Head<0x2aff> head_ff;
+    std::vector<Packet_Repeat<0x2aff>> repeat_ff;
     for (io::FD i : iter_fds())
     {
         Session *s = get_session(i);
@@ -1190,13 +1267,13 @@ void send_users_tochar(TimerData *, tick_t)
                || sd->state.shroud_active
                || bool(sd->status.option & Option::HIDE)) && pc_isGM(sd)))
         {
-            WFIFOL(char_session, 6 + 4 * users) = unwrap<CharId>(sd->status_key.char_id);
-            users++;
+            Packet_Repeat<0x2aff> info;
+            info.char_id = sd->status_key.char_id;
+            repeat_ff.push_back(info);
         }
     }
-    WFIFOW(char_session, 2) = 6 + 4 * users;
-    WFIFOW(char_session, 4) = users;
-    WFIFOSET(char_session, 6 + 4 * users);
+    head_ff.users = users;
+    send_vpacket<0x2aff, 6, 4>(char_session, head_ff, repeat_ff);
 }
 
 /*==========================================
diff --git a/src/map/intif.cpp b/src/map/intif.cpp
index bb4b893..3ca2130 100644
--- a/src/map/intif.cpp
+++ b/src/map/intif.cpp
@@ -29,11 +29,13 @@
 
 #include "../io/cxxstdio.hpp"
 
+#include "../net/packets.hpp"
 #include "../net/socket.hpp"
-#include "../net/vomit.hpp"
 
 #include "../mmo/mmo.hpp"
 
+#include "../proto2/char-map.hpp"
+
 #include "battle.hpp"
 #include "chrif.hpp"
 #include "clif.hpp"
@@ -44,20 +46,6 @@
 
 #include "../poison.hpp"
 
-static
-const int packet_len_table[] =
-{
-    -1, -1, 27, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    -1, 7, 0, 0, 0, 0, 0, 0, -1, 11, 0, 0, 0, 0, 0, 0,
-    35, -1, 11, 15, 34, 29, 7, -1, 0, 0, 0, 0, 0, 0, 0, 0,
-    10, -1, 15, 0, 79, 19, 7, -1, 0, -1, -1, -1, 14, 67, 186, -1,
-    9, 9, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    11, -1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-};
-
 
 //-----------------------------------------------------------------
 // inter serverへの送信
@@ -65,11 +53,7 @@ const int packet_len_table[] =
 // Message for all GMs on all map servers
 void intif_GMmessage(XString mes)
 {
-    WFIFOW(char_session, 0) = 0x3000;
-    size_t len = mes.size() + 1;
-    WFIFOW(char_session, 2) = 4 + len;
-    WFIFO_STRING(char_session, 4, mes, len);
-    WFIFOSET(char_session, WFIFOW(char_session, 2));
+    send_packet_repeatonly<0x3000, 4, 1>(char_session, mes);
 }
 
 // The transmission of Wisp/Page to inter-server (player not found on this server)
@@ -77,13 +61,10 @@ void intif_wis_message(dumb_ptr<map_session_data> sd, CharName nick, ZString mes
 {
     nullpo_retv(sd);
 
-    size_t mes_len = mes.size() + 1;
-    WFIFOW(char_session, 0) = 0x3001;
-    WFIFOW(char_session, 2) = mes_len + 52;
-    WFIFO_STRING(char_session, 4, sd->status_key.name.to__actual(), 24);
-    WFIFO_STRING(char_session, 28, nick.to__actual(), 24);
-    WFIFO_STRING(char_session, 52, mes, mes_len);
-    WFIFOSET(char_session, WFIFOW(char_session, 2));
+    Packet_Head<0x3001> head_01;
+    head_01.from_char_name = sd->status_key.name;
+    head_01.to_char_name = nick;
+    send_vpacket<0x3001, 52, 1>(char_session, head_01, mes);
 
     if (battle_config.etc_log)
         PRINTF("intif_wis_message from %s to %s)\n"_fmt,
@@ -92,12 +73,12 @@ void intif_wis_message(dumb_ptr<map_session_data> sd, CharName nick, ZString mes
 
 // The reply of Wisp/page
 static
-void intif_wis_replay(int id, int flag)
+void intif_wis_replay(CharId id, int flag)
 {
-    WFIFOW(char_session, 0) = 0x3002;
-    WFIFOL(char_session, 2) = id;
-    WFIFOB(char_session, 6) = flag;    // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
-    WFIFOSET(char_session, 7);
+    Packet_Fixed<0x3002> fixed_02;
+    fixed_02.char_id = id;
+    fixed_02.flag = flag;    // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+    send_fpacket<0x3002, 7>(char_session, fixed_02);
 
     if (battle_config.etc_log)
         PRINTF("intif_wis_replay: id: %d, flag:%d\n"_fmt, id, flag);
@@ -106,13 +87,10 @@ void intif_wis_replay(int id, int flag)
 // The transmission of GM only Wisp/Page from server to inter-server
 void intif_wis_message_to_gm(CharName Wisp_name, GmLevel min_gm_level, ZString mes)
 {
-    size_t mes_len = mes.size() + 1;
-    WFIFOW(char_session, 0) = 0x3003;
-    WFIFOW(char_session, 2) = mes_len + 30;
-    WFIFO_STRING(char_session, 4, Wisp_name.to__actual(), 24);
-    WFIFOW(char_session, 28) = static_cast<uint16_t>(min_gm_level.get_all_bits());
-    WFIFO_STRING(char_session, 30, mes, mes_len);
-    WFIFOSET(char_session, WFIFOW(char_session, 2));
+    Packet_Head<0x3003> head_03;
+    head_03.char_name = Wisp_name;
+    head_03.min_gm_level = min_gm_level;
+    send_vpacket<0x3003, 30, 1>(char_session, head_03, mes);
 
     if (battle_config.etc_log)
         PRINTF("intif_wis_message_to_gm: from: '%s', min level: %d, message: '%s'.\n"_fmt,
@@ -122,20 +100,18 @@ void intif_wis_message_to_gm(CharName Wisp_name, GmLevel min_gm_level, ZString m
 // アカウント変数送信
 void intif_saveaccountreg(dumb_ptr<map_session_data> sd)
 {
-    int j, p;
-
     nullpo_retv(sd);
     assert (sd->status.account_reg_num < ACCOUNT_REG_NUM);
 
-    WFIFOW(char_session, 0) = 0x3004;
-    WFIFOL(char_session, 4) = unwrap<BlockId>(sd->bl_id);
-    for (j = 0, p = 8; j < sd->status.account_reg_num; j++, p += 36)
+    Packet_Head<0x3004> head_04;
+    head_04.account_id = block_to_account(sd->bl_id);
+    std::vector<Packet_Repeat<0x3004>> repeat_04(sd->status.account_reg_num);
+    for (size_t j = 0; j < sd->status.account_reg_num; j++)
     {
-        WFIFO_STRING(char_session, p, sd->status.account_reg[j].str, 32);
-        WFIFOL(char_session, p + 32) = sd->status.account_reg[j].value;
+        repeat_04[j].name = sd->status.account_reg[j].str;
+        repeat_04[j].value = sd->status.account_reg[j].value;
     }
-    WFIFOW(char_session, 2) = p;
-    WFIFOSET(char_session, p);
+    send_vpacket<0x3004, 8, 36>(char_session, head_04, repeat_04);
 }
 
 // アカウント変数要求
@@ -143,28 +119,27 @@ void intif_request_accountreg(dumb_ptr<map_session_data> sd)
 {
     nullpo_retv(sd);
 
-    WFIFOW(char_session, 0) = 0x3005;
-    WFIFOL(char_session, 2) = unwrap<BlockId>(sd->bl_id);
-    WFIFOSET(char_session, 6);
+    Packet_Fixed<0x3005> fixed_05;
+    fixed_05.account_id = block_to_account(sd->bl_id);
+    send_fpacket<0x3005, 6>(char_session, fixed_05);
 }
 
 // 倉庫データ要求
 void intif_request_storage(AccountId account_id)
 {
-    WFIFOW(char_session, 0) = 0x3010;
-    WFIFOL(char_session, 2) = unwrap<AccountId>(account_id);
-    WFIFOSET(char_session, 6);
+    Packet_Fixed<0x3010> fixed_10;
+    fixed_10.account_id = account_id;
+    send_fpacket<0x3010, 6>(char_session, fixed_10);
 }
 
 // 倉庫データ送信
 void intif_send_storage(Storage *stor)
 {
     nullpo_retv(stor);
-    WFIFOW(char_session, 0) = 0x3011;
-    WFIFOW(char_session, 2) = sizeof(Storage) + 8;
-    WFIFOL(char_session, 4) = unwrap<AccountId>(stor->account_id);
-    WFIFO_STRUCT(char_session, 8, *stor);
-    WFIFOSET(char_session, WFIFOW(char_session, 2));
+    Packet_Payload<0x3011> payload_11;
+    payload_11.account_id = stor->account_id;
+    payload_11.storage = *stor;
+    send_ppacket<0x3011>(char_session, payload_11);
 }
 
 // パーティ作成要求
@@ -172,21 +147,21 @@ void intif_create_party(dumb_ptr<map_session_data> sd, PartyName name)
 {
     nullpo_retv(sd);
 
-    WFIFOW(char_session, 0) = 0x3020;
-    WFIFOL(char_session, 2) = unwrap<AccountId>(sd->status_key.account_id);
-    WFIFO_STRING(char_session, 6, name, 24);
-    WFIFO_STRING(char_session, 30, sd->status_key.name.to__actual(), 24);
-    WFIFO_STRING(char_session, 54, sd->bl_m->name_, 16);
-    WFIFOW(char_session, 70) = sd->status.base_level;
-    WFIFOSET(char_session, 72);
+    Packet_Fixed<0x3020> fixed_20;
+    fixed_20.account_id = sd->status_key.account_id;
+    fixed_20.party_name = name;
+    fixed_20.char_name = sd->status_key.name;
+    fixed_20.map_name = sd->bl_m->name_;
+    fixed_20.level = sd->status.base_level;
+    send_fpacket<0x3020, 72>(char_session, fixed_20);
 }
 
 // パーティ情報要求
 void intif_request_partyinfo(PartyId party_id)
 {
-    WFIFOW(char_session, 0) = 0x3021;
-    WFIFOL(char_session, 2) = unwrap<PartyId>(party_id);
-    WFIFOSET(char_session, 6);
+    Packet_Fixed<0x3021> fixed_21;
+    fixed_21.party_id = party_id;
+    send_fpacket<0x3021, 6>(char_session, fixed_21);
 }
 
 // パーティ追加要求
@@ -196,34 +171,34 @@ void intif_party_addmember(PartyId party_id, AccountId account_id)
     sd = map_id2sd(account_to_block(account_id));
     if (sd != NULL)
     {
-        WFIFOW(char_session, 0) = 0x3022;
-        WFIFOL(char_session, 2) = unwrap<PartyId>(party_id);
-        WFIFOL(char_session, 6) = unwrap<AccountId>(account_id);
-        WFIFO_STRING(char_session, 10, sd->status_key.name.to__actual(), 24);
-        WFIFO_STRING(char_session, 34, sd->bl_m->name_, 16);
-        WFIFOW(char_session, 50) = sd->status.base_level;
-        WFIFOSET(char_session, 52);
+        Packet_Fixed<0x3022> fixed_22;
+        fixed_22.party_id = party_id;
+        fixed_22.account_id = account_id;
+        fixed_22.char_name = sd->status_key.name;
+        fixed_22.map_name = sd->bl_m->name_;
+        fixed_22.level = sd->status.base_level;
+        send_fpacket<0x3022, 52>(char_session, fixed_22);
     }
 }
 
 // パーティ設定変更
 void intif_party_changeoption(PartyId party_id, AccountId account_id, int exp, int item)
 {
-    WFIFOW(char_session, 0) = 0x3023;
-    WFIFOL(char_session, 2) = unwrap<PartyId>(party_id);
-    WFIFOL(char_session, 6) = unwrap<AccountId>(account_id);
-    WFIFOW(char_session, 10) = exp;
-    WFIFOW(char_session, 12) = item;
-    WFIFOSET(char_session, 14);
+    Packet_Fixed<0x3023> fixed_23;
+    fixed_23.party_id = party_id;
+    fixed_23.account_id = account_id;
+    fixed_23.exp = exp;
+    fixed_23.item = item;
+    send_fpacket<0x3023, 14>(char_session, fixed_23);
 }
 
 // パーティ脱退要求
 void intif_party_leave(PartyId party_id, AccountId account_id)
 {
-    WFIFOW(char_session, 0) = 0x3024;
-    WFIFOL(char_session, 2) = unwrap<PartyId>(party_id);
-    WFIFOL(char_session, 6) = unwrap<AccountId>(account_id);
-    WFIFOSET(char_session, 10);
+    Packet_Fixed<0x3024> fixed_24;
+    fixed_24.party_id = party_id;
+    fixed_24.account_id = account_id;
+    send_fpacket<0x3024, 10>(char_session, fixed_24);
 }
 
 // パーティ移動要求
@@ -231,36 +206,33 @@ void intif_party_changemap(dumb_ptr<map_session_data> sd, int online)
 {
     if (sd != NULL)
     {
-        WFIFOW(char_session, 0) = 0x3025;
-        WFIFOL(char_session, 2) = unwrap<PartyId>(sd->status.party_id);
-        WFIFOL(char_session, 6) = unwrap<AccountId>(sd->status_key.account_id);
-        WFIFO_STRING(char_session, 10, sd->bl_m->name_, 16);
-        WFIFOB(char_session, 26) = online;
-        WFIFOW(char_session, 27) = sd->status.base_level;
-        WFIFOSET(char_session, 29);
+        Packet_Fixed<0x3025> fixed_25;
+        fixed_25.party_id = sd->status.party_id;
+        fixed_25.account_id = sd->status_key.account_id;
+        fixed_25.map_name = sd->bl_m->name_;
+        fixed_25.online = online;
+        fixed_25.level = sd->status.base_level;
+        send_fpacket<0x3025, 29>(char_session, fixed_25);
     }
 }
 
 // パーティ会話送信
 void intif_party_message(PartyId party_id, AccountId account_id, XString mes)
 {
-    size_t len = mes.size() + 1;
-    WFIFOW(char_session, 0) = 0x3027;
-    WFIFOW(char_session, 2) = len + 12;
-    WFIFOL(char_session, 4) = unwrap<PartyId>(party_id);
-    WFIFOL(char_session, 8) = unwrap<AccountId>(account_id);
-    WFIFO_STRING(char_session, 12, mes, len);
-    WFIFOSET(char_session, len + 12);
+    Packet_Head<0x3027> head_27;
+    head_27.party_id = party_id;
+    head_27.account_id = account_id;
+    send_vpacket<0x3027, 12, 1>(char_session, head_27, mes);
 }
 
 // パーティ競合チェック要求
 void intif_party_checkconflict(PartyId party_id, AccountId account_id, CharName nick)
 {
-    WFIFOW(char_session, 0) = 0x3028;
-    WFIFOL(char_session, 2) = unwrap<PartyId>(party_id);
-    WFIFOL(char_session, 6) = unwrap<AccountId>(account_id);
-    WFIFO_STRING(char_session, 10, nick.to__actual(), 24);
-    WFIFOSET(char_session, 34);
+    Packet_Fixed<0x3028> fixed_28;
+    fixed_28.party_id = party_id;
+    fixed_28.account_id = account_id;
+    fixed_28.char_name = nick;
+    send_fpacket<0x3028, 34>(char_session, fixed_28);
 }
 
 //-----------------------------------------------------------------
@@ -268,21 +240,18 @@ void intif_party_checkconflict(PartyId party_id, AccountId account_id, CharName
 
 // Wisp/Page reception
 static
-int intif_parse_WisMessage(Session *s)
+int intif_parse_WisMessage(Session *, const Packet_Head<0x3801>& head, AString& buf)
 {
     // rewritten by [Yor]
     dumb_ptr<map_session_data> sd;
 
-    CharName from = stringish<CharName>(RFIFO_STRING<24>(s, 8));
-    CharName to = stringish<CharName>(RFIFO_STRING<24>(s, 32));
-
-    size_t len = RFIFOW(s, 2) - 56;
-    AString buf = RFIFO_STRING(s, 56, len);
+    CharName from = head.src_char_name;
+    CharName to = head.dst_char_name;
 
     if (battle_config.etc_log)
     {
         PRINTF("intif_parse_wismessage: id: %d, from: %s, to: %s\n"_fmt,
-                RFIFOL(s, 4),
+                head.whisper_id,
                 from,
                 to);
     }
@@ -294,23 +263,23 @@ int intif_parse_WisMessage(Session *s)
             // if source player not found in ignore list
             {
                 clif_wis_message(sd->sess, from, buf);
-                intif_wis_replay(RFIFOL(s, 4), 0);   // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+                intif_wis_replay(head.whisper_id, 0);   // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
             }
         }
     }
     else
-        intif_wis_replay(RFIFOL(s, 4), 1);   // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+        intif_wis_replay(head.whisper_id, 1);   // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
     return 0;
 }
 
 // Wisp/page transmission result reception
 static
-int intif_parse_WisEnd(Session *s)
+int intif_parse_WisEnd(Session *, const Packet_Fixed<0x3802>& fixed)
 {
     dumb_ptr<map_session_data> sd;
 
-    CharName name = stringish<CharName>(RFIFO_STRING<24>(s, 2));
-    uint8_t flag = RFIFOB(s, 26);
+    CharName name = fixed.sender_char_name;
+    uint8_t flag = fixed.flag;
     if (battle_config.etc_log)
         // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
         PRINTF("intif_parse_wisend: player: %s, flag: %d\n"_fmt,
@@ -324,17 +293,11 @@ int intif_parse_WisEnd(Session *s)
 
 // Received wisp message from map-server via char-server for ALL gm
 static
-void mapif_parse_WisToGM(Session *s)
+void mapif_parse_WisToGM(Session *, const Packet_Head<0x3803>& head, AString& message)
 {
     // 0x3003/0x3803 <packet_len>.w <wispname>.24B <min_gm_level>.w <message>.?B
-    if (RFIFOW(s, 2) - 30 <= 0)
-        return;
-
-    int len = RFIFOW(s, 2) - 30;
-
-    GmLevel min_gm_level = GmLevel::from(static_cast<uint32_t>(RFIFOW(s, 28)));
-    CharName Wisp_name = stringish<CharName>(RFIFO_STRING<24>(s, 4));
-    AString message = RFIFO_STRING(s, 30, len);
+    GmLevel min_gm_level = head.min_gm_level;
+    CharName Wisp_name = head.char_name;
     // information is sended to all online GM
     for (io::FD i : iter_fds())
     {
@@ -352,39 +315,39 @@ void mapif_parse_WisToGM(Session *s)
 
 // アカウント変数通知
 static
-int intif_parse_AccountReg(Session *s)
+int intif_parse_AccountReg(Session *, const Packet_Head<0x3804>& head, const std::vector<Packet_Repeat<0x3804>>& repeat)
 {
-    int j, p;
-    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(wrap<AccountId>(RFIFOL(s, 4))));
+    dumb_ptr<map_session_data> sd = map_id2sd(account_to_block(head.account_id));
     if (sd == NULL)
         return 1;
-    for (p = 8, j = 0; p < RFIFOW(s, 2) && j < ACCOUNT_REG_NUM;
-         p += 36, j++)
+
+    size_t jlim = std::min(ACCOUNT_REG_NUM, repeat.size());
+    for (size_t j = 0; j < jlim; j++)
     {
-        sd->status.account_reg[j].str = stringish<VarName>(RFIFO_STRING<32>(s, p));
-        sd->status.account_reg[j].value = RFIFOL(s, p + 32);
+        sd->status.account_reg[j].str = repeat[j].name;
+        sd->status.account_reg[j].value = repeat[j].value;
     }
-    sd->status.account_reg_num = j;
+    sd->status.account_reg_num = jlim;
 
     return 0;
 }
 
 // 倉庫データ受信
 static
-int intif_parse_LoadStorage(Session *s)
+int intif_parse_LoadStorage(Session *, const Packet_Payload<0x3810>& payload)
 {
     Storage *stor;
     dumb_ptr<map_session_data> sd;
 
-    sd = map_id2sd(account_to_block(wrap<AccountId>(RFIFOL(s, 4))));
+    sd = map_id2sd(account_to_block(payload.account_id));
     if (sd == NULL)
     {
         if (battle_config.error_log)
             PRINTF("intif_parse_LoadStorage: user not found %d\n"_fmt,
-                    RFIFOL(s, 4));
+                    payload.account_id);
         return 1;
     }
-    stor = account2storage(wrap<AccountId>(RFIFOL(s, 4)));
+    stor = account2storage(payload.account_id);
     if (stor->storage_status == 1)
     {                           // Already open.. lets ignore this update
         if (battle_config.error_log)
@@ -400,16 +363,9 @@ int intif_parse_LoadStorage(Session *s)
         return 1;
     }
 
-    if (RFIFOW(s, 2) - 8 != sizeof(Storage))
-    {
-        if (battle_config.error_log)
-            PRINTF("intif_parse_LoadStorage: data size error %d %zu\n"_fmt,
-                    RFIFOW(s, 2) - 8, sizeof(Storage));
-        return 1;
-    }
     if (battle_config.save_log)
-        PRINTF("intif_openstorage: %d\n"_fmt, RFIFOL(s, 4));
-    RFIFO_STRUCT(s, 8, *stor);
+        PRINTF("intif_openstorage: %d\n"_fmt, payload.account_id);
+    *stor = payload.storage;
     stor->dirty = 0;
     stor->storage_status = 1;
     sd->state.storage_open = 1;
@@ -422,50 +378,41 @@ int intif_parse_LoadStorage(Session *s)
 
 // 倉庫データ送信成功
 static
-void intif_parse_SaveStorage(Session *s)
+void intif_parse_SaveStorage(Session *, const Packet_Fixed<0x3811>& fixed)
 {
     if (battle_config.save_log)
-        PRINTF("intif_savestorage: done %d %d\n"_fmt, RFIFOL(s, 2),
-                RFIFOB(s, 6));
-    storage_storage_saved(wrap<AccountId>(RFIFOL(s, 2)));
+        PRINTF("intif_savestorage: done %d %d\n"_fmt, fixed.account_id,
+                fixed.unknown);
+    storage_storage_saved(fixed.account_id);
 }
 
 // パーティ作成可否
 static
-void intif_parse_PartyCreated(Session *s)
+void intif_parse_PartyCreated(Session *, const Packet_Fixed<0x3820>& fixed)
 {
     if (battle_config.etc_log)
         PRINTF("intif: party created\n"_fmt);
-    AccountId account_id = wrap<AccountId>(RFIFOL(s, 2));
-    int fail = RFIFOB(s, 6);
-    PartyId party_id = wrap<PartyId>(RFIFOL(s, 7));
-    PartyName name = stringish<PartyName>(RFIFO_STRING<24>(s, 11));
+    AccountId account_id = fixed.account_id;
+    int fail = fixed.error;
+    PartyId party_id = fixed.party_id;
+    PartyName name = fixed.party_name;
     party_created(account_id, fail, party_id, name);
 }
 
 // パーティ情報
 static
-void intif_parse_PartyInfo(Session *s)
+void intif_parse_PartyInfo(Session *, const Packet_Head<0x3821>& head, bool has_opt, const Packet_Option<0x3821>& option)
 {
-    if (RFIFOW(s, 2) == 8)
+    if (!has_opt)
     {
         if (battle_config.error_log)
-            PRINTF("intif: party noinfo %d\n"_fmt, RFIFOL(s, 4));
-        party_recv_noinfo(wrap<PartyId>(RFIFOL(s, 4)));
+            PRINTF("intif: party noinfo %d\n"_fmt, head.party_id);
+        party_recv_noinfo(head.party_id);
         return;
     }
 
-    if (RFIFOW(s, 2) != sizeof(PartyMost) + 8)
-    {
-        if (battle_config.error_log)
-            PRINTF("intif: party info : data size error %d %d %zu\n"_fmt,
-                    RFIFOL(s, 4), RFIFOW(s, 2),
-                    sizeof(PartyMost) + 8);
-    }
-    PartyId pi;
-    PartyMost pm;
-    RFIFO_STRUCT(s, 4, pi);
-    RFIFO_STRUCT(s, 8, pm);
+    PartyId pi = head.party_id;
+    PartyMost pm = option.party_most;
     PartyPair pp;
     pp.party_id = pi;
     pp.party_most = &pm;
@@ -474,29 +421,29 @@ void intif_parse_PartyInfo(Session *s)
 
 // パーティ追加通知
 static
-void intif_parse_PartyMemberAdded(Session *s)
+void intif_parse_PartyMemberAdded(Session *, const Packet_Fixed<0x3822>& fixed)
 {
     if (battle_config.etc_log)
-        PRINTF("intif: party member added %d %d %d\n"_fmt, RFIFOL(s, 2),
-                RFIFOL(s, 6), RFIFOB(s, 10));
-    party_member_added(wrap<PartyId>(RFIFOL(s, 2)), wrap<AccountId>(RFIFOL(s, 6)), RFIFOB(s, 10));
+        PRINTF("intif: party member added %d %d %d\n"_fmt, fixed.party_id,
+                fixed.account_id, fixed.flag);
+    party_member_added(fixed.party_id, fixed.account_id, fixed.flag);
 }
 
 // パーティ設定変更通知
 static
-void intif_parse_PartyOptionChanged(Session *s)
+void intif_parse_PartyOptionChanged(Session *, const Packet_Fixed<0x3823>& fixed)
 {
-    party_optionchanged(wrap<PartyId>(RFIFOL(s, 2)), wrap<AccountId>(RFIFOL(s, 6)), RFIFOW(s, 10),
-                         RFIFOW(s, 12), RFIFOB(s, 14));
+    party_optionchanged(fixed.party_id, fixed.account_id, fixed.exp,
+            fixed.item, fixed.flag);
 }
 
 // パーティ脱退通知
 static
-void intif_parse_PartyMemberLeaved(Session *s)
+void intif_parse_PartyMemberLeaved(Session *, const Packet_Fixed<0x3824>& fixed)
 {
-    PartyId party_id = wrap<PartyId>(RFIFOL(s, 2));
-    AccountId account_id = wrap<AccountId>(RFIFOL(s, 6));
-    CharName name = stringish<CharName>(RFIFO_STRING<24>(s, 10));
+    PartyId party_id = fixed.party_id;
+    AccountId account_id = fixed.account_id;
+    CharName name = fixed.char_name;
     if (battle_config.etc_log)
         PRINTF("intif: party member leaved %d %d %s\n"_fmt,
                 party_id, account_id, name);
@@ -505,118 +452,198 @@ void intif_parse_PartyMemberLeaved(Session *s)
 
 // パーティ解散通知
 static
-void intif_parse_PartyBroken(Session *s)
+void intif_parse_PartyBroken(Session *, const Packet_Fixed<0x3826>& fixed)
 {
-    party_broken(wrap<PartyId>(RFIFOL(s, 2)));
+    party_broken(fixed.party_id);
 }
 
 // パーティ移動通知
 static
-void intif_parse_PartyMove(Session *s)
+void intif_parse_PartyMove(Session *, const Packet_Fixed<0x3825>& fixed)
 {
-    PartyId party_id = wrap<PartyId>(RFIFOL(s, 2));
-    AccountId account_id = wrap<AccountId>(RFIFOL(s, 6));
-    MapName map = stringish<MapName>(RFIFO_STRING<16>(s, 10));
-    uint8_t online = RFIFOB(s, 26);
-    uint16_t lv = RFIFOW(s, 27);
+    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;
     party_recv_movemap(party_id, account_id, map, online, lv);
 }
 
 // パーティメッセージ
 static
-void intif_parse_PartyMessage(Session *s)
+void intif_parse_PartyMessage(Session *, const Packet_Head<0x3827>& head, AString& buf)
 {
-    size_t len = RFIFOW(s, 2) - 12;
-    AString buf = RFIFO_STRING(s, 12, len);
-    party_recv_message(wrap<PartyId>(RFIFOL(s, 4)), wrap<AccountId>(RFIFOL(s, 8)), buf);
+    party_recv_message(head.party_id, head.account_id, buf);
 }
 
 //-----------------------------------------------------------------
 // inter serverからの通信
 // エラーがあれば0(false)を返すこと
 // パケットが処理できれば1,パケット長が足りなければ2を返すこと
-int intif_parse(Session *s)
+RecvResult intif_parse(Session *s, uint16_t packet_id)
 {
-    int packet_len;
-    int cmd = RFIFOW(s, 0);
-    // パケットのID確認
-    if (cmd < 0x3800
-        || cmd >=
-        0x3800 + (sizeof(packet_len_table) / sizeof(packet_len_table[0]))
-        || packet_len_table[cmd - 0x3800] == 0)
-    {
-        return 0;
-    }
-    // パケットの長さ確認
-    packet_len = packet_len_table[cmd - 0x3800];
-    if (packet_len == -1)
-    {
-        if (RFIFOREST(s) < 4)
-            return 2;
-        packet_len = RFIFOW(s, 2);
-    }
-    if (RFIFOREST(s) < packet_len)
-    {
-        return 2;
-    }
-    // 処理分岐
-    switch (cmd)
+    RecvResult rv;
+
+    switch (packet_id)
     {
         case 0x3800:
         {
-            AString mes = RFIFO_STRING(s, 4, packet_len - 4);
+            AString mes;
+            rv = recv_packet_repeatonly<0x3800, 4, 1>(s, mes);
+            if (rv != RecvResult::Complete)
+                return rv;
+
             clif_GMmessage(NULL, mes, 0);
-        }
             break;
+        }
         case 0x3801:
-            intif_parse_WisMessage(s);
+        {
+            Packet_Head<0x3801> head;
+            AString repeat;
+            rv = recv_vpacket<0x3801, 56, 1>(s, head, repeat);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_WisMessage(s, head, repeat);
             break;
+        }
         case 0x3802:
-            intif_parse_WisEnd(s);
+        {
+            Packet_Fixed<0x3802> fixed;
+            rv = recv_fpacket<0x3802, 27>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_WisEnd(s, fixed);
             break;
+        }
         case 0x3803:
-            mapif_parse_WisToGM(s);
+        {
+            Packet_Head<0x3803> head;
+            AString repeat;
+            rv = recv_vpacket<0x3803, 30, 1>(s, head, repeat);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            mapif_parse_WisToGM(s, head, repeat);
             break;
+        }
         case 0x3804:
-            intif_parse_AccountReg(s);
+        {
+            Packet_Head<0x3804> head;
+            std::vector<Packet_Repeat<0x3804>> repeat;
+            rv = recv_vpacket<0x3804, 8, 36>(s, head, repeat);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_AccountReg(s, head, repeat);
             break;
+        }
         case 0x3810:
-            intif_parse_LoadStorage(s);
+        {
+            Packet_Payload<0x3810> payload;
+            rv = recv_ppacket<0x3810>(s, payload);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_LoadStorage(s, payload);
             break;
+        }
         case 0x3811:
-            intif_parse_SaveStorage(s);
+        {
+            Packet_Fixed<0x3811> fixed;
+            rv = recv_fpacket<0x3811, 7>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_SaveStorage(s, fixed);
             break;
+        }
         case 0x3820:
-            intif_parse_PartyCreated(s);
+        {
+            Packet_Fixed<0x3820> fixed;
+            rv = recv_fpacket<0x3820, 35>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyCreated(s, fixed);
             break;
+        }
         case 0x3821:
-            intif_parse_PartyInfo(s);
+        {
+            Packet_Head<0x3821> head;
+            bool has_opt;
+            Packet_Option<0x3821> option;
+            rv = recv_opacket<0x3821, 8, sizeof(NetPacket_Option<0x3821>)>(s, head, &has_opt, option);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyInfo(s, head, has_opt, option);
             break;
+        }
         case 0x3822:
-            intif_parse_PartyMemberAdded(s);
+        {
+            Packet_Fixed<0x3822> fixed;
+            rv = recv_fpacket<0x3822, 11>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyMemberAdded(s, fixed);
             break;
+        }
         case 0x3823:
-            intif_parse_PartyOptionChanged(s);
+        {
+            Packet_Fixed<0x3823> fixed;
+            rv = recv_fpacket<0x3823, 15>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyOptionChanged(s, fixed);
             break;
+        }
         case 0x3824:
-            intif_parse_PartyMemberLeaved(s);
+        {
+            Packet_Fixed<0x3824> fixed;
+            rv = recv_fpacket<0x3824, 34>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyMemberLeaved(s, fixed);
             break;
+        }
         case 0x3825:
-            intif_parse_PartyMove(s);
+        {
+            Packet_Fixed<0x3825> fixed;
+            rv = recv_fpacket<0x3825, 29>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyMove(s, fixed);
             break;
+        }
         case 0x3826:
-            intif_parse_PartyBroken(s);
+        {
+            Packet_Fixed<0x3826> fixed;
+            rv = recv_fpacket<0x3826, 7>(s, fixed);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyBroken(s, fixed);
             break;
+        }
         case 0x3827:
-            intif_parse_PartyMessage(s);
+        {
+            Packet_Head<0x3827> head;
+            AString repeat;
+            rv = recv_vpacket<0x3827, 12, 1>(s, head, repeat);
+            if (rv != RecvResult::Complete)
+                return rv;
+
+            intif_parse_PartyMessage(s, head, repeat);
             break;
+        }
         default:
-            if (battle_config.error_log)
-                PRINTF("intif_parse : unknown packet %d %x\n"_fmt, s,
-                        RFIFOW(s, 0));
-            return 0;
+            return RecvResult::Error;
     }
-    // パケット読み飛ばし
-    RFIFOSKIP(s, packet_len);
-    return 1;
+    return rv;
 }
diff --git a/src/map/intif.hpp b/src/map/intif.hpp
index 2e3d3fe..44bd8eb 100644
--- a/src/map/intif.hpp
+++ b/src/map/intif.hpp
@@ -31,7 +31,7 @@
 
 # include "../mmo/fwd.hpp"
 
-int intif_parse(Session *);
+RecvResult intif_parse(Session *, uint16_t packet_id);
 
 void intif_GMmessage(XString mes);
 
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index 33cb57f..e3a672e 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -40,10 +40,11 @@
 #include "../io/read.hpp"
 
 #include "../net/timer.hpp"
-#include "../net/vomit.hpp"
 
 #include "../mmo/utils.hpp"
 
+#include "../proto2/char-map.hpp"
+
 #include "atcommand.hpp"
 #include "battle.hpp"
 #include "chrif.hpp"
@@ -2852,7 +2853,7 @@ int pc_attack(dumb_ptr<map_session_data> sd, BlockId target_id, int type)
 
     if (bl->bl_type == BL::NPC)
     {                           // monster npcs [Valaris]
-        npc_click(sd, wrap<BlockId>(RFIFOL(sd->sess, 2)));
+        npc_click(sd, target_id);
         return 0;
     }
 
@@ -5176,15 +5177,14 @@ void pc_autosave(TimerData *, tick_t)
     ).detach();
 }
 
-int pc_read_gm_account(Session *s)
+int pc_read_gm_account(Session *, const std::vector<Packet_Repeat<0x2b15>>& repeat)
 {
     gm_accountm.clear();
 
-    // (RFIFOW(fd, 2) - 4) / 5
-    for (int i = 4; i < RFIFOW(s, 2); i += 5)
+    for (const auto& i : repeat)
     {
-        AccountId account_id = wrap<AccountId>(RFIFOL(s, i));
-        GmLevel level = GmLevel::from(static_cast<uint32_t>(RFIFOB(s, i + 4)));
+        AccountId account_id = i.account_id;
+        GmLevel level = i.gm_level;
         gm_accountm[account_id] = level;
     }
     return gm_accountm.size();
diff --git a/src/map/pc.hpp b/src/map/pc.hpp
index 10c11cf..53e5c1d 100644
--- a/src/map/pc.hpp
+++ b/src/map/pc.hpp
@@ -31,6 +31,8 @@
 
 # include "../mmo/utils.hpp"
 
+# include "../proto2/fwd.hpp"
+
 # include "clif.t.hpp"
 # include "map.hpp"
 
@@ -172,7 +174,7 @@ void pc_set_gm_level(AccountId account_id, GmLevel level);
 void pc_setstand(dumb_ptr<map_session_data> sd);
 void pc_cleanup(dumb_ptr<map_session_data> sd);  // [Fate] Clean up after a logged-out PC
 
-int pc_read_gm_account(Session *);
+int pc_read_gm_account(Session *, const std::vector<Packet_Repeat<0x2b15>>&);
 int pc_setinvincibletimer(dumb_ptr<map_session_data> sd, interval_t);
 int pc_delinvincibletimer(dumb_ptr<map_session_data> sd);
 int pc_logout(dumb_ptr<map_session_data> sd);   // [fate] Player logs out
diff --git a/src/net/packets.hpp b/src/net/packets.hpp
index 54167e8..4bb412f 100644
--- a/src/net/packets.hpp
+++ b/src/net/packets.hpp
@@ -188,7 +188,7 @@ RecvResult net_recv_opacket(Session *s, NetPacket_Head<id>& head, bool *has_opt,
         if (nat.magic_packet_length < sizeof(NetPacket_Head<id>))
             return RecvResult::Error;
         size_t bytes_repeat = nat.magic_packet_length - sizeof(NetPacket_Head<id>);
-        if (bytes_repeat % sizeof(NetPacket_Repeat<id>))
+        if (bytes_repeat % sizeof(NetPacket_Option<id>))
             return RecvResult::Error;
         size_t has_opt_pls = bytes_repeat / sizeof(NetPacket_Option<id>);
         if (has_opt_pls > 1)
-- 
cgit v1.2.3-70-g09d2