DEADSOFTWARE

Game: Use proper syntax of sets for game options instead of raw bitwise operations
[d2df-sdl.git] / src / game / g_netmsg.pas
index c2e379b76d25e2b6890ed70f77dd52f8211c2306..a55a8a849d153a29ff46afd7b62f1b95f362d69f 100644 (file)
@@ -31,6 +31,7 @@ const
   NET_MSG_FLAG   = 107;
   NET_MSG_REQFST = 108;
   NET_MSG_GSET   = 109;
+  NET_MSG_FLAGPOS= 110;
 
   NET_MSG_PLR    = 111;
   NET_MSG_PLRPOS = 112;
@@ -44,6 +45,7 @@ const
 
   NET_MSG_ISPAWN = 121;
   NET_MSG_IDEL   = 122;
+  NET_MSG_IPOS   = 123;
 
   NET_MSG_MSPAWN = 131;
   NET_MSG_MPOS   = 132;
@@ -51,8 +53,8 @@ const
   NET_MSG_MSHOT  = 134;
   NET_MSG_MDEL   = 135;
 
-  NET_MSG_PSTATE   = 141;
-  NET_MSG_PTEX     = 142;
+  NET_MSG_PSTATE = 141;
+  NET_MSG_PTEX   = 142;
 
   NET_MSG_TSOUND = 151;
   NET_MSG_TMUSIC = 152;
@@ -129,11 +131,13 @@ const
   NET_CHEAT_SUICIDE  = 1;
   NET_CHEAT_SPECTATE = 2;
   NET_CHEAT_READY    = 3;
+  NET_CHEAT_DROPFLAG = 4;
 
   NET_MAX_DIFFTIME = 5000 div 36;
 
 // HOST MESSAGES
 
+procedure MH_MalformedPacket(C: pTNetClient);
 procedure MH_ProcessFirstSpawn (C: pTNetClient);
 
 procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
@@ -161,6 +165,7 @@ procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
 procedure MH_SEND_GameEvent(EvType: Byte; EvNum: Integer = 0; EvStr: string = 'N'; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
+procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
 // PLAYER
 procedure MH_SEND_PlayerCreate(PID: Word; ID: Integer = NET_EVERYONE);
@@ -174,6 +179,7 @@ procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_
 // ITEM
 procedure MH_SEND_ItemSpawn(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
+procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
 // PANEL
 procedure MH_SEND_PanelTexture(PGUID: Integer; AnimLoop: Byte; ID: Integer = NET_EVERYONE);
 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
@@ -203,6 +209,7 @@ procedure MC_RECV_GameStats(var M: TMsg);
 procedure MC_RECV_CoopStats(var M: TMsg);
 procedure MC_RECV_GameEvent(var M: TMsg);
 procedure MC_RECV_FlagEvent(var M: TMsg);
+procedure MC_RECV_FlagPos(var M: TMsg);
 procedure MC_RECV_GameSettings(var M: TMsg);
 // PLAYER
 function  MC_RECV_PlayerCreate(var M: TMsg): Word;
@@ -216,6 +223,7 @@ procedure MC_RECV_PlayerSettings(var M: TMsg);
 // ITEM
 procedure MC_RECV_ItemSpawn(var M: TMsg);
 procedure MC_RECV_ItemDestroy(var M: TMsg);
+procedure MC_RECV_ItemPos(var M: TMsg);
 // PANEL
 procedure MC_RECV_PanelTexture(var M: TMsg);
 procedure MC_RECV_PanelState(var M: TMsg);
@@ -282,17 +290,15 @@ uses
   g_language, g_monsters, g_netmaster, utils, wadreader, MAPDEF;
 
 const
-  NET_KEY_LEFT     = 1;
-  NET_KEY_RIGHT    = 2;
-  NET_KEY_UP       = 4;
-  NET_KEY_DOWN     = 8;
-  NET_KEY_JUMP     = 16;
-  NET_KEY_FIRE     = 32;
-  NET_KEY_OPEN     = 64;
-  NET_KEY_NW       = 256;
-  NET_KEY_PW       = 512;
-  NET_KEY_CHAT     = 2048;
-  NET_KEY_FORCEDIR = 4096;
+  NET_KEY_LEFT     = 1 shl 0;
+  NET_KEY_RIGHT    = 1 shl 1;
+  NET_KEY_UP       = 1 shl 2;
+  NET_KEY_DOWN     = 1 shl 3;
+  NET_KEY_JUMP     = 1 shl 4;
+  NET_KEY_FIRE     = 1 shl 5;
+  NET_KEY_OPEN     = 1 shl 6;
+  NET_KEY_CHAT     = 1 shl 7;
+  NET_KEY_FORCEDIR = 1 shl 8;
 
 //var
   //kBytePrev: Word = 0;
@@ -331,18 +337,34 @@ end;
 
 // GAME
 
+procedure MH_MalformedPacket(C: pTNetClient);
+begin
+  g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
+    _lc[I_NET_DISC_BADMSG]);
+  g_Net_Host_Ban(C, True);
+end;
+
 procedure MH_RECV_Chat(C: pTNetClient; var M: TMsg);
 var
   Txt: string;
   Mode: Byte;
   PID: Word;
   Pl: TPlayer;
+  Err: Boolean;
 begin
   PID := C^.Player;
   Pl := g_Player_Get(PID);
 
-  Txt := M.ReadString();
-  Mode := M.ReadByte();
+  Err := False;
+  try
+    Txt := M.ReadString();
+    Mode := M.ReadByte();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
+
   if (Mode = NET_CHAT_SYSTEM) then
     Mode := NET_CHAT_PLAYER; // prevent sending system messages from clients
   if (Mode = NET_CHAT_TEAM) and (not gGameSettings.GameMode in [GM_TDM, GM_CTF]) then
@@ -358,40 +380,57 @@ procedure MH_RECV_Info(C: pTNetClient; var M: TMsg);
 var
   Ver, PName, Model, Pw: string;
   R, G, B, T: Byte;
+  WeapSwitch: Byte;
+  TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
+  SwitchEmpty: Byte;
+  SkipF: Byte;
   PID: Word;
   Color: TRGB;
   I: Integer;
-begin
-  Ver := M.ReadString();
-  Pw := M.ReadString();
-  PName := M.ReadString();
-  Model := M.ReadString();
-  R := M.ReadByte();
-  G := M.ReadByte();
-  B := M.ReadByte();
-  T := M.ReadByte();
+  Err: Boolean;
+begin
+  Err := False;
+  try
+    Ver := M.ReadString();
+    Pw := M.ReadString();
+    PName := M.ReadString();
+    Model := M.ReadString();
+    R := M.ReadByte();
+    G := M.ReadByte();
+    B := M.ReadByte();
+    T := M.ReadByte();
+    WeapSwitch := M.ReadByte();
+    for I := WP_FIRST to WP_LAST + 1 do
+      TmpPrefArray[I] := M.ReadByte();
+    SwitchEmpty := M.ReadByte();
+    SkipF := M.ReadByte();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
 
   if Ver <> GAME_VERSION then
   begin
     g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
       _lc[I_NET_DISC_VERSION]);
