From fcf31a258f2925650cf51f15d0280c0efb67c6a2 Mon Sep 17 00:00:00 2001
From: mekolat <mekolat@users.noreply.github.com>
Date: Sun, 10 Apr 2016 14:14:51 -0400
Subject: consolidate client version handling

---
 src/char/char.cpp      |  25 +++-----
 src/char/char.hpp      |   2 +-
 src/ints/wrap.hpp      |  15 +++++
 src/login/login.cpp    |  15 +++--
 src/login/login.hpp    |   1 +
 src/map/chrif.cpp      |   2 +-
 src/map/clif.cpp       | 168 ++++++++++++++++++++++++++-----------------------
 src/map/map.hpp        |   2 +-
 src/map/pc.cpp         |   4 +-
 src/map/pc.hpp         |   2 +-
 src/map/script-fun.cpp |   2 +-
 src/mmo/fwd.hpp        |   1 +
 src/mmo/ids.hpp        |   1 +
 src/mmo/ids.py         |   1 +
 src/mmo/version.hpp    |   8 ++-
 tools/protocol.py      |  23 ++++---
 16 files changed, 152 insertions(+), 120 deletions(-)

diff --git a/src/char/char.cpp b/src/char/char.cpp
index 1dc0e90..3767c54 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -99,7 +99,7 @@ struct char_session_data : SessionData
     AccountId account_id;
     int login_id1, login_id2;
     SEX sex;
-    unsigned short packet_client_version;
+    ClientVersion client_version;
     AccountEmail email;
 };
 } // namespace char_
@@ -1193,10 +1193,9 @@ void parse_tologin(Session *ls)
             case 0x2713:
             {
                 Packet_Fixed<0x2713> fixed;
-                rv = recv_fpacket<0x2713, 51>(ls, fixed);
+                rv = recv_fpacket<0x2713, 55>(ls, fixed);
                 if (rv != RecvResult::Complete)
                     break;
-
                 for (io::FD i : iter_fds())
                 {
                     AccountId acc = fixed.account_id;
@@ -1218,6 +1217,7 @@ void parse_tologin(Session *ls)
                             sd->email = stringish<AccountEmail>(fixed.email);
                             if (!e_mail_check(sd->email))
                                 sd->email = DEFAULT_EMAIL;
+                            sd->client_version = fixed.client_protocol_version;
                             // send characters to player
                             mmo_char_send006b(s2, sd);
                         }
@@ -1689,10 +1689,10 @@ void parse_frommap(Session *ms)
                         Packet_Payload<0x2afd> payload_fd; // not file descriptor
                         payload_fd.account_id = account_id;
                         payload_fd.login_id2 = afi.login_id2;
-                        payload_fd.packet_client_version = afi.packet_client_version;
+                        payload_fd.client_protocol_version = afi.client_version;
                         FPRINTF(stderr,
                                 "From queue index %zd: recalling packet version %d\n"_fmt,
-                                (&afi - &auth_fifo.front()), afi.packet_client_version);
+                                (&afi - &auth_fifo.front()), afi.client_version);
                         payload_fd.char_key = *ck;
                         payload_fd.char_data = *cd;
                         send_ppacket<0x2afd>(ms, payload_fd);
@@ -2178,7 +2178,7 @@ void handle_x0066(Session *s, struct char_session_data *sd, uint8_t rfifob_2, IP
             auth_fifo_iter->delflag = 0;
             auth_fifo_iter->sex = sd->sex;
             auth_fifo_iter->ip = s->client_ip;
-            auth_fifo_iter->packet_client_version = sd->packet_client_version;
+            auth_fifo_iter->client_version = sd->client_version;
             auth_fifo_iter++;
         }
     }
@@ -2249,7 +2249,6 @@ void parse_char(Session *s)
                     sd->account_id = account_id;
                     sd->login_id1 = fixed.login_id1;
                     sd->login_id2 = fixed.login_id2;
-                    sd->packet_client_version = fixed.packet_client_version;
                     sd->sex = fixed.sex;
 
                     // formerly: send back account_id
@@ -2257,14 +2256,6 @@ void parse_char(Session *s)
                     special.magic_packet_length = 4;
                     send_ppacket<0x8000>(s, special);
 
-                    if(sd->packet_client_version < MIN_CLIENT_VERSION)
-                    {
-                        Packet_Fixed<0x006a> fixed_6a;
-                        fixed_6a.error_code = 5;
-                        send_fpacket<0x006a, 23>(s, fixed_6a);
-                        goto x65_out;
-                    }
-
                     // search authentification
                     for (AuthFifoEntry& afi : auth_fifo)
                     {
@@ -2285,9 +2276,7 @@ void parse_char(Session *s)
                                     fixed_16.account_id = sd->account_id;
                                     send_fpacket<0x2716, 6>(login_session, fixed_16);
                                 }
-                                // Record client version
-                                afi.packet_client_version =
-                                    sd->packet_client_version;
+
                                 // send characters to player
                                 mmo_char_send006b(s, sd);
                             }
diff --git a/src/char/char.hpp b/src/char/char.hpp
index 049875b..a771730 100644
--- a/src/char/char.hpp
+++ b/src/char/char.hpp
@@ -50,7 +50,7 @@ struct AuthFifoEntry
     IP4Address ip;
     int delflag;
     SEX sex;
-    unsigned short packet_client_version;
+    ClientVersion client_version;
 };
 
 struct mmo_map_server
diff --git a/src/ints/wrap.hpp b/src/ints/wrap.hpp
index 25b03c1..2c8cd20 100644
--- a/src/ints/wrap.hpp
+++ b/src/ints/wrap.hpp
@@ -62,6 +62,21 @@ namespace ints
         {
             return l._value < r._value;
         }
