DEADSOFTWARE

Net: Buffer outgoing messages buffers2
authorfgsfds <pvt.fgsfds@gmail.com>
Mon, 26 Aug 2019 20:18:25 +0000 (23:18 +0300)
committerfgsfds <pvt.fgsfds@gmail.com>
Mon, 26 Aug 2019 20:18:25 +0000 (23:18 +0300)
src/engine/e_msg.pas
src/game/g_game.pas
src/game/g_net.pas
src/game/g_nethandler.pas
src/game/g_netmsg.pas
src/game/g_window.pas

index 6433511f3abd87e2d2570a4e05b272f3e1ecb7a8..a2ecc52e3e9a40e6a2c58dede4d26344e35847b3 100644 (file)
@@ -39,6 +39,9 @@ type
     function AssignBuffer(P: Pointer; N: Integer; Full: Boolean = False): Boolean;
 
     procedure BeginReading();
+    procedure Seek(Pos: Integer);
+    procedure Skip(Size: Integer);
+    function BytesLeft(): Integer;
     function ReadData(V: Pointer; N: Integer): Integer;
     function ReadChar(): Char;
     function ReadByte(): Byte;
@@ -61,6 +64,7 @@ type
     procedure Write(V: Int64); overload;
     procedure Write(V: String); overload;
     procedure Write(V: TMD5Digest); overload;
+    procedure Write(V: TMsg);
   end;
 
 type
@@ -97,6 +101,7 @@ procedure TMsg.Free();
 begin
   if not OwnMemory then
     raise Exception.Create('TMsg.Free: called on borrowed memory');
+  Clear();
   OwnMemory := False;
   FreeMem(Data);
   Data := nil;
@@ -147,6 +152,18 @@ begin
   CurSize := CurSize + N;
 end;
 
+procedure TMsg.Write(V: TMsg);
+begin
+  if CurSize + V.CurSize > MaxSize then
+  begin
+    Overflow := True;
+    raise Exception.Create('TMsg.WriteData: buffer overrun!');
+    Exit;
+  end;
+  Move(V.Data^, (Data + CurSize)^, V.CurSize);
+  CurSize := CurSize + V.CurSize;
+end;
+
 procedure TMsg.Write(V: Byte); overload;
 begin
   WriteData(@V, 1);
@@ -206,6 +223,25 @@ begin
   Bit := 0;
 end;
 
+procedure TMsg.Seek(Pos: Integer);
+begin
+  if Pos > CurSize then
+    raise Exception.Create('TMsg.Seek: buffer overrun!');
+  ReadCount := Pos;
+end;
+
+procedure TMsg.Skip(Size: Integer);
+begin
+  if ReadCount + Size > CurSize then
+    raise Exception.Create('TMsg.Skip: buffer overrun!');
+  ReadCount := ReadCount + Size;
+end;
+
+function TMsg.BytesLeft(): Integer;
+begin
+  Result := CurSize - ReadCount;
+end;
+
 function TMsg.ReadData(V: Pointer; N: Integer): Integer;
 begin
   Result := 0;
index 7b8324325dffae8ff5a131987a28a9326f555b87..77fd80ac499b78f21c870be4cb5a75d167fca1e2 100644 (file)
@@ -4614,6 +4614,7 @@ begin
         if not InMsg.Init(Ptr, NetEvent.packet^.dataLength, True) then
           continue;
 
+        InMsg.ReadLongWord(); // skip size
         MID := InMsg.ReadByte();
 
         if (MID = NET_MSG_INFO) and (State = 0) then
index 907a5beefd437b163adaf1ef934dbc29c55fee58..83c8398991ff1ed0429f97212a1f80df69f55440 100644 (file)
@@ -22,7 +22,7 @@ uses
   e_log, e_msg, ENet, Classes, MAPDEF{$IFDEF USE_MINIUPNPC}, miniupnpc;{$ELSE};{$ENDIF}
 
 const