-    enet_peer_disconnect(C^.Peer, NET_DISC_VERSION);
+    g_Net_Host_Kick(C^.ID, NET_DISC_VERSION);
     Exit;
   end;
 
-  if g_Net_IsHostBanned(C^.Peer^.address.host) then
+  if g_Net_IsAddressBanned(C^.Peer^.address.host) then
   begin
-    if g_Net_IsHostBanned(C^.Peer^.address.host, True) then
+    if g_Net_IsAddressBanned(C^.Peer^.address.host, True) then
     begin
       g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
         _lc[I_NET_DISC_BAN]);
-      enet_peer_disconnect(C^.Peer, NET_DISC_BAN);
+      g_Net_Host_Kick(C^.ID, NET_DISC_BAN);
     end
     else
     begin
       g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
         _lc[I_NET_DISC_BAN]);
-      enet_peer_disconnect(C^.Peer, NET_DISC_TEMPBAN);
+      g_Net_Host_Kick(C^.ID, NET_DISC_TEMPBAN);
     end;
     Exit;
   end;
@@ -401,10 +440,17 @@ begin
     begin
       g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
         _lc[I_NET_DISC_PASSWORD]);
-      enet_peer_disconnect(C^.Peer, NET_DISC_PASSWORD);
+      g_Net_Host_Kick(C^.ID, NET_DISC_PASSWORD);
       Exit;
     end;
 
+  if (C^.Player <> 0) then
+  begin
+    // already received info
+    g_Net_Penalize(C, 'client info spam');
+    Exit;
+  end;
+
   Color.R := R;
   Color.B := B;
   Color.G := G;
@@ -413,11 +459,18 @@ begin
   with g_Player_Get(PID) do
   begin
     Name := PName;
+    WeapSwitchMode := WeapSwitch;
+    SetWeaponPrefs(TmpPrefArray);
+    SwitchToEmpty := SwitchEmpty;
+    SkipIronFist := SkipF;
+    if (g_Force_Model_Get() <> 0) then
+      SetModel(g_Forced_Model_GetName());
     Reset(True);
   end;
 
   C^.Player := PID;
   C^.WaitForFirstSpawn := false;