+        template<class W, typename=typename W::wrapped_type>
+        bool operator > (W l, W r)
+        {
+            return l._value > r._value;
+        }
+        template<class W, typename=typename W::wrapped_type>
+        bool operator <= (W l, W r)
+        {
+            return l._value <= r._value;
+        }
+        template<class W, typename=typename W::wrapped_type>
+        bool operator >= (W l, W r)
+        {
+            return l._value >= r._value;
+        }
 
         template<class T>
         struct Sub : T
diff --git a/src/login/login.cpp b/src/login/login.cpp
index c071429..7ae95c7 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -114,6 +114,8 @@ bool extract(XString str, login::ACO *aco)
 }
 namespace login
 {
+#define VERSION_2_UPDATEHOST (1<<0)
+
 struct mmo_account
 {
     AccountName userid;
@@ -908,8 +910,9 @@ void parse_fromchar(Session *s)
                                     fixed_13.account_id = acc;
                                     fixed_13.invalid = 0;
                                     fixed_13.email = ad.email;
+                                    fixed_13.client_protocol_version = auth_fifo[i].client_version;
 
-                                    send_fpacket<0x2713, 51>(s, fixed_13);
+                                    send_fpacket<0x2713, 55>(s, fixed_13);
                                     break;
                                 }
                             }
@@ -928,7 +931,7 @@ void parse_fromchar(Session *s)
                         // fixed_13.email
                         // fixed_13.connect_until
 
-                        send_fpacket<0x2713, 51>(s, fixed_13);
+                        send_fpacket<0x2713, 55>(s, fixed_13);
                     }
                 }
                 break;
@@ -2441,7 +2444,7 @@ void parse_login(Session *s)
                 result = mmo_auth(&account, s);
                 if (result == -1)
                 {
-                    if (fixed.version < MIN_CLIENT_VERSION)
+                    if (fixed.client_protocol_version < wrap<ClientVersion>(MIN_CLIENT_VERSION))
                         result = 5; // client too old
                 }
                 if (result == -1)
@@ -2478,7 +2481,7 @@ void parse_login(Session *s)
                          *
                          * All supported clients now send both, so the check is removed.
                          */
-                        // if (version_2 & VERSION_2_UPDATEHOST)
+                        if (fixed.flags & VERSION_2_UPDATEHOST)
                         {
                             if (login_conf.update_host)
                             {
@@ -2488,7 +2491,7 @@ void parse_login(Session *s)
 
                         // Load list of char servers into outbound packet
                         std::vector<Packet_Repeat<0x0069>> repeat_69;
-                        // if (version_2 & VERSION_2_SERVERORDER)
+                        //if (fixed.flags & VERSION_2_SERVERORDER)
                         for (int i = 0; i < MAX_SERVERS; i++)
                         {
                             if (server_session[i])
@@ -2531,6 +2534,8 @@ void parse_login(Session *s)
                             auth_fifo[auth_fifo_pos].delflag = 0;
                             auth_fifo[auth_fifo_pos].ip =
                                 s->client_ip;
+                            auth_fifo[auth_fifo_pos].client_version =
+                                fixed.client_protocol_version;
                             auth_fifo_pos++;
                             // if no char-server, don't send void list of servers, just disconnect the player with proper message
                         }
diff --git a/src/login/login.hpp b/src/login/login.hpp
index ba42bae..ed44e68 100644
--- a/src/login/login.hpp
+++ b/src/login/login.hpp
@@ -79,6 +79,7 @@ struct AuthFifo
     IP4Address ip;
     SEX sex;
     int delflag;
+    ClientVersion client_version;
 };
 } // namespace login
 } // namespace tmwa
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index 7421344..708cbe1 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -864,7 +864,7 @@ void chrif_parse(Session *s)
 
                 AccountId id = payload.account_id;
                 int login_id2 = payload.login_id2;
