From 69ed21bb4a9cd4c59ba1acfd5971c7be5f854ee2 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 26 Aug 2019 23:18:25 +0300 Subject: [PATCH] Net: Buffer outgoing messages --- src/engine/e_msg.pas | 36 +++++++++++ src/game/g_game.pas | 1 + src/game/g_net.pas | 125 ++++++++++++++++++++++++++++---------- src/game/g_nethandler.pas | 94 +++++++++++++++++++++------- src/game/g_netmsg.pas | 2 + src/game/g_window.pas | 2 + 6 files changed, 205 insertions(+), 55 deletions(-) diff --git a/src/engine/e_msg.pas b/src/engine/e_msg.pas index 6433511..a2ecc52 100644 --- a/src/engine/e_msg.pas +++ b/src/engine/e_msg.pas @@ -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; diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 7b83243..77fd80a 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -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 diff --git a/src/game/g_net.pas b/src/game/g_net.pas index 907a5be..83c8398 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -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. diff --git a/src/game/g_nethandler.pas b/src/game/g_nethandler.pas index ac57351..dadef20 100644 --- a/src/game/g_nethandler.pas +++ b/src/game/g_nethandler.pas @@ -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. diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index bffbceb..ec78e30 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -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); diff --git a/src/game/g_window.pas b/src/game/g_window.pas index 88b229a..d703ea2 100644 --- a/src/game/g_window.pas +++ b/src/game/g_window.pas @@ -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(); -- 2.29.2