+  C^.AuthTime := 0;
 
   g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
   e_WriteLog('NET: Client ' + PName + ' [' + IntToStr(C^.ID) +
@@ -504,6 +557,14 @@ end;
 procedure MH_RECV_FullStateRequest(C: pTNetClient; var M: TMsg);
 begin
   //e_LogWritefln('*** client #%u (cid #%u) full state request', [C.ID, C.Player]);
+
+  if C^.FullUpdateSent then
+  begin
+    // FullStateRequest spam?
+    g_Net_Penalize(C, 'duplicate full state request');
+    exit;
+  end;
+
   if gGameOn then
   begin
     MH_SEND_Everything((C^.State = NET_STATE_AUTH), C^.ID)
@@ -519,16 +580,26 @@ end;
 function  MH_RECV_PlayerPos(C: pTNetClient; var M: TMsg): Word;
 var
   Dir, i: Byte;
+  WeaponAct: Byte;
   WeaponSelect: Word;
   PID: Word;
   kByte: Word;
   Pl: TPlayer;
   GT: LongWord;
+  Err: Boolean;
 begin
   Result := 0;
+  Err := False;
   if not gGameOn then Exit;
 
-  GT := M.ReadLongWord();
+  try
+    GT := M.ReadLongWord();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
+
   PID := C^.Player;
   Pl := g_Player_Get(PID);
   if Pl = nil then
@@ -539,9 +610,17 @@ begin
   with Pl do
   begin
     NetTime := GT;
-    kByte := M.ReadWord();
-    Dir := M.ReadByte();
-    WeaponSelect := M.ReadWord();
+    try
+      kByte := M.ReadWord();
+      Dir := M.ReadByte();
+      WeaponAct := M.ReadByte();
+      WeaponSelect := M.ReadWord();
+    except
+      Err := True;
+    end;
+
+    if Err then begin MH_MalformedPacket(C); Exit; end;
+
     //e_WriteLog(Format('R:ws=%d', [WeaponSelect]), MSG_WARNING);
     if Direction <> TDirection(Dir) then
       JustTeleported := False;
@@ -562,8 +641,15 @@ begin
     if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
     if LongBool(kByte and NET_KEY_FIRE) then PressKey(KEY_FIRE, 10000);
     if LongBool(kByte and NET_KEY_OPEN) then PressKey(KEY_OPEN, 10000);
-    if LongBool(kByte and NET_KEY_NW) then PressKey(KEY_NEXTWEAPON, 10000);
-    if LongBool(kByte and NET_KEY_PW) then PressKey(KEY_PREVWEAPON, 10000);
+
+    for i := 0 to 7 do
+    begin
+      if (WeaponAct and Byte(1 shl i)) <> 0 then
+      begin
+        //e_WriteLog(Format(' R:wn=%d', [i]), MSG_WARNING);
+        ProcessWeaponAction(i);
+      end;
+    end;
 
     for i := 0 to 15 do
     begin
@@ -582,11 +668,19 @@ procedure MH_RECV_CheatRequest(C: pTNetClient; var M: TMsg);
 var
   CheatKind: Byte;
   Pl: TPlayer;
+  Err: Boolean;
 begin
+  Err := False;
   Pl := g_Player_Get(C^.Player);
   if Pl = nil then Exit;
 
-  CheatKind := M.ReadByte();
+  try
+    CheatKind := M.ReadByte();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
 
   case CheatKind of
     NET_CHEAT_SUICIDE:
@@ -595,7 +689,7 @@ begin
     begin
       if Pl.FSpectator then
       begin
-        if (gGameSettings.MaxLives = 0) or (gLMSRespawn = LMS_RESPAWN_WARMUP) then
+        if (gGameSettings.MaxLives = 0) or (gLMSRespawn > LMS_RESPAWN_NONE) then
           Pl.Respawn(False)
         else
           MH_SEND_GameEvent(NET_EV_LMS_NOSPAWN, Pl.UID);
@@ -618,6 +712,8 @@ begin
         Dec(gInterReadyCount);
       end;
     end;
+    NET_CHEAT_DROPFLAG:
+      Pl.TryDropFlag();
   end;
 end;
 
@@ -627,14 +723,32 @@ var
   TmpModel: string;
   TmpColor: TRGB;
   TmpTeam: Byte;
+  TmpWeapSwitch: Byte;
+  TmpPrefArray: Array [WP_FIRST .. WP_LAST + 1] of Byte;
+  TmpSwEmpty: Byte;
+  TmpSkipF: Byte;
+  I: Integer;
   Pl: TPlayer;
-begin
-  TmpName := M.ReadString();
-  TmpModel := M.ReadString();
-  TmpColor.R := M.ReadByte();
-  TmpColor.G := M.ReadByte();
-  TmpColor.B := M.ReadByte();
-  TmpTeam := M.ReadByte();
+  Err: Boolean;
+begin
+  Err := False;
+  try
+    TmpName := M.ReadString();
+    TmpModel := M.ReadString();
+    TmpColor.R := M.ReadByte();
+    TmpColor.G := M.ReadByte();
+    TmpColor.B := M.ReadByte();
+    TmpTeam := M.ReadByte();
+    TmpWeapSwitch := M.ReadByte();
+    for I := WP_FIRST to WP_LAST + 1 do
+      TmpPrefArray[I] := M.ReadByte();
+    TmpSwEmpty := M.ReadByte();
+    TmpSkipF := M.ReadByte();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
 
   Pl := g_Player_Get(C^.Player);
   if Pl = nil then Exit;
@@ -650,9 +764,21 @@ begin
     Pl.Name := TmpName;
   end;
 
+  if (g_Force_Model_Get() <> 0) then
+    TmpModel := g_Forced_Model_GetName();
   if TmpModel <> Pl.Model.Name then
     Pl.SetModel(TmpModel);
 
+  if (TmpWeapSwitch <> Pl.WeapSwitchMode) then
+    Pl.WeapSwitchMode := TmpWeapSwitch;
+
+  Pl.SetWeaponPrefs(TmpPrefArray);
+  if (TmpSwEmpty <> Pl.SwitchToEmpty) then
+    Pl.SwitchToEmpty := TmpSwEmpty;
+
+  if (TmpSkipF <> Pl.SkipIronFist) then
+    Pl.SkipIronFist := TmpSkipF;
+
   MH_SEND_PlayerSettings(Pl.UID, TmpModel);
 end;
 
@@ -661,8 +787,15 @@ end;
 procedure MH_RECV_RCONPassword(C: pTNetClient; var M: TMsg);
 var
   Pwd: string;
+  Err: Boolean;
 begin
-  Pwd := M.ReadString();
+  Err := False;
+  try
+    Pwd := M.ReadString();
+  except
+    Err := True;
+  end;
+  if Err then begin MH_MalformedPacket(C); Exit; end;
   if not NetAllowRCON then Exit;
   if Pwd = NetRCONPassword then
   begin
@@ -676,8 +809,15 @@ end;
 procedure MH_RECV_RCONCommand(C: pTNetClient; var M: TMsg);
 var
   Cmd: string;
+  Err: Boolean;
 begin
-  Cmd := M.ReadString();
+  Err := False;
+  try
+    Cmd := M.ReadString();
+  except
+    Err := True;
+  end;
+  if Err then begin MH_MalformedPacket(C); Exit; end;
   if not NetAllowRCON then Exit;
   if not C^.RCONAuth then
   begin
@@ -695,9 +835,17 @@ var
   Name, Command: string;
   Need: Integer;
   Pl: TPlayer;
-begin
-  Start := M.ReadByte() <> 0;
-  Command := M.ReadString();
+  Err: Boolean;
+begin
+  Err := False;
+  try
+    Start := M.ReadByte() <> 0;
+    Command := M.ReadString();
+  except
+    Err := True;
+  end;
+
+  if Err then begin MH_MalformedPacket(C); Exit; end;
 
   Pl := g_Player_Get(C^.Player);
   if Pl = nil then Exit;
@@ -758,11 +906,14 @@ procedure MH_SEND_Everything(CreatePlayers: Boolean {= False}; ID: Integer {= NE
 var
   I: Integer;
 begin
-  if (ID >= 0) and (ID < length(NetClients)) then
-  begin
-    e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
-    MH_ProcessFirstSpawn(@NetClients[ID]);
-  end;
+  if (ID < 0) or (ID >= Length(NetClients)) then
+    exit; // bogus client, this shouldn't happen
+
+  NetClients[ID].FullUpdateSent := True;
+
+  e_LogWritefln('*** client #%u (cid #%u) will get everything', [ID, NetClients[ID].Player]);
+
+  MH_ProcessFirstSpawn(@NetClients[ID]);
 
   if gPlayers <> nil then
   begin
@@ -835,13 +986,13 @@ begin
   NetOut.Write(g_ExtractFileName(gMapInfo.Map));
   NetOut.Write(gWADHash);
   NetOut.Write(gGameSettings.GameMode);
-  NetOut.Write(gGameSettings.GoalLimit);
+  NetOut.Write(gGameSettings.ScoreLimit);
   NetOut.Write(gGameSettings.TimeLimit);
   NetOut.Write(gGameSettings.MaxLives);
-  NetOut.Write(gGameSettings.Options);
+  NetOut.Write(LongWord(gGameSettings.Options));
   NetOut.Write(gTime);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_Chat(Txt: string; Mode: Byte; ID: Integer = NET_EVERYONE);
@@ -863,7 +1014,7 @@ begin
         NetOut.Write(Byte(NET_MSG_CHAT));
         NetOut.Write(Txt);
         NetOut.Write(Mode);
-        g_Net_Host_Send(gPlayers[i].FClientID, True, NET_CHAN_CHAT);
+        g_Net_Host_Send(gPlayers[i].FClientID, True);
       end;
     Team := ID;
     ID := NET_EVERYONE;
@@ -873,7 +1024,7 @@ begin
     NetOut.Write(Byte(NET_MSG_CHAT));
     NetOut.Write(Txt);
     NetOut.Write(Mode);
-    g_Net_Host_Send(ID, True, NET_CHAN_CHAT);
+    g_Net_Host_Send(ID, True);
   end;
 
   if Mode = NET_CHAT_SYSTEM then
@@ -922,7 +1073,7 @@ begin
   NetOut.Write(Y);
   NetOut.Write(Ang);
 
-  g_Net_Host_Send(ID, False, NET_CHAN_GAME);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_Sound(X, Y: Integer; Name: string; Pos: Boolean = True; ID: Integer = NET_EVERYONE);
@@ -938,7 +1089,7 @@ begin
   else
     NetOut.Write(Byte(0));
 
-  g_Net_Host_Send(ID, False, NET_CHAN_GAME);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_CreateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
@@ -956,7 +1107,7 @@ begin
   NetOut.Write(Shots[Proj].Obj.Vel.X);
   NetOut.Write(Shots[Proj].Obj.Vel.Y);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_UpdateShot(Proj: LongInt; ID: Integer = NET_EVERYONE);
@@ -970,7 +1121,7 @@ begin
   NetOut.Write(Shots[Proj].Obj.Vel.X);
   NetOut.Write(Shots[Proj].Obj.Vel.Y);
 
-  g_Net_Host_Send(ID, False, NET_CHAN_SHOTS);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_Send_DeleteShot(Proj: LongInt; X, Y: LongInt; Loud: Boolean = True; ID: Integer = NET_EVERYONE);
@@ -981,7 +1132,7 @@ begin
   NetOut.Write(X);
   NetOut.Write(Y);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_GameStats(ID: Integer = NET_EVERYONE);
@@ -989,8 +1140,8 @@ begin
   NetOut.Write(Byte(NET_MSG_SCORE));
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
   begin
-    NetOut.Write(gTeamStat[TEAM_RED].Goals);
-    NetOut.Write(gTeamStat[TEAM_BLUE].Goals);
+    NetOut.Write(gTeamStat[TEAM_RED].Score);
+    NetOut.Write(gTeamStat[TEAM_BLUE].Score);
   end
   else
     if gGameSettings.GameMode = GM_COOP then
@@ -999,7 +1150,7 @@ begin
       NetOut.Write(gCoopSecretsFound);
     end;
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_CoopStats(ID: Integer = NET_EVERYONE);
@@ -1028,7 +1179,7 @@ begin
   end else
     NetOut.Write(Byte(0));
 
-  g_Net_Host_Send(ID, True, NET_CHAN_SERVICE);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_FlagEvent(EvType: Byte; Flag: Byte; PID: Word; Quiet: Boolean = False; ID: Integer = NET_EVERYONE);
@@ -1044,20 +1195,33 @@ begin
   NetOut.Write(gFlags[Flag].Obj.Y);
   NetOut.Write(gFlags[Flag].Obj.Vel.X);
   NetOut.Write(gFlags[Flag].Obj.Vel.Y);
+  NetOut.Write(Byte(gFlags[Flag].Direction));
+
+  g_Net_Host_Send(ID, True);
+end;
+
+procedure MH_SEND_FlagPos(Flag: Byte; ID: Integer = NET_EVERYONE);
+begin
+  NetOut.Write(Byte(NET_MSG_FLAGPOS));
+  NetOut.Write(Flag);
+  NetOut.Write(gFlags[Flag].Obj.X);
+  NetOut.Write(gFlags[Flag].Obj.Y);
+  NetOut.Write(gFlags[Flag].Obj.Vel.X);
+  NetOut.Write(gFlags[Flag].Obj.Vel.Y);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_GameSettings(ID: Integer = NET_EVERYONE);
 begin
   NetOut.Write(Byte(NET_MSG_GSET));
   NetOut.Write(gGameSettings.GameMode);
-  NetOut.Write(gGameSettings.GoalLimit);
+  NetOut.Write(gGameSettings.ScoreLimit);
   NetOut.Write(gGameSettings.TimeLimit);
   NetOut.Write(gGameSettings.MaxLives);
-  NetOut.Write(gGameSettings.Options);
+  NetOut.Write(LongWord(gGameSettings.Options));
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, True);
 end;
 
 // PLAYER (SEND)
@@ -1079,7 +1243,7 @@ begin
   NetOut.Write(P.FColor.B);
   NetOut.Write(P.Team);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT)
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PlayerPos(Reliable: Boolean; PID: Word; ID: Integer = NET_EVERYONE);
@@ -1110,9 +1274,10 @@ begin
       if IsKeyPressed(KEY_UP) then kByte := kByte or NET_KEY_UP;
       if IsKeyPressed(KEY_DOWN) then kByte := kByte or NET_KEY_DOWN;
       if IsKeyPressed(KEY_JUMP) then kByte := kByte or NET_KEY_JUMP;
-      if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
     end;
 
+    if JustTeleported then kByte := kByte or NET_KEY_FORCEDIR;
+
     NetOut.Write(kByte);
     if Direction = TDirection.D_LEFT then NetOut.Write(Byte(0)) else NetOut.Write(Byte(1));
     NetOut.Write(GameX);
@@ -1123,7 +1288,7 @@ begin
     NetOut.Write(GameAccelY);
   end;
 
-  g_Net_Host_Send(ID, Reliable, NET_CHAN_PLAYERPOS);
+  g_Net_Host_Send(ID, Reliable);
 end;
 
 procedure MH_SEND_PlayerStats(PID: Word; ID: Integer = NET_EVERYONE);
@@ -1158,13 +1323,13 @@ begin
       NetOut.Write(FMaxAmmo[I]);
 
     for I := MR_SUIT to MR_MAX do
-      NetOut.Write(LongWord(FMegaRulez[I]));
+      NetOut.Write(LongWord(FPowerups[I]));
 
-    NetOut.Write(Byte(R_ITEM_BACKPACK in FRulez));
-    NetOut.Write(Byte(R_KEY_RED in FRulez));
-    NetOut.Write(Byte(R_KEY_GREEN in FRulez));
-    NetOut.Write(Byte(R_KEY_BLUE in FRulez));
-    NetOut.Write(Byte(R_BERSERK in FRulez));
+    NetOut.Write(Byte(R_ITEM_BACKPACK in FInventory));
+    NetOut.Write(Byte(R_KEY_RED in FInventory));
+    NetOut.Write(Byte(R_KEY_GREEN in FInventory));
+    NetOut.Write(Byte(R_KEY_BLUE in FInventory));
+    NetOut.Write(Byte(R_BERSERK in FInventory));
 
     NetOut.Write(Frags);
     NetOut.Write(Death);
@@ -1181,7 +1346,7 @@ begin
     NetOut.Write(FSpawnInvul);
   end;
 
-  g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PlayerDamage(PID: Word; Kind: Byte; Attacker, Value: Word; VX, VY: Integer; ID: Integer = NET_EVERYONE);
@@ -1194,7 +1359,7 @@ begin
   NetOut.Write(VX);
   NetOut.Write(VY);
 
-  g_Net_Host_Send(ID, False, NET_CHAN_PLAYER);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_PlayerDeath(PID: Word; KillType, DeathType: Byte; Attacker: Word; ID: Integer = NET_EVERYONE);
@@ -1205,7 +1370,7 @@ begin
   NetOut.Write(DeathType);
   NetOut.Write(Attacker);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_PLAYER);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PlayerFire(PID: Word; Weapon: Byte; X, Y, AX, AY: Integer; ShotID: Integer = -1; ID: Integer = NET_EVERYONE);