-                short client_version = payload.packet_client_version;
+                ClientVersion client_version = payload.client_protocol_version;
                 CharKey st_key = payload.char_key;
                 CharData st_data = payload.char_data;
                 pc_authok(id, login_id2,
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 2a34870..ea39ecb 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -217,7 +217,8 @@ AString clif_validate_chat(dumb_ptr<map_session_data> sd, ChatType type, XString
  */
 static
 void clif_send_sub(dumb_ptr<block_list> bl, const Buffer& buf,
-        dumb_ptr<block_list> src_bl, SendWho type, short min_version)
+        dumb_ptr<block_list> src_bl, SendWho type, ClientVersion min_version,
+        const Buffer& elseBuf)
 {
     nullpo_retv(bl);
     dumb_ptr<map_session_data> sd = bl->is_player();
@@ -245,12 +246,10 @@ void clif_send_sub(dumb_ptr<block_list> bl, const Buffer& buf,
 
     if (sd->sess != nullptr)
     {
-        {
-            if (sd->client_version >= min_version)
-            {
-                send_buffer(sd->sess, buf);
-            }
-        }
+        if (sd->client_version >= min_version)
+            send_buffer(sd->sess, buf);
+        else if (!elseBuf.bytes.empty())
+            send_buffer(sd->sess, elseBuf);
     }
 }
 
@@ -259,7 +258,7 @@ void clif_send_sub(dumb_ptr<block_list> bl, const Buffer& buf,
  *------------------------------------------
  */
 static
-int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short min_version)
+int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, ClientVersion min_version, const Buffer& elseBuf)
 {
     int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
 
@@ -327,7 +326,7 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
         {
             if (bl->bl_m != borrow(undefined_gat))
             {
-                map_foreachinarea(std::bind(clif_send_sub, ph::_1, buf, bl, type, min_version),
+                map_foreachinarea(std::bind(clif_send_sub, ph::_1, buf, bl, type, min_version, elseBuf),
                         bl->bl_m,
                         bl->bl_x - AREA_SIZE, bl->bl_y - AREA_SIZE,
                         bl->bl_x + AREA_SIZE, bl->bl_y + AREA_SIZE,
@@ -339,7 +338,7 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
         {
             if (bl->bl_m != borrow(undefined_gat))
             {
-                map_foreachinarea(std::bind(clif_send_sub, ph::_1, buf, bl, SendWho::AREA_CHAT_WOC, min_version),
+                map_foreachinarea(std::bind(clif_send_sub, ph::_1, buf, bl, SendWho::AREA_CHAT_WOC, min_version, elseBuf),
                         bl->bl_m,
                         bl->bl_x - (AREA_SIZE), bl->bl_y - (AREA_SIZE),
                         bl->bl_x + (AREA_SIZE), bl->bl_y + (AREA_SIZE),
@@ -392,9 +391,9 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
                              sd->bl_x > x1 || sd->bl_y > y1))
                             continue;
                         if (sd->client_version >= min_version)
-                        {
                             send_buffer(sd->sess, buf);
-                        }
+                        else if (!elseBuf.bytes.empty())
+                            send_buffer(sd->sess, elseBuf);
                     }
                 }
                 for (io::FD i : iter_fds())
@@ -408,9 +407,9 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
                         if (sd->partyspy == p.party_id)
                         {
                             if (sd->client_version >= min_version)
-                            {
                                 send_buffer(sd->sess, buf);
-                            }
+                            else if (!elseBuf.bytes.empty())
+                                send_buffer(sd->sess, elseBuf);
                         }
                     }
                 }
@@ -425,6 +424,8 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
             {
                 if (sd->client_version >= min_version)
                     send_buffer(sd->sess, buf);
+                else if (!elseBuf.bytes.empty())
+                    send_buffer(sd->sess, elseBuf);
             }
         }
             break;
@@ -438,6 +439,19 @@ int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, short mi
     return 0;
 }
 
+static
+int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type, ClientVersion min_version)
+{
+    Buffer emptyBuf;
+    return clif_send(buf, bl, type, min_version, emptyBuf);
+}
+
+static
+int clif_send(const Buffer& buf, dumb_ptr<block_list> bl, SendWho type)
+{
+    return clif_send(buf, bl, type, wrap<ClientVersion>(MIN_CLIENT_VERSION));
+}
+
 //
 // パケット作って送信
 //
@@ -542,7 +556,7 @@ int clif_dropflooritem(dumb_ptr<flooritem_data> fitem)
 
     Buffer buf;
     clif_set009e(fitem, buf);
-    clif_send(buf, fitem, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, fitem, SendWho::AREA);
 
     return 0;
 }
@@ -561,7 +575,7 @@ int clif_clearflooritem(dumb_ptr<flooritem_data> fitem, Session *s)
     if (!s)
     {
         Buffer buf = create_fpacket<0x00a1, 6>(fixed_a1);
-        clif_send(buf, fitem, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, fitem, SendWho::AREA);
     }
     else
     {
@@ -586,14 +600,14 @@ int clif_clearchar(dumb_ptr<block_list> bl, BeingRemoveWhy type)
     {
         fixed_80.type = BeingRemoveWhy::GONE;
         Buffer buf = create_fpacket<0x0080, 7>(fixed_80);
-        clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, bl, SendWho::AREA);
     }
     else
     {
         fixed_80.type = type;
         Buffer buf = create_fpacket<0x0080, 7>(fixed_80);
         clif_send(buf, bl,
-                   type == BeingRemoveWhy::DEAD ? SendWho::AREA : SendWho::AREA_WOS, MIN_CLIENT_VERSION);
+                   type == BeingRemoveWhy::DEAD ? SendWho::AREA : SendWho::AREA_WOS);
     }
 
     return 0;
@@ -814,8 +828,6 @@ void clif_npc_action(dumb_ptr<map_session_data> sd, BlockId npcid,
         short command, int id, short x, short y)
 {
     nullpo_retv(sd);
-    if(sd->client_version < 2)
-        return;
 
     Packet_Fixed<0x0212> fixed_212;
     fixed_212.npc_id = npcid;
@@ -825,7 +837,7 @@ void clif_npc_action(dumb_ptr<map_session_data> sd, BlockId npcid,
     fixed_212.y = y;
 
     Buffer buf = create_fpacket<0x0212, 16>(fixed_212);
-    send_buffer(sd->sess, buf);
+    clif_send(buf, sd, SendWho::SELF, wrap<ClientVersion>(2));
 }
 
 /*==========================================
@@ -938,7 +950,7 @@ int clif_spawnpc(dumb_ptr<map_session_data> sd)
     Buffer buf;
     clif_set0078_alt_1d9(sd, buf);
 
-    clif_send(buf, sd, SendWho::AREA_WOS, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::AREA_WOS);
 
     clif_pvpstatus(sd);
 
@@ -977,17 +989,17 @@ int clif_spawnnpc(dumb_ptr<npc_data> nd)
     fixed_7c.pos.y = nd->bl_y;
 
     Buffer buf = create_fpacket<0x007c, 41>(fixed_7c);
-    clif_send(buf, nd, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, nd, SendWho::AREA);
     */
     Buffer buf;
     clif_npc0078(nd, buf);
-    clif_send(buf, nd, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, nd, SendWho::AREA);
 
     if(nd->sit == DamageType::SIT)
     {
         Buffer buff;
         clif_sitnpc_sub(buff, nd, nd->sit);
-        clif_send(buff, nd, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buff, nd, SendWho::AREA);
     }
 
     return 0;
@@ -1046,12 +1058,12 @@ int clif_spawnmob(dumb_ptr<mob_data> md)
         fixed_7c.pos.x = md->bl_x;
         fixed_7c.pos.y = md->bl_y;
         Buffer buf = create_fpacket<0x007c, 41>(fixed_7c);
-        clif_send(buf, md, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, md, SendWho::AREA);
     }
 
     Buffer buf;
     clif_mob0078(md, buf);