-  NET_PROTOCOL_VER = 180;
+  NET_PROTOCOL_VER = 181;
 
   NET_MAXCLIENTS = 24;
   NET_CHANS = 11;
@@ -48,6 +48,9 @@ const
 
   NET_EVERYONE = -1;
 
+  NET_UNRELIABLE = 0;
+  NET_RELIABLE = 1;
+
   NET_DISC_NONE: enet_uint32 = 0;
   NET_DISC_PROTOCOL: enet_uint32 = 1;
   NET_DISC_VERSION: enet_uint32 = 2;
@@ -76,14 +79,15 @@ const
 
 type
   TNetClient = record
-    ID:      Byte;
-    Used:    Boolean;
-    State:   Byte;
-    Peer:    pENetPeer;
-    Player:  Word;
+    ID:       Byte;
+    Used:     Boolean;
+    State:    Byte;
+    Peer:     pENetPeer;
+    Player:   Word;
     RequestedFullUpdate: Boolean;
     RCONAuth: Boolean;
     Voted:    Boolean;
+    NetOut:   array [0..1] of TMsg;
   end;
   TBanRecord = record
     IP: LongWord;
@@ -126,6 +130,7 @@ var
   NetClientPort: Word   = 25666;
 
   NetIn, NetOut: TMsg;
+  NetBuf:        array [0..1] of TMsg;
 
   NetClients:     array of TNetClient;
   NetClientCount: Byte = 0;
@@ -275,6 +280,8 @@ var
 begin
   NetIn.Clear();
   NetOut.Clear();
+  NetBuf[NET_UNRELIABLE].Clear();
+  NetBuf[NET_RELIABLE].Clear();
   SetLength(NetClients, 0);
   NetPeer := nil;
   NetHost := nil;
@@ -301,14 +308,62 @@ begin
 end;
 
 procedure g_Net_Flush();
+var
+  T: Integer;
+  P: pENetPacket;
+  F, Chan: enet_uint32;
+  I: Integer;
 begin
-  enet_host_flush(NetHost);
+  F := 0;
+  Chan := NET_CHAN_GAME;
+
+  if NetMode = NET_SERVER then
+    for T := NET_UNRELIABLE to NET_RELIABLE do
+    begin
+      if NetBuf[T].CurSize > 0 then
+      begin
+        P := enet_packet_create(NetBuf[T].Data, NetBuf[T].CurSize, F);
+        if not Assigned(P) then continue;
+        enet_host_broadcast(NetHost, Chan, P);
+        NetBuf[T].Clear();
+      end;
+
+      for I := Low(NetClients) to High(NetClients) do
+      begin
+        if not NetClients[I].Used then continue;
+        if NetClients[I].NetOut[T].CurSize <= 0 then continue;
+        P := enet_packet_create(NetClients[I].NetOut[T].Data, NetClients[I].NetOut[T].CurSize, F);
+        if not Assigned(P) then continue;
+        enet_peer_send(NetClients[I].Peer, Chan, P);
+        NetClients[I].NetOut[T].Clear();
+      end;
+
+      // next and last iteration is always RELIABLE
+      F := LongWord(ENET_PACKET_FLAG_RELIABLE);
+      Chan := NET_CHAN_IMPORTANT;
+    end
+  else if NetMode = NET_CLIENT then
+    for T := NET_UNRELIABLE to NET_RELIABLE do
+    begin
+      if NetBuf[T].CurSize > 0 then
+      begin
+        P := enet_packet_create(NetBuf[T].Data, NetBuf[T].CurSize, F);
+        if not Assigned(P) then continue;
+        enet_peer_send(NetPeer, Chan, P);
+        NetBuf[T].Clear();
+      end;
+      // next and last iteration is always RELIABLE
+      F := LongWord(ENET_PACKET_FLAG_RELIABLE);
+      Chan := NET_CHAN_IMPORTANT;
+    end;
 end;
 
 procedure g_Net_Cleanup();
 begin
   NetIn.Clear();
   NetOut.Clear();