@@ -1219,7 +1384,7 @@ begin
   NetOut.Write(AY);
   NetOut.Write(ShotID);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_SHOTS);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PlayerDelete(PID: Word; ID: Integer = NET_EVERYONE);
@@ -1227,7 +1392,7 @@ begin
   NetOut.Write(Byte(NET_MSG_PLRDEL));
   NetOut.Write(PID);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PlayerSettings(PID: Word; Mdl: string = ''; ID: Integer = NET_EVERYONE);
@@ -1249,7 +1414,7 @@ begin
   NetOut.Write(Pl.FColor.B);
   NetOut.Write(Pl.Team);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, True);
 end;
 
 // ITEM (SEND)
@@ -1274,7 +1439,7 @@ begin
   NetOut.Write(it.Obj.Vel.X);
   NetOut.Write(it.Obj.Vel.Y);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_ItemDestroy(Quiet: Boolean; IID: Word; ID: Integer = NET_EVERYONE);
@@ -1283,7 +1448,23 @@ begin
   NetOut.Write(IID);
   NetOut.Write(Byte(Quiet));
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
+end;
+
+procedure MH_SEND_ItemPos(IID: Word; ID: Integer = NET_EVERYONE);
+var
+  it: PItem;
+begin
+  it := g_Items_ByIdx(IID);
+
+  NetOut.Write(Byte(NET_MSG_IPOS));
+  NetOut.Write(IID);
+  NetOut.Write(it.Obj.X);
+  NetOut.Write(it.Obj.Y);
+  NetOut.Write(it.Obj.Vel.X);
+  NetOut.Write(it.Obj.Vel.Y);
+
+  g_Net_Host_Send(ID, False);
 end;
 
 // PANEL