-    clif_send(buf, md, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, md, SendWho::AREA);
 
     return 0;
 }
@@ -1105,7 +1117,7 @@ int clif_movechar(dumb_ptr<map_session_data> sd)
     Buffer buf;
     clif_set007b(sd, buf);
 
-    clif_send(buf, sd, SendWho::AREA_WOS, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::AREA_WOS);
 
     if (battle_config.save_clothcolor == 1 && sd->status.clothes_color > 0)
         clif_changelook(sd, LOOK::CLOTHES_COLOR,
@@ -1200,7 +1212,7 @@ void clif_fixpos(dumb_ptr<block_list> bl)
     fixed_88.y = bl->bl_y;
 
     Buffer buf = create_fpacket<0x0088, 10>(fixed_88);
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
 }
 
 /*==========================================
@@ -1878,9 +1890,9 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
 
         Buffer buf = create_fpacket<0x01d7, 11>(fixed_1d7);
         if (dstsd)
-            clif_send(buf, dstsd, SendWho::SELF, MIN_CLIENT_VERSION);
+            clif_send(buf, dstsd, SendWho::SELF);
         else
-            clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+            clif_send(buf, bl, SendWho::AREA);
     }
     else
     {
@@ -1892,9 +1904,9 @@ int clif_changelook_towards(dumb_ptr<block_list> bl, LOOK type, int val,
 
         Buffer buf = create_fpacket<0x01d7, 11>(fixed_1d7);
         if (dstsd)
-            clif_send(buf, dstsd, SendWho::SELF, MIN_CLIENT_VERSION);
+            clif_send(buf, dstsd, SendWho::SELF);
         else
-            clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+            clif_send(buf, bl, SendWho::AREA);
     }
     return 0;
 }
@@ -2058,7 +2070,7 @@ int clif_misceffect(dumb_ptr<block_list> bl, int type)
     fixed_19b.type = type;
     Buffer buf = create_fpacket<0x019b, 10>(fixed_19b);
 
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
 
     return 0;
 }
@@ -2071,7 +2083,7 @@ void clif_map_pvp(dumb_ptr<map_session_data> sd)
     fixed_199.status = sd->bl_m->flag.get(MapFlag::PVP)? 1: 0;
     Buffer buf = create_fpacket<0x0199, 4>(fixed_199);
 
-    clif_send(buf, sd, SendWho::SELF, 2);
+    clif_send(buf, sd, SendWho::SELF, wrap<ClientVersion>(2));
 }
 
 static
@@ -2091,7 +2103,7 @@ void clif_pvpstatus(dumb_ptr<map_session_data> sd)
     nullpo_retv(sd);
     Buffer buf;
     clif_pvpstatus_towards(buf, sd);
-    clif_send(buf, sd, SendWho::AREA, 2);
+    clif_send(buf, sd, SendWho::AREA, wrap<ClientVersion>(2));
 }
 
 /*==========================================
@@ -2115,7 +2127,7 @@ int clif_changeoption(dumb_ptr<block_list> bl)
     fixed_119.zero = 0;
     Buffer buf = create_fpacket<0x0119, 13>(fixed_119);
 
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
 
     return 0;
 }
@@ -2147,7 +2159,7 @@ int clif_useitemack(dumb_ptr<map_session_data> sd, IOff0 index, int amount,
         fixed_1c8.amount = amount;
         fixed_1c8.ok = ok;
         Buffer buf = create_fpacket<0x01c8, 13>(fixed_1c8);
-        clif_send(buf, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::SELF);
     }
 
     return 0;
@@ -2404,7 +2416,7 @@ void clif_getareachar_pc(dumb_ptr<map_session_data> sd,
 
     Buffer buff;
     clif_pvpstatus_towards(buff, dstsd);
-    clif_send(buff, sd, SendWho::SELF, 2);
+    clif_send(buff, sd, SendWho::SELF, wrap<ClientVersion>(2));
 
     if (battle_config.save_clothcolor == 1 && dstsd->status.clothes_color > 0)
         clif_changelook(dstsd, LOOK::CLOTHES_COLOR,
@@ -2452,18 +2464,19 @@ void clif_movemob_sub(dumb_ptr<block_list> sd_bl, dumb_ptr<mob_data> md)
     dumb_ptr<map_session_data> sd = sd_bl->is_player();
     Buffer buf;
 
-    if (sd->client_version < 3 || sd->client_version >= 4)
+    if (sd->client_version < wrap<ClientVersion>(3) ||
+        sd->client_version >= wrap<ClientVersion>(4))
         clif_mob007b(md, buf); // backward compatibility for old clients
     else
     {
         Buffer buf2;
         clif_mob0078(md, buf2);
-        clif_send(buf2, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+        clif_send(buf2, sd, SendWho::SELF);
 
         clif_0225_being_move3(md, buf);
     }
 
-    clif_send(buf, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::SELF);
 }
 
 int clif_movemob(dumb_ptr<mob_data> md)
@@ -2495,7 +2508,7 @@ int clif_fixmobpos(dumb_ptr<mob_data> md)
     {
         Buffer buf;
         clif_mob0078(md, buf);
-        clif_send(buf, md, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, md, SendWho::AREA);
     }
 
     return 0;
@@ -2513,13 +2526,13 @@ int clif_fixpcpos(dumb_ptr<map_session_data> sd)
     {
         Buffer buf;
         clif_set007b(sd, buf);
-        clif_send(buf, sd, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::AREA);
     }
     else
     {
         Buffer buf;
         clif_set0078_main_1d8(sd, buf);
-        clif_send(buf, sd, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::AREA);
     }
     clif_changelook_accessories(sd, nullptr);
 
@@ -2552,7 +2565,7 @@ int clif_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
     fixed_8a.damage_type = type;
     fixed_8a.damage2 = 0;
     Buffer buf = create_fpacket<0x008a, 29>(fixed_8a);
-    clif_send(buf, src, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, src, SendWho::AREA);
 
     return 0;
 }
@@ -2872,7 +2885,7 @@ int clif_skill_damage(dumb_ptr<block_list> src, dumb_ptr<block_list> dst,
     fixed_1de.div = div;
     fixed_1de.type_or_hit = (type > 0) ? type : skill_get_hit(skill_id);
     Buffer buf = create_fpacket<0x01de, 33>(fixed_1de);
-    clif_send(buf, src, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, src, SendWho::AREA);
 
     return 0;
 }
@@ -2890,7 +2903,7 @@ int clif_status_change(dumb_ptr<block_list> bl, StatusChange type, int flag)
     fixed_196.block_id = bl->bl_id;
     fixed_196.flag = flag;
     Buffer buf = create_fpacket<0x0196, 9>(fixed_196);
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
     return 0;
 }
 
@@ -2920,7 +2933,7 @@ void clif_GMmessage(dumb_ptr<block_list> bl, XString mes, int flag)
                (flag == 1) ? SendWho::ALL_SAMEMAP :
                (flag == 2) ? SendWho::AREA :
                (flag == 3) ? SendWho::SELF :
-               SendWho::ALL_CLIENT, MIN_CLIENT_VERSION);
+               SendWho::ALL_CLIENT);
 }
 
 /*==========================================
@@ -2937,7 +2950,7 @@ void clif_resurrection(dumb_ptr<block_list> bl, int type)
     Buffer buf = create_fpacket<0x0148, 8>(fixed_148);
 
     clif_send(buf, bl,
-            type == 1 ? SendWho::AREA : SendWho::AREA_WOS, MIN_CLIENT_VERSION);
+            type == 1 ? SendWho::AREA : SendWho::AREA_WOS);
 }
 
 /*==========================================
@@ -3024,7 +3037,7 @@ int clif_party_info(PartyPair p, Session *s)
     if (sd != nullptr)
     {
         Buffer buf = create_vpacket<0x00fb, 28, 46>(head_fb, repeat_fb);
-        clif_send(buf, sd, SendWho::PARTY, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::PARTY);
     }
     return 0;
 }
@@ -3101,7 +3114,7 @@ void clif_party_option(PartyPair p, dumb_ptr<map_session_data> sd, int flag)
     if (flag == 0)
     {
         Buffer buf = create_fpacket<0x0101, 6>(fixed_101);
-        clif_send(buf, sd, SendWho::PARTY, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::PARTY);
     }
     else
     {
@@ -3135,7 +3148,7 @@ void clif_party_leaved(PartyPair p, dumb_ptr<map_session_data> sd,
         if (sd != nullptr)
         {
             Buffer buf = create_fpacket<0x0105, 31>(fixed_105);
-            clif_send(buf, sd, SendWho::PARTY, MIN_CLIENT_VERSION);
+            clif_send(buf, sd, SendWho::PARTY);
         }
     }
     else if (sd != nullptr)
@@ -3165,7 +3178,7 @@ void clif_party_message(PartyPair p, AccountId account_id, XString mes)
         Packet_Head<0x0109> head_109;
         head_109.account_id = account_id;
         Buffer buf = create_vpacket<0x0109, 8, 1>(head_109, mes);
-        clif_send(buf, sd, SendWho::PARTY, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::PARTY);
     }
 }
 
@@ -3182,7 +3195,7 @@ int clif_party_xy(PartyPair , dumb_ptr<map_session_data> sd)
     fixed_107.x = sd->bl_x;
     fixed_107.y = sd->bl_y;
     Buffer buf = create_fpacket<0x0107, 10>(fixed_107);
-    clif_send(buf, sd, SendWho::PARTY_SAMEMAP_WOS, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::PARTY_SAMEMAP_WOS);
     return 0;
 }
 
@@ -3200,7 +3213,7 @@ int clif_party_hp(PartyPair , dumb_ptr<map_session_data> sd)
     fixed_106.max_hp =
         (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp;
     Buffer buf = create_fpacket<0x0106, 10>(fixed_106);
-    clif_send(buf, sd, SendWho::PARTY_AREA_WOS, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::PARTY_AREA_WOS);
     return 0;
 }
 
@@ -3237,7 +3250,7 @@ void clif_emotion(dumb_ptr<block_list> bl, int type)
     fixed_c0.block_id = bl->bl_id;
     fixed_c0.type = type;
     Buffer buf = create_fpacket<0x00c0, 7>(fixed_c0);
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
 }
 
 void clif_emotion_towards(dumb_ptr<block_list> bl,
@@ -3270,7 +3283,7 @@ void clif_sitting(Session *, dumb_ptr<map_session_data> sd)
     fixed_8a.src_id = sd->bl_id;
     fixed_8a.damage_type = DamageType::SIT;
     Buffer buf = create_fpacket<0x008a, 29>(fixed_8a);
-    clif_send(buf, sd, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::AREA);
 }
 
 static
@@ -3294,7 +3307,7 @@ void clif_sitnpc_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_data> nd, D
 
     Buffer buf;
     clif_sitnpc_sub(buf, nd, dmg);
-    clif_send(buf, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::SELF);
 }
 
 void clif_sitnpc(dumb_ptr<npc_data> nd, DamageType dmg)
@@ -3303,7 +3316,7 @@ void clif_sitnpc(dumb_ptr<npc_data> nd, DamageType dmg)
 
     Buffer buf;
     clif_sitnpc_sub(buf, nd, dmg);
-    clif_send(buf, nd, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, nd, SendWho::AREA);
 }
 
 static
@@ -3340,7 +3353,7 @@ void clif_setnpcdirection_towards(dumb_ptr<map_session_data> sd, dumb_ptr<npc_da
 
     Buffer buf;
     clif_setnpcdirection_sub(buf, nd, direction);
-    clif_send(buf, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::SELF);
 }
 
 void clif_setnpcdirection(dumb_ptr<npc_data> nd, DIR direction)
@@ -3349,7 +3362,7 @@ void clif_setnpcdirection(dumb_ptr<npc_data> nd, DIR direction)
 
     Buffer buf;
     clif_setnpcdirection_sub(buf, nd, direction);
-    clif_send(buf, nd, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, nd, SendWho::AREA);
 }
 
 /*==========================================
@@ -3408,9 +3421,9 @@ int clif_specialeffect(dumb_ptr<block_list> bl, int type, int flag)
         }
     }
     else if (flag == 1)
-        clif_send(buf, bl, SendWho::SELF, MIN_CLIENT_VERSION);
+        clif_send(buf, bl, SendWho::SELF);
     else if (!flag)
-        clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, bl, SendWho::AREA);
 
     return 0;
 
@@ -3841,8 +3854,7 @@ RecvResult clif_parse_GlobalMessage(Session *s, dumb_ptr<map_session_data> sd)
         /* It's not a spell/magic message, so send the message to others. */
         Buffer sendbuf;
         clif_message_sub(sendbuf, sd, mbuf);