+  NetBuf[NET_UNRELIABLE].Clear();
+  NetBuf[NET_RELIABLE].Clear();
 
   SetLength(NetClients, 0);
   NetClientCount := 0;
@@ -413,6 +468,8 @@ begin
 
   NetMode := NET_SERVER;
   NetOut.Clear();
+  NetBuf[NET_UNRELIABLE].Clear();
+  NetBuf[NET_RELIABLE].Clear();
 
   if NetDump then
     g_Net_DumpStart();
@@ -441,6 +498,8 @@ begin
       enet_peer_reset(NetClients[I].Peer);
       NetClients[I].Peer := nil;
       NetClients[I].Used := False;
+      NetClients[I].NetOut[NET_UNRELIABLE].Free();
+      NetClients[I].NetOut[NET_RELIABLE].Free();
     end;
 
   if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
@@ -459,34 +518,29 @@ end;
 
 procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
 var
-  P: pENetPacket;
-  F: enet_uint32;
+  T: Integer;
 begin
   if (Reliable) then
-    F := LongWord(ENET_PACKET_FLAG_RELIABLE)
+    T := NET_RELIABLE
   else
-    F := 0;
+    T := NET_UNRELIABLE;
 
   if (ID >= 0) then
   begin
     if ID > High(NetClients) then Exit;
     if NetClients[ID].Peer = nil then Exit;
-
-    P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
-    if not Assigned(P) then Exit;
-
-    enet_peer_send(NetClients[ID].Peer, Chan, P);
+    // write size first
+    NetClients[ID].NetOut[T].Write(Integer(NetOut.CurSize));
+    NetClients[ID].NetOut[T].Write(NetOut);
   end
   else
   begin
-    P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
-    if not Assigned(P) then Exit;
-
-    enet_host_broadcast(NetHost, Chan, P);
+    // write size first
+    NetBuf[T].Write(Integer(NetOut.CurSize));
+    NetBuf[T].Write(NetOut);
   end;
 
   if NetDump then g_Net_DumpSendBuffer();
-  g_Net_Flush();
   NetOut.Clear();
 end;
 
@@ -587,6 +641,8 @@ begin
         Byte(NetClients[ID].Peer^.data^) := ID;
         NetClients[ID].State := NET_STATE_AUTH;
         NetClients[ID].RCONAuth := False;
+        NetClients[ID].NetOut[NET_UNRELIABLE].Alloc(NET_BUFSIZE*2);
+        NetClients[ID].NetOut[NET_RELIABLE].Alloc(NET_BUFSIZE*2);
 
         enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
 
@@ -601,7 +657,7 @@ begin
         TC := @NetClients[ID];
 
         if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
-        g_Net_HostMsgHandler(TC, NetEvent.packet);
+        g_Net_Host_HandlePacket(TC, NetEvent.packet, g_Net_HostMsgHandler);
       end;
 
       ENET_EVENT_TYPE_DISCONNECT:
@@ -629,6 +685,8 @@ begin
         TC^.Peer := nil;
         TC^.Player := 0;
         TC^.RequestedFullUpdate := False;
+        TC^.NetOut[NET_UNRELIABLE].Free();
+        TC^.NetOut[NET_RELIABLE].Free();
 
         FreeMemory(NetEvent.peer^.data);
         NetEvent.peer^.data := nil;
@@ -693,21 +751,20 @@ end;
 
 procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
 var
-  P: pENetPacket;
-  F: enet_uint32;
+  T: Integer;
 begin
   if (Reliable) then
-    F := LongWord(ENET_PACKET_FLAG_RELIABLE)
+    T := NET_RELIABLE
   else
-    F := 0;
+    T := NET_UNRELIABLE;
 
-  P := enet_packet_create(NetOut.Data, NetOut.CurSize, F);
-  if not Assigned(P) then Exit;
+  // write size first
+  NetBuf[T].Write(Integer(NetOut.CurSize));
+  NetBuf[T].Write(NetOut);
 