@@ -1305,7 +1486,7 @@ begin
     NetOut.Write(AnimLoop);
   end;
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_PanelState(PGUID: Integer; ID: Integer = NET_EVERYONE);
@@ -1339,7 +1520,7 @@ begin
   if TP.moveOnce then mpflags := mpflags or 2;
   NetOut.Write(Byte(mpflags));
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
 end;
 
 // TRIGGER
@@ -1399,7 +1580,7 @@ begin
     NetOut.Write(Byte(GameDirection));
   end;
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_MonsterPos(UID: Word; ID: Integer = NET_EVERYONE);
@@ -1421,7 +1602,7 @@ begin
     NetOut.Write(Byte(GameDirection));
   end;
 
-  g_Net_Host_Send(ID, False, NET_CHAN_MONSTERPOS);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_MonsterState(UID: Word; ForcedAnim: Byte = 255; ID: Integer = NET_EVERYONE);
@@ -1448,7 +1629,7 @@ begin
     NetOut.Write(FFireTime);
   end;
 
-  g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_MonsterShot(UID: Word; X, Y, VX, VY: Integer; ID: Integer = NET_EVERYONE);
@@ -1460,7 +1641,7 @@ begin
   NetOut.Write(VX);
   NetOut.Write(VY);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_MONSTER);
+  g_Net_Host_Send(ID, True);
 end;
 
 procedure MH_SEND_MonsterDelete(UID: Word; ID: Integer = NET_EVERYONE);