-
-        clif_send(sendbuf, sd, SendWho::AREA_CHAT_WOC, MIN_CLIENT_VERSION);
+        clif_send(sendbuf, sd, SendWho::AREA_CHAT_WOC);
     }
 
     /* Send the message back to the speaker. */
@@ -3868,8 +3880,6 @@ void clif_npc_send_title(Session *s, BlockId npcid, XString msg)
 void clif_change_music(dumb_ptr<map_session_data> sd, XString music)
 {
     nullpo_retv(sd);
-    if(sd->client_version < 2)
-        return;
 
     size_t msg_len = music.size();
     if (msg_len > 128)
@@ -3878,7 +3888,7 @@ void clif_change_music(dumb_ptr<map_session_data> sd, XString music)
     Packet_Head<0x0227> head_227;
     Buffer buf = create_vpacket<0x0227, 4, 1>(head_227, music);
 
-    send_buffer(sd->sess, buf);
+    clif_send(buf, sd, SendWho::SELF, wrap<ClientVersion>(2));
 }
 
 void clif_message_towards(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl, XString msg)
@@ -3891,7 +3901,7 @@ void clif_message_towards(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> bl
 
     Buffer buf;
     clif_message_sub(buf, bl, msg);
-    clif_send(buf, sd, SendWho::SELF, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::SELF);
 }
 
 void clif_message(dumb_ptr<block_list> bl, XString msg)