-  enet_peer_send(NetPeer, Chan, P);
   if NetDump then g_Net_DumpSendBuffer();
-  g_Net_Flush();
   NetOut.Clear();
+  g_Net_Flush(); // FIXME: for now, send immediately
 end;
 
 function g_Net_Client_Update(): enet_size_t;
@@ -719,7 +776,7 @@ begin
       ENET_EVENT_TYPE_RECEIVE:
       begin
         if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
-        g_Net_ClientMsgHandler(NetEvent.packet);
+        g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientMsgHandler);
       end;
 
       ENET_EVENT_TYPE_DISCONNECT:
@@ -741,7 +798,7 @@ begin
       ENET_EVENT_TYPE_RECEIVE:
       begin
         if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
-        g_Net_ClientLightMsgHandler(NetEvent.packet);
+        g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientLightMsgHandler);
       end;
 
       ENET_EVENT_TYPE_DISCONNECT:
@@ -1290,7 +1347,11 @@ initialization
   g_Net_DownloadTimeout := 60;
   NetIn.Alloc(NET_BUFSIZE);
   NetOut.Alloc(NET_BUFSIZE);
+  NetBuf[NET_UNRELIABLE].Alloc(NET_BUFSIZE*2);
+  NetBuf[NET_RELIABLE].Alloc(NET_BUFSIZE*2);
 finalization
   NetIn.Free();
   NetOut.Free();
+  NetBuf[NET_UNRELIABLE].Free();
+  NetBuf[NET_RELIABLE].Free();
 end.
index ac57351ce2abf65d42e2cf3ddd2b73d1fb555fc4..dadef20a3acf022e4716002a1c960bb2892eb757 100644 (file)
@@ -18,24 +18,75 @@ unit g_nethandler;
 
 interface
 
-uses g_net, g_netmsg, ENet;
+uses e_msg, g_net, g_netmsg, ENet;
 
-procedure g_Net_ClientMsgHandler(P: pENetPacket);
-procedure g_Net_ClientLightMsgHandler(P: pENetPacket);
-procedure g_Net_HostMsgHandler(S: pTNetClient; P: pENetPacket);
+type
+  TNetClientMsgHandler = function (M: TMsg): Boolean;
+  TNetHostMsgHandler = function (S: pTNetClient; M: TMsg): Boolean;
+
+procedure g_Net_Client_HandlePacket(P: pENetPacket; Handler: TNetClientMsgHandler);
+procedure g_Net_Host_HandlePacket(S: pTNetClient; P: pENetPacket; Handler: TNetHostMsgHandler);
+
+function g_Net_ClientMsgHandler(NetMsg: TMsg): Boolean;
+function g_Net_ClientLightMsgHandler(NetMsg: TMsg): Boolean;
+function g_Net_HostMsgHandler(S: pTNetClient; NetMsg: TMsg): Boolean;
 
 implementation
 
-uses e_msg;
+uses sysutils, g_console;
 
-procedure g_Net_ClientMsgHandler(P: pENetPacket);
+procedure g_Net_Client_HandlePacket(P: pENetPacket; Handler: TNetClientMsgHandler);
 var
-  MID: Byte;
+  MNext: Integer;
+  MSize: LongWord;
+  MHandled: Boolean;
   NetMsg: TMsg;
 begin
   if not NetMsg.Init(P^.data, P^.dataLength, True) then
     Exit;
 