@@ -1473,7 +1654,7 @@ begin
   NetOut.Write(Byte(NET_MSG_MDEL));
   NetOut.Write(UID);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_LARGEDATA);
+  g_Net_Host_Send(ID, True);
 end;
 
 // MISC
@@ -1483,7 +1664,7 @@ begin
   NetOut.Write(Byte(NET_MSG_TIME_SYNC));
   NetOut.Write(Time);
 
-  g_Net_Host_Send(ID, False, NET_CHAN_SERVICE);
+  g_Net_Host_Send(ID, False);
 end;
 
 procedure MH_SEND_VoteEvent(EvType: Byte;
@@ -1498,7 +1679,7 @@ begin
   NetOut.Write(StrArg1);
   NetOut.Write(StrArg2);
 
-  g_Net_Host_Send(ID, True, NET_CHAN_IMPORTANT);
+  g_Net_Host_Send(ID, True);
 end;
 
 // CLIENT MESSAGES //
@@ -1515,22 +1696,25 @@ begin
 
   if Mode <> NET_CHAT_SYSTEM then
   begin
-    if Mode = NET_CHAT_PLAYER then
-    begin
-      g_Console_Add(Txt, True);
-      e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
-      g_Game_ChatSound(b_Text_Unformat(Txt));
-    end else
-    if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
+    if NetDeafLevel = 0 then
     begin
-      if gPlayer1.Team = TEAM_RED then
-        g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
-      if gPlayer1.Team = TEAM_BLUE then
-        g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
-      e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
-      g_Game_ChatSound(b_Text_Unformat(Txt));
+      if Mode = NET_CHAT_PLAYER then
+      begin
+        g_Console_Add(Txt, True);
+        e_WriteLog('[Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
+        g_Game_ChatSound(b_Text_Unformat(Txt));
+      end else
+      if (Mode = NET_CHAT_TEAM) and (gPlayer1 <> nil) then
+      begin
+        if gPlayer1.Team = TEAM_RED then
+          g_Console_Add(b_Text_Format('\r[Team] ') + Txt, True);
+        if gPlayer1.Team = TEAM_BLUE then
+          g_Console_Add(b_Text_Format('\b[Team] ') + Txt, True);
+        e_WriteLog('[Team Chat] ' + b_Text_Unformat(Txt), TMsgType.Notify);
+        g_Game_ChatSound(b_Text_Unformat(Txt));
+      end;
     end;
-  end else
+  end else if (NetDeafLevel < 2) then
     g_Console_Add(Txt, True);
 end;
 
@@ -1719,8 +1903,8 @@ procedure MC_RECV_GameStats(var M: TMsg);
 begin
   if gGameSettings.GameMode in [GM_TDM, GM_CTF] then
   begin
-    gTeamStat[TEAM_RED].Goals := M.ReadSmallInt();
-    gTeamStat[TEAM_BLUE].Goals := M.ReadSmallInt();
+    gTeamStat[TEAM_RED].Score := M.ReadSmallInt();
+    gTeamStat[TEAM_BLUE].Score := M.ReadSmallInt();
   end
   else
     if gGameSettings.GameMode = GM_COOP then
@@ -1841,10 +2025,13 @@ begin
 
     NET_EV_CHANGE_TEAM:
     begin
-      if EvNum = TEAM_RED then
-        g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
-      if EvNum = TEAM_BLUE then
-        g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
+      if NetDeafLevel < 2 then
+      begin
+        if EvNum = TEAM_RED then
+          g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_RED], [EvStr]), True);
+        if EvNum = TEAM_BLUE then
+          g_Console_Add(Format(_lc[I_PLAYER_CHTEAM_BLUE], [EvStr]), True);
+      end;
     end;
 
     NET_EV_PLAYER_KICK:
@@ -1877,7 +2064,7 @@ begin
       g_Console_Add('*** ' + _lc[I_MESSAGE_LMS_SURVIVOR] + ' ***', True);
 
     NET_EV_BIGTEXT:
-      g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
+      if NetDeafLevel < 2 then g_Game_Message(AnsiUpperCase(EvStr), Word(EvNum));
 
     NET_EV_SCORE:
     begin
@@ -2009,6 +2196,18 @@ begin
   end;
 end;
 
+procedure MC_RECV_FlagPos(var M: TMsg);
+var
+  Fl: Byte;
+begin
+  Fl := M.ReadByte();
+  if Fl = FLAG_NONE then Exit;
+  gFlags[Fl].Obj.X := M.ReadLongInt();
+  gFlags[Fl].Obj.Y := M.ReadLongInt();
+  gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
+  gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
+end;
+
 procedure MC_RECV_FlagEvent(var M: TMsg);
 var
   PID: Word;
@@ -2032,6 +2231,7 @@ begin
   gFlags[Fl].Obj.Y := M.ReadLongInt();
   gFlags[Fl].Obj.Vel.X := M.ReadLongInt();
   gFlags[Fl].Obj.Vel.Y := M.ReadLongInt();
+  gFlags[Fl].Direction := TDirection(M.ReadByte());
 
   Pl := g_Player_Get(PID);
   if (Pl = nil) and
@@ -2169,10 +2369,10 @@ end;
 procedure MC_RECV_GameSettings(var M: TMsg);
 begin
   gGameSettings.GameMode := M.ReadByte();
-  gGameSettings.GoalLimit := M.ReadWord();
+  gGameSettings.ScoreLimit := M.ReadWord();
   gGameSettings.TimeLimit := M.ReadWord();
   gGameSettings.MaxLives := M.ReadByte();
-  gGameSettings.Options := M.ReadLongWord();
+  gGameSettings.Options := TGameOptions(M.ReadLongWord());
 end;
 
 // PLAYER
@@ -2199,6 +2399,8 @@ begin
   if (PID <> NetPlrUID1) and (PID <> NetPlrUID2) then
   begin
     if (Pl <> nil) then Exit;
+    if (g_Force_Model_Get() <> 0) then
+      Model := g_Forced_Model_GetName();
     DID := g_Player_Create(Model, Color, T, False);
     with g_Player_Get(DID) do
     begin