@@ -3900,20 +3910,18 @@ void clif_message(dumb_ptr<block_list> bl, XString msg)
 
     Buffer buf;
     clif_message_sub(buf, bl, msg);
-    clif_send(buf, bl, SendWho::AREA, MIN_CLIENT_VERSION);
+    clif_send(buf, bl, SendWho::AREA);
 }
 
 void clif_send_mask(dumb_ptr<map_session_data> sd, int map_mask)
 {
     nullpo_retv(sd);
-    if(sd->client_version < 2)
-        return;
 
     Packet_Fixed<0x0226> fixed_226;
     fixed_226.mask = map_mask;
 
     Buffer buf = create_fpacket<0x0226, 10>(fixed_226);
-    send_buffer(sd->sess, buf);
+    clif_send(buf, sd, SendWho::SELF, wrap<ClientVersion>(2));
 }
 
 /*==========================================
@@ -3957,7 +3965,7 @@ RecvResult clif_parse_ChangeDir(Session *s, dumb_ptr<map_session_data> sd)
     fixed_9c.client_dir = client_dir;
     Buffer buf = create_fpacket<0x009c, 9>(fixed_9c);
 
-    clif_send(buf, sd, SendWho::AREA_WOS, MIN_CLIENT_VERSION);
+    clif_send(buf, sd, SendWho::AREA_WOS);
 
     return rv;
 }
@@ -3982,7 +3990,7 @@ RecvResult clif_parse_Emotion(Session *s, dumb_ptr<map_session_data> sd)
         fixed_c0.block_id = sd->bl_id;
         fixed_c0.type = emote;
         Buffer buf = create_fpacket<0x00c0, 7>(fixed_c0);
-        clif_send(buf, sd, SendWho::AREA, MIN_CLIENT_VERSION);
+        clif_send(buf, sd, SendWho::AREA);
     }
     else
         clif_skill_fail(sd, SkillID::ONE, 0, 1);
@@ -4053,7 +4061,7 @@ RecvResult clif_parse_ActionRequest(Session *s, dumb_ptr<map_session_data> sd)
             fixed_8a.src_id = sd->bl_id;
             fixed_8a.damage_type = DamageType::STAND;
             Buffer buf = create_fpacket<0x008a, 29>(fixed_8a);
-            clif_send(buf, sd, SendWho::AREA, MIN_CLIENT_VERSION);
+            clif_send(buf, sd, SendWho::AREA);
             break;
     }
 
@@ -4951,7 +4959,7 @@ void clif_sendallquest(dumb_ptr<map_session_data> sd)
     if (!sd->sess)
         return;
 
-    if(sd->client_version < 2) // require 1.5.5.9 or above
+    if(sd->client_version < wrap<ClientVersion>(2)) // require 1.5.5.9 or above
         return;
 
     Session *s = sd->sess;
@@ -4988,7 +4996,7 @@ void clif_sendquest(dumb_ptr<map_session_data> sd, QuestId questid, int value)
     if (!sd->sess)
         return;
 
-    if(sd->client_version < 2) // require 1.5.5.9 or above
+    if(sd->client_version < wrap<ClientVersion>(2)) // require 1.5.5.9 or above
         return;
 
     Session *s = sd->sess;
@@ -5693,7 +5701,7 @@ AString clif_validate_chat(dumb_ptr<map_session_data> sd, ChatType type, XString
     }
 
     // Step beyond the separator. for older clients
-    if (type == ChatType::Global && sd->client_version < 6)
+    if (type == ChatType::Global && sd->client_version < wrap<ClientVersion>(6))
         return buf.xslice_t(sd->status_key.name.to__actual().size() + 3);
 
     // newer clients will not send the name
diff --git a/src/map/map.hpp b/src/map/map.hpp
index a247bb6..47eef57 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -154,7 +154,7 @@ struct map_session_data : block_list, SessionData
     CharId char_id_;
     int login_id1, login_id2;
     SEX sex;
-    int client_version;  // tmw client version
+    ClientVersion client_version;  // tmw client version
     CharKey status_key;
     CharData status;
     GenericArray<Option<Borrowed<struct item_data>>, InventoryIndexing<IOff0, MAX_INVENTORY>> inventory_data =
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index cb115c3..c47925f 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -640,8 +640,8 @@ int pc_isequip(dumb_ptr<map_session_data> sd, IOff0 n)
  * char鯖から送られてきたステータスを設定
  *------------------------------------------
  */