+  MNext := 0;
+
+  while NetMsg.BytesLeft() > 0 do
+  begin
+    MSize := NetMsg.ReadLongWord();
+    MNext := NetMsg.ReadCount + MSize;
+    MHandled := Handler(NetMsg); // TODO: maybe do something with this bool
+    NetMsg.Seek(MNext);
+  end;
+
+  enet_packet_destroy(P);
+end;
+
+procedure g_Net_Host_HandlePacket(S: pTNetClient; P: pENetPacket; Handler: TNetHostMsgHandler);
+var
+  MNext: Integer;
+  MSize: LongWord;
+  MHandled: Boolean;
+  NetMsg: TMsg;
+begin
+  if not NetMsg.Init(P^.data, P^.dataLength, True) then
+    Exit;
+
+  MNext := 0;
+
+  while NetMsg.BytesLeft() > 0 do
+  begin
+    MSize := NetMsg.ReadLongWord();
+    MNext := NetMsg.ReadCount + MSize;
+    MHandled := Handler(S, NetMsg); // TODO: maybe do something with this bool
+    NetMsg.Seek(MNext);
+  end;
+
+  enet_packet_destroy(P);
+end;
+
+
+function g_Net_ClientMsgHandler(NetMsg: TMsg): Boolean;
+var
+  MID: Byte;
+begin
+  Result := True;
   MID := NetMsg.ReadByte();
 
   case MID of
@@ -78,19 +129,20 @@ begin
 
     NET_MSG_TIME_SYNC:  MC_RECV_TimeSync(NetMsg);
     NET_MSG_VOTE_EVENT: MC_RECV_VoteEvent(NetMsg);
-  end;
 
-  enet_packet_destroy(P);
+    else
+    begin
+      Result := False;
+      g_Console_Add('unknown message ID: ' + IntToStr(MID));
+    end;
+  end;
 end;
 
-procedure g_Net_ClientLightMsgHandler(P: pENetPacket);
+function g_Net_ClientLightMsgHandler(NetMsg: TMsg): Boolean;
 var
   MID: Byte;
-  NetMsg: TMsg;
 begin
-  if not NetMsg.Init(P^.data, P^.dataLength, True) then
-    Exit;
-
+  Result := True;
   MID := NetMsg.ReadByte();
 
   case MID of
@@ -99,20 +151,18 @@ begin
 
     NET_MSG_PLR:    if NetState <> NET_STATE_AUTH then MC_RECV_PlayerCreate(NetMsg);
     NET_MSG_PLRDEL: if NetState <> NET_STATE_AUTH then MC_RECV_PlayerDelete(NetMsg);
-  end;
 
-  enet_packet_destroy(P);
+    else Result := False;
+  end;
 end;
 
-procedure g_Net_HostMsgHandler(S: pTNetClient; P: pENetPacket);
+function g_Net_HostMsgHandler(S: pTNetClient; NetMsg: TMsg): Boolean;
 var
   MID: Byte;
-  NetMsg: TMsg;
 begin
-  if not NetMsg.Init(P^.data, P^.dataLength, True) then
-    Exit;
-
+  Result := True;
   MID := NetMsg.ReadByte();
+  g_Console_Add('MID = ' + IntTOStr(MID));
 
   case MID of
     NET_MSG_INFO: MH_RECV_Info(S, NetMsg);
@@ -131,8 +181,6 @@ begin
 
     NET_MSG_VOTE_EVENT: MH_RECV_Vote(S, NetMsg);
   end;
-
-  enet_packet_destroy(P);
 end;
 
 end.
index bffbcebd729077018f5b1ad41ef34148cd80fdc7..ec78e3020b83f0328be0831527027955a9ca6b4f 100644 (file)
@@ -729,6 +729,8 @@ begin
   begin
     MH_SEND_GameEvent(NET_EV_LMS_WARMUP, (gLMSRespawnTime - gTime) div 1000, 'N', ID);
   end;
+
+  g_Net_Flush();
 end;
 
 procedure MH_SEND_Info(ID: Byte);
index 88b229aa220b888697903da121c21f01e0bbe0f4..d703ea2fc2088a829bfa3af1bf07b83d31b44f16 100644 (file)
@@ -901,6 +901,8 @@ begin
     else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
   end;
 
+  if NetMode = NET_SERVER then g_Net_Flush();
+
   g_Map_ProfilersEnd();
   g_Mons_ProfilersEnd();