@@ -2221,7 +2423,8 @@ begin
     end;
   end;
 
-  g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
+  if NetDeafLevel < 3 then 
+    g_Console_Add(Format(_lc[I_PLAYER_JOIN], [PName]), True);
   e_WriteLog('NET: Player ' + PName + ' [' + IntToStr(PID) + '] added.', TMsgType.Notify);
   Result := PID;
 end;
@@ -2264,7 +2467,7 @@ begin
 
     ReleaseKeys;
 
-    if (kByte = NET_KEY_CHAT) then
+    if LongBool(kByte and NET_KEY_CHAT) then
       PressKey(KEY_CHAT, 10000)
     else
     begin
@@ -2275,7 +2478,9 @@ begin
       if LongBool(kByte and NET_KEY_JUMP) then PressKey(KEY_JUMP, 10000);
     end;
 
-    if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or LongBool(kByte and NET_KEY_FORCEDIR) then
+    JustTeleported := LongBool(kByte and NET_KEY_FORCEDIR);
+
+    if ((Pl <> gPlayer1) and (Pl <> gPlayer2)) or JustTeleported then
       SetDirection(TDirection(Dir));
 
     GameVelX := M.ReadLongInt();
@@ -2322,19 +2527,14 @@ begin
       FMaxAmmo[I] := M.ReadWord();
 
     for I := MR_SUIT to MR_MAX do
-      FMegaRulez[I] := M.ReadLongWord();
-
-    FRulez := [];
-    if (M.ReadByte() <> 0) then
-      FRulez := FRulez + [R_ITEM_BACKPACK];
-    if (M.ReadByte() <> 0) then
-      FRulez := FRulez + [R_KEY_RED];
-    if (M.ReadByte() <> 0) then
-      FRulez := FRulez + [R_KEY_GREEN];
-    if (M.ReadByte() <> 0) then
-      FRulez := FRulez + [R_KEY_BLUE];
-    if (M.ReadByte() <> 0) then
-      FRulez := FRulez + [R_BERSERK];
+      FPowerups[I] := M.ReadLongWord();
+
+    FInventory := [];
+    if (M.ReadByte() <> 0) then FInventory += [R_ITEM_BACKPACK];
+    if (M.ReadByte() <> 0) then FInventory += [R_KEY_RED];
+    if (M.ReadByte() <> 0) then FInventory += [R_KEY_GREEN];
+    if (M.ReadByte() <> 0) then FInventory += [R_KEY_BLUE];
+    if (M.ReadByte() <> 0) then FInventory += [R_BERSERK];
 
     Frags := M.ReadLongInt();
     Death := M.ReadLongInt();
@@ -2344,12 +2544,12 @@ begin
     FSpectator := M.ReadByte() <> 0;
     if FSpectator then
     begin
-      if Pl = gPlayer1 then
+      if UID = NetPlrUID1 then
       begin
         gSpectLatchPID1 := UID;
         gPlayer1 := nil;
       end;
-      if Pl = gPlayer2 then
+      if UID = NetPlrUID2 then
       begin
         gSpectLatchPID2 := UID;
         gPlayer2 := nil;
@@ -2452,7 +2652,8 @@ begin
   Result := 0;
   if Pl = nil then Exit;
 
-  g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
+  if NetDeafLevel < 3 then 
+    g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [Pl.Name]), True);
   e_WriteLog('NET: Player ' + Pl.Name + ' [' + IntToStr(PID) + '] removed.', TMsgType.Notify);
 
   g_Player_Remove(PID);
@@ -2489,6 +2690,7 @@ procedure MC_RECV_PlayerSettings(var M: TMsg);
 var
   TmpName: string;
   TmpModel: string;
+  CheckModel: string;
   TmpColor: TRGB;
   TmpTeam: Byte;
   Pl: TPlayer;
@@ -2517,10 +2719,13 @@ begin
 
   if Pl.Name <> TmpName then
   begin
-    g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
+    if NetDeafLevel < 3 then 
+      g_Console_Add(Format(_lc[I_PLAYER_NAME], [Pl.Name, TmpName]), True);
     Pl.Name := TmpName;
   end;
 
+  if (g_Force_Model_Get() <> 0) then
+    TmpModel := g_Forced_Model_GetName();
   if TmpModel <> Pl.Model.Name then
     Pl.SetModel(TmpModel);
 end;
@@ -2583,6 +2788,31 @@ begin
   g_Items_Remove(ID);
 end;
 
+procedure MC_RECV_ItemPos(var M: TMsg);
+var
+  ID: Word;
+  X, Y, VX, VY: Integer;
+  it: PItem;
+begin
+  if not gGameOn then Exit;
+
+  ID := M.ReadWord();
+  X := M.ReadLongInt();
+  Y := M.ReadLongInt();
+  VX := M.ReadLongInt();
+  VY := M.ReadLongInt();
+
+  if g_Items_ValidId(ID) then
+  begin
+    it := g_Items_ByIdx(ID);
+    it.Obj.X := X;
+    it.Obj.Y := Y;
+    it.Obj.Vel.X := VX;
+    it.Obj.Vel.Y := VY;
+    it.positionChanged();
+  end;
+end;
+
 // PANEL
 
 procedure MC_RECV_PanelTexture(var M: TMsg);
@@ -2911,23 +3141,25 @@ begin
   Str1 := M.ReadString();
   Str2 := M.ReadString();
 
-  case EvID of
-    NET_VE_STARTED:
-      g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
-    NET_VE_PASSED:
-      g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
-    NET_VE_FAILED:
-      g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
-    NET_VE_VOTE:
-      g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
-    NET_VE_INPROGRESS:
-      g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
-  end;
+  if NetDeafLevel < 2 then 
+    case EvID of
+      NET_VE_STARTED:
+        g_Console_Add(Format(_lc[I_MESSAGE_VOTE_STARTED], [Str1, Str2, Int1]), True);
+      NET_VE_PASSED:
+        g_Console_Add(Format(_lc[I_MESSAGE_VOTE_PASSED], [Str1]), True);
+      NET_VE_FAILED:
+        g_Console_Add(_lc[I_MESSAGE_VOTE_FAILED], True);
+      NET_VE_VOTE:
+        g_Console_Add(Format(_lc[I_MESSAGE_VOTE_VOTE], [Str1, Int1, Int2]), True);
+      NET_VE_INPROGRESS:
+        g_Console_Add(Format(_lc[I_MESSAGE_VOTE_INPROGRESS], [Str1]), True);
+    end;
 end;
 
 // CLIENT SEND
 
 procedure MC_SEND_Info(Password: string);