-int pc_authok(AccountId id, int login_id2,
-        short client_version, const CharKey *st_key, const CharData *st_data)
+int pc_authok(AccountId id, int login_id2, ClientVersion client_version,
+    const CharKey *st_key, const CharData *st_data)
 {
     dumb_ptr<map_session_data> sd = nullptr;
 
diff --git a/src/map/pc.hpp b/src/map/pc.hpp
index fc0bdb0..db1d5be 100644
--- a/src/map/pc.hpp
+++ b/src/map/pc.hpp
@@ -80,7 +80,7 @@ int pc_counttargeted(dumb_ptr<map_session_data> sd, dumb_ptr<block_list> src,
 int pc_setrestartvalue(dumb_ptr<map_session_data> sd, int type);
 void pc_makesavestatus(dumb_ptr<map_session_data>);
 int pc_setnewpc(dumb_ptr<map_session_data>, AccountId, CharId, int, uint32_t /*tick_t*/, SEX);
-int pc_authok(AccountId, int, short client_version, const CharKey *, const CharData *);
+int pc_authok(AccountId, int, ClientVersion, const CharKey *, const CharData *);
 int pc_authfail(AccountId accid);
 
 EPOS pc_equippoint(dumb_ptr<map_session_data> sd, IOff0 n);
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index 9014d2b..fe5a961 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -1161,7 +1161,7 @@ static
 void builtin_getversion(ScriptState *st)
 {
     dumb_ptr<map_session_data> sd = script_rid2sd(st);;
-    push_int<ScriptDataInt>(st->stack, sd->client_version);
+    push_int<ScriptDataInt>(st->stack, unwrap<ClientVersion>(sd->client_version));
 }
 
 /*==========================================
diff --git a/src/mmo/fwd.hpp b/src/mmo/fwd.hpp
index 434885e..d98bd33 100644
--- a/src/mmo/fwd.hpp
+++ b/src/mmo/fwd.hpp
@@ -44,6 +44,7 @@ class PartyId;
 class ItemNameId;
 class BlockId;
 class GmLevel;
+class ClientVersion;
 
 class AccountName;
 class AccountPass;
diff --git a/src/mmo/ids.hpp b/src/mmo/ids.hpp
index 28b146a..22f5eae 100644
--- a/src/mmo/ids.hpp
+++ b/src/mmo/ids.hpp
@@ -41,6 +41,7 @@ class ItemNameId : public Wrapped<uint16_t> { public: constexpr ItemNameId() : W
 
 class BlockId : public Wrapped<uint32_t> { public: constexpr BlockId() : Wrapped<uint32_t>() {} protected: constexpr explicit BlockId(uint32_t a) : Wrapped<uint32_t>(a) {} };
 class QuestId : public Wrapped<uint16_t> { public: constexpr QuestId() : Wrapped<uint16_t>() {} protected: constexpr explicit QuestId(uint16_t a) : Wrapped<uint16_t>(a) {} };
+class ClientVersion : public Wrapped<uint32_t>{ public: constexpr ClientVersion() : Wrapped<uint32_t>() {} protected: constexpr explicit ClientVersion(uint32_t a) : Wrapped<uint32_t>(a) {} };
 
 bool impl_extract(XString str, GmLevel *lvl);
 class GmLevel
diff --git a/src/mmo/ids.py b/src/mmo/ids.py
index a98920f..0d39aae 100644
--- a/src/mmo/ids.py
+++ b/src/mmo/ids.py
@@ -6,6 +6,7 @@ for s in [
         'ItemNameId',
         'BlockId',
         'QuestId',
+        'ClientVersion',
 ]:
     class OtherId(object):
         __slots__ = ('_value')
diff --git a/src/mmo/version.hpp b/src/mmo/version.hpp
index 459b500..79d67fe 100644
--- a/src/mmo/version.hpp
+++ b/src/mmo/version.hpp
@@ -37,9 +37,11 @@ namespace tmwa
 
 // increase the min version when the protocol is incompatible with old m+ versions
 // 1 = latest mana, old manaplus, bots
-// 2 = manaplus 1.5.5.9 and above
-// 3 = manaplus 1.5.5.23 and above
-// 4 = manaplus 1.5.8.15 and above
+// 2 = manaplus 1.5.5.9 to manaplus 1.5.5.23
+// 3 = manaplus 1.5.5.23 to manaplus 1.5.8.15
+// 4 = manaplus 1.5.8.15 to manaplus 1.6.3.15
+// 5 = manaplus 1.6.3.15 to 1.6.4.23 (adds SMSG_SCRIPT_MESSAGE)
+// 6 = manaplus 1.6.4.23 and above
 #define MIN_CLIENT_VERSION 1
 
 // TODO now that I generate the protocol, split 'flags' out of the struct
diff --git a/tools/protocol.py b/tools/protocol.py
index 8c3a677..5d27f0d 100755
--- a/tools/protocol.py
+++ b/tools/protocol.py
@@ -320,6 +320,12 @@ class NativeType(LowType):
     def a_tag(self):
         return self.name
 
+    def native_tag(self):
+        return self.name
+
+    def network_tag(self):
+        return self.name
+
 class NetworkType(LowType):
     __slots__ = ('name')
 
@@ -1419,6 +1425,7 @@ def build_context():
     ItemNameId = ids_h.native('ItemNameId')
     BlockId = ids_h.native('BlockId')
     GmLevel = ids_h.native('GmLevel')
+    ClientVersion = ids_h.native('ClientVersion')
 
     party_member = consts_h.native('PartyMember')
 
@@ -1524,6 +1531,7 @@ def build_context():
 
     species = ctx.wrap(Species, u16)
     account_id = ctx.wrap(AccountId, u32)
+    client_version = ctx.wrap(ClientVersion, u32)
     char_id = ctx.wrap(CharId, u32)
     party_id = ctx.wrap(PartyId, u32)
     item_name_id = ctx.wrap(ItemNameId, u16)
@@ -1958,10 +1966,10 @@ def build_context():
         define='CMSG_LOGIN_REGISTER',
         fixed=[
             at(0, u16, 'packet id'),
-            at(2, u32, 'unknown'),
+            at(2, client_version, 'client protocol version'),
             at(6, account_name, 'account name'),
             at(30, account_pass, 'account pass'),
-            at(54, u8, 'version'),
+            at(54, u8, 'flags'),
         ],
         fixed_size=55,
         pre=[HUMAN, 0x7531],
@@ -1979,7 +1987,7 @@ def build_context():
             at(2, account_id, 'account id'),
             at(6, u32, 'login id1'),
             at(10, u32, 'login id2'),
-            at(14, u16, 'packet client version'),
+            at(14, u16, 'unused client protocol version'),
             at(16, sex, 'sex'),
         ],
         fixed_size=17,
@@ -4811,8 +4819,9 @@ def build_context():
             at(6, u8, 'invalid'),
             at(7, account_email, 'email'),
             at(47, time32, 'unused connect until'),
+            at(51, client_version, 'client protocol version'),
         ],
-        fixed_size=51,
+        fixed_size=55,
         pre=[0x2712],
         post=[0x006b, 0x006c],
         desc='''
@@ -5201,8 +5210,8 @@ def build_context():
             at(4, account_id, 'account id'),
             at(8, u32, 'login id2'),
             at(12, time32, 'unused connect until'),
-            at(16, u16, 'packet client version'),
-            at(18, char_key, 'char key'),
+            at(16, client_version, 'client protocol version'),
+            at(20, char_key, 'char key'),
             at(None, char_data, 'char data'),
         ],
         payload_size=None,
@@ -5407,7 +5416,7 @@ def build_context():
             at(6, sex, 'sex'),
         ],
         fixed_size=7,
-        pre=[NOTHING],
+        pre=[0x2723],
         post=[0x00ac, 0x01d7, 0x2b01, 0x3011],
         xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b05, 0x3022],
         desc='''
-- 
cgit v1.2.3-70-g09d2


From 607d40ebcac47555cc01da8ee61c2fae5cec3186 Mon Sep 17 00:00:00 2001
From: mekolat <mekolat@users.noreply.github.com>
Date: Sun, 10 Apr 2016 17:27:52 -0400
Subject: modify remote execution prevention, check for client version

---
 src/map/clif.cpp | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index ea39ecb..386ac63 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -3852,9 +3852,18 @@ RecvResult clif_parse_GlobalMessage(Session *s, dumb_ptr<map_session_data> sd)
         }
 
         /* It's not a spell/magic message, so send the message to others. */
+
         Buffer sendbuf;
         clif_message_sub(sendbuf, sd, mbuf);
-        clif_send(sendbuf, sd, SendWho::AREA_CHAT_WOC);
+
+        Buffer filteredBuf; // ManaPlus remote execution exploit prevention
+        XString filtered = mbuf;
+        if (mbuf.contains_seq("@@="_s) && mbuf.contains('|'))
+            filtered = "##B##3[##1Impossible to see this message. Please update your client.##3]"_s;
+        clif_message_sub(filteredBuf, sd, filtered);
+
+        clif_send(sendbuf, sd, SendWho::AREA_CHAT_WOC,
+            wrap<ClientVersion>(6), filteredBuf);
     }
 
     /* Send the message back to the speaker. */
@@ -5692,14 +5701,6 @@ AString clif_validate_chat(dumb_ptr<map_session_data> sd, ChatType type, XString
         return AString();
     }
 
-    // ManaPlus remote command vulnerability fix
-    if (buf.contains_seq("@@="_s) && buf.contains('|'))
-    {
-        clif_setwaitclose(sd->sess);
-        WARN_MALFORMED_MSG(sd, "remote command exploit attempt"_s);
-        return AString();
-    }
-
     // Step beyond the separator. for older clients
     if (type == ChatType::Global && sd->client_version < wrap<ClientVersion>(6))
         return buf.xslice_t(sd->status_key.name.to__actual().size() + 3);
-- 
cgit v1.2.3-70-g09d2