+var i: Integer;
 begin
   NetOut.Clear();
 
@@ -2940,8 +3172,13 @@ begin
   NetOut.Write(gPlayer1Settings.Color.G);
   NetOut.Write(gPlayer1Settings.Color.B);
   NetOut.Write(gPlayer1Settings.Team);
+  NetOut.Write(gPlayer1Settings.WeaponSwitch);
+  for i := WP_FIRST to WP_LAST + 1 do
+    NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
+  NetOut.Write(gPlayer1Settings.SwitchToEmpty);
+  NetOut.Write(gPlayer1Settings.SkipIronFist);
 
-  g_Net_Client_Send(True, NET_CHAN_SERVICE);
+  g_Net_Client_Send(True);
 end;
 
 procedure MC_SEND_Chat(Txt: string; Mode: Byte);
@@ -2950,7 +3187,7 @@ begin
   NetOut.Write(Txt);
   NetOut.Write(Mode);
 
-  g_Net_Client_Send(True, NET_CHAN_CHAT);
+  g_Net_Client_Send(True);
 end;
 
 procedure MC_SEND_PlayerPos();
@@ -2958,6 +3195,7 @@ var
   kByte: Word;
   Predict: Boolean;
   strafeDir: Byte;
+  WeaponAct: Byte = 0;
   WeaponSelect: Word = 0;
   i: Integer;
 begin
@@ -3031,11 +3269,15 @@ begin
     end;
     if gPlayerAction[0, ACTION_ATTACK] then kByte := kByte or NET_KEY_FIRE;
     if gPlayerAction[0, ACTION_ACTIVATE] then kByte := kByte or NET_KEY_OPEN;
-    if gPlayerAction[0, ACTION_WEAPNEXT] then kByte := kByte or NET_KEY_NW;
-    if gPlayerAction[0, ACTION_WEAPPREV] then kByte := kByte or NET_KEY_PW;
 
-    gPlayerAction[0, ACTION_WEAPNEXT] := False; // HACK, remove after readyweaon&pendinweapon implementation
-    gPlayerAction[0, ACTION_WEAPPREV] := False; // HACK, remove after readyweaon&pendinweapon implementation
+    for i := WP_FACT to WP_LACT do
+    begin
+      if gWeaponAction[0, i] then
+      begin
+        WeaponAct := WeaponAct or Byte(1 shl i);
+        gWeaponAction[0, i] := False
+      end
+    end;
 
     for i := WP_FIRST to WP_LAST do
     begin
@@ -3056,9 +3298,10 @@ begin
   NetOut.Write(gTime);
   NetOut.Write(kByte);
   NetOut.Write(Byte(gPlayer1.Direction));
+  NetOut.Write(WeaponAct);
   NetOut.Write(WeaponSelect);
   //e_WriteLog(Format('S:ws=%d', [WeaponSelect]), MSG_WARNING);
-  g_Net_Client_Send(True, NET_CHAN_PLAYERPOS);
+  g_Net_Client_Send(True);
 
   //kBytePrev := kByte;
   //kDirPrev := gPlayer1.Direction;
@@ -3069,10 +3312,11 @@ begin
   NetOut.Write(Byte(NET_MSG_VOTE_EVENT));
   NetOut.Write(Byte(Start));
   NetOut.Write(Command);
-  g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
+  g_Net_Client_Send(True);
 end;
 
 procedure MC_SEND_PlayerSettings();
+var i: Integer;
 begin
   NetOut.Write(Byte(NET_MSG_PLRSET));
   NetOut.Write(gPlayer1Settings.Name);
@@ -3081,15 +3325,20 @@ begin
   NetOut.Write(gPlayer1Settings.Color.G);
   NetOut.Write(gPlayer1Settings.Color.B);
   NetOut.Write(gPlayer1Settings.Team);
+  NetOut.Write(gPlayer1Settings.WeaponSwitch);
+  for i := WP_FIRST to WP_LAST + 1 do
+    NetOut.Write(gPlayer1Settings.WeaponPreferences[i]);
+  NetOut.Write(gPlayer1Settings.SwitchToEmpty);
+  NetOut.Write(gPlayer1Settings.SkipIronFist);
 
-  g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
+  g_Net_Client_Send(True);
 end;
 
 procedure MC_SEND_FullStateRequest();
 begin
   NetOut.Write(Byte(NET_MSG_REQFST));
 
-  g_Net_Client_Send(True, NET_CHAN_SERVICE);
+  g_Net_Client_Send(True);
 end;
 
 procedure MC_SEND_CheatRequest(Kind: Byte);
@@ -3097,21 +3346,21 @@ begin
   NetOut.Write(Byte(NET_MSG_CHEAT));
   NetOut.Write(Kind);
 
-  g_Net_Client_Send(True, NET_CHAN_IMPORTANT);
+  g_Net_Client_Send(True);
 end;
 procedure MC_SEND_RCONPassword(Password: string);
 begin
   NetOut.Write(Byte(NET_MSG_RCON_AUTH));
   NetOut.Write(Password);
 
-  g_Net_Client_Send(True, NET_CHAN_SERVICE);
+  g_Net_Client_Send(True);
 end;
 procedure MC_SEND_RCONCommand(Cmd: string);
 begin
   NetOut.Write(Byte(NET_MSG_RCON_CMD));
   NetOut.Write(Cmd);
 
-  g_Net_Client_Send(True, NET_CHAN_SERVICE);
+  g_Net_Client_Send(True);
 end;