summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 782be84)
raw | patch | inline | side by side (parent: 782be84)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sat, 12 Oct 2019 23:33:00 +0000 (02:33 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Sun, 13 Oct 2019 00:22:55 +0000 (03:22 +0300) |
src/game/g_net.pas | patch | blob | history | |
src/game/g_res_downloader.pas | patch | blob | history |
diff --git a/src/game/g_net.pas b/src/game/g_net.pas
index 8f8488f904fad54e4e014f62db6a1afd15de329c..d3e67cfd6ef34c4ad3fdfc9cc0ed8ab1c1f6b2f4 100644 (file)
--- a/src/game/g_net.pas
+++ b/src/game/g_net.pas
function g_Net_UserRequestExit: Boolean;
-function g_Net_SendMapRequest (): Boolean;
function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; resList: TStringList): Integer;
function g_Net_RequestResFileInfo (resIndex: LongInt; out tf: TNetFileTransfer): Integer;
function g_Net_AbortResTransfer (var tf: TNetFileTransfer): Boolean;
trans_omsg: TMsg;
-{ /// SERVICE FUNCTIONS /// }
+//**************************************************************************
+//
+// SERVICE FUNCTIONS
+//
+//**************************************************************************
+
procedure clearNetClientTransfers (var nc: TNetClient);
begin
nc.Transfer.stream.Free;
clearNetClientTransfers(nc);
end;
+
procedure clearNetClients (clearArray: Boolean);
var
f: Integer;
end;
-function g_Net_FindSlot(): Integer;
-var
- I: Integer;
- F: Boolean;
- N, C: Integer;
+function g_Net_UserRequestExit (): Boolean;
begin
- N := -1;
- F := False;
- C := 0;
- for I := Low(NetClients) to High(NetClients) do
- begin
- if NetClients[I].Used then
- Inc(C)
- else
- if not F then
- begin
- F := True;
- N := I;
- end;
- end;
- if C >= NetMaxClients then
- begin
- Result := -1;
- Exit;
- end;
-
- if not F then
- begin
- if (Length(NetClients) >= NetMaxClients) then
- N := -1
- else
- begin
- SetLength(NetClients, Length(NetClients) + 1);
- N := High(NetClients);
- end;
- end;
-
- if N >= 0 then
- begin
- NetClients[N].Used := True;
- NetClients[N].ID := N;
- NetClients[N].RequestedFullUpdate := False;
- NetClients[N].RCONAuth := False;
- NetClients[N].Voted := False;
- NetClients[N].Player := 0;
- clearNetClientTransfers(NetClients[N]); // just in case
- end;
-
- Result := N;
+ Result := {e_KeyPressed(IK_SPACE) or}
+ e_KeyPressed(IK_ESCAPE) or
+ e_KeyPressed(VK_ESCAPE) or
+ e_KeyPressed(JOY0_JUMP) or
+ e_KeyPressed(JOY1_JUMP) or
+ e_KeyPressed(JOY2_JUMP) or
+ e_KeyPressed(JOY3_JUMP)
end;
-function g_Net_Init(): Boolean;
-var
- F: TextFile;
- IPstr: string;
- IP: LongWord;
-begin
- NetIn.Clear();
- NetOut.Clear();
- NetBuf[NET_UNRELIABLE].Clear();
- NetBuf[NET_RELIABLE].Clear();
- //SetLength(NetClients, 0);
- clearNetClients(true); // clear array
- NetPeer := nil;
- NetHost := nil;
- NetMyID := -1;
- NetPlrUID1 := -1;
- NetPlrUID2 := -1;
- NetAddr.port := 25666;
- SetLength(NetBannedHosts, 0);
- if FileExists(DataDir + BANLIST_FILENAME) then
- begin
- Assign(F, DataDir + BANLIST_FILENAME);
- Reset(F);
- while not EOF(F) do
- begin
- Readln(F, IPstr);
- if StrToIp(IPstr, IP) then
- g_Net_BanHost(IP);
- end;
- CloseFile(F);
- g_Net_SaveBanList();
- end;
-
- Result := (enet_initialize() = 0);
-end;
-procedure g_Net_Flush();
-var
- T: Integer;
- P: pENetPacket;
- F, Chan: enet_uint32;
- I: Integer;
-begin
- F := 0;
- Chan := NET_CHAN_GAME;
+//**************************************************************************
+//
+// file transfer declaraions and host packet processor
+//
+//**************************************************************************
- 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;
+const
+ // server packet type
+ NTF_SERVER_DONE = 10; // done with this file
+ NTF_SERVER_FILE_INFO = 11; // sent after client request
+ NTF_SERVER_CHUNK = 12; // next chunk; chunk number follows
+ NTF_SERVER_ABORT = 13; // server abort
+ NTF_SERVER_MAP_INFO = 14;
- 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;
+ // client packet type
+ NTF_CLIENT_MAP_REQUEST = 100; // map file request; also, returns list of additional wads to download
+ NTF_CLIENT_FILE_REQUEST = 101; // resource file request (by index)
+ NTF_CLIENT_ABORT = 102; // do not send requested file, or abort current transfer
+ NTF_CLIENT_START = 103; // start transfer; client may resume download by sending non-zero starting chunk
+ NTF_CLIENT_ACK = 104; // chunk ack; chunk number follows
- // 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();
+// disconnect client due to some file transfer error
+procedure killClientByFT (var nc: TNetClient);
begin
- NetIn.Clear();
- NetOut.Clear();
- NetBuf[NET_UNRELIABLE].Clear();
- NetBuf[NET_RELIABLE].Clear();
-
- //SetLength(NetClients, 0);
- clearNetClients(true); // clear array
- NetClientCount := 0;
-
- NetPeer := nil;
- NetHost := nil;
- NetMPeer := nil;
- NetMHost := nil;
- NetMyID := -1;
- NetPlrUID1 := -1;
- NetPlrUID2 := -1;
- NetState := NET_STATE_NONE;
-
- NetPongSock := ENET_SOCKET_NULL;
-
- NetTimeToMaster := 0;
- NetTimeToUpdate := 0;
- NetTimeToReliable := 0;
-
- NetMode := NET_NONE;
-
- if NetPortThread <> NilThreadId then
- WaitForThreadTerminate(NetPortThread, 66666);
-
- NetPortThread := NilThreadId;
- g_Net_UnforwardPorts();
-
- if NetDump then
- g_Net_DumpEnd();
+ e_LogWritefln('disconnected client #%d due to file transfer error', [nc.ID], TMsgType.Warning);
+ enet_peer_disconnect(nc.Peer, NET_DISC_FILE_TIMEOUT);
+ clearNetClientTransfers(nc);
end;
-procedure g_Net_Free();
-begin
- g_Net_Cleanup();
- enet_deinitialize();
- NetInitDone := False;
+// send file transfer message from server to client
+function ftransSendServerMsg (var nc: TNetClient; var m: TMsg): Boolean;
+var
+ pkt: PENetPacket;
+begin
+ result := false;
+ if (m.CurSize < 1) then exit;
+ pkt := enet_packet_create(m.Data, m.CurSize, ENET_PACKET_FLAG_RELIABLE);
+ if not Assigned(pkt) then begin killClientByFT(nc); exit; end;
+ if (enet_peer_send(nc.Peer, NET_CHAN_DOWNLOAD_EX, pkt) <> 0) then begin killClientByFT(nc); exit; end;
+ result := true;
end;
-{ /// SERVER FUNCTIONS /// }
-
-
-function ForwardThread(Param: Pointer): PtrInt;
+// send file transfer message from client to server
+function ftransSendClientMsg (var m: TMsg): Boolean;
+var
+ pkt: PENetPacket;
begin
- Result := 0;
- if not g_Net_ForwardPorts() then Result := -1;
+ result := false;
+ if (m.CurSize < 1) then exit;
+ pkt := enet_packet_create(m.Data, m.CurSize, ENET_PACKET_FLAG_RELIABLE);
+ if not Assigned(pkt) then exit;
+ if (enet_peer_send(NetPeer, NET_CHAN_DOWNLOAD_EX, pkt) <> 0) then exit;
+ result := true;
end;
-function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
+
+// file chunk sender
+procedure ProcessChunkSend (var nc: TNetClient);
+var
+ tf: ^TNetFileTransfer;
+ ct: Int64;
+ chunks: Integer;
+ rd: Integer;
begin
- if NetMode <> NET_NONE then
+ tf := @nc.Transfer;
+ if (tf.stream = nil) then exit;
+ ct := GetTimerMS();
+ // arbitrary timeout number
+ if (ct-tf.lastAckTime >= 5000) then
begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
- Result := False;
- Exit;
+ killClientByFT(nc);
+ exit;
end;
+ // check if we need to send something
+ if (not tf.inProgress) then exit; // waiting for the initial ack
+ // ok, we're sending chunks
+ if (tf.lastAckChunk <> tf.lastSentChunk) then exit;
+ Inc(tf.lastSentChunk);
+ // do it one chunk at a time; client ack will advance our chunk counter
+ chunks := (tf.size+tf.chunkSize-1) div tf.chunkSize;
- Result := True;
-
- g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
- if not NetInitDone then
+ if (tf.lastSentChunk > chunks) then
begin
- if (not g_Net_Init()) then
- begin
- g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
- Result := False;
- Exit;
- end
- else
- NetInitDone := True;
- end;
-
- NetAddr.host := IPAddr;
- NetAddr.port := Port;
-
- if NetForwardPorts then NetPortThread := BeginThread(ForwardThread);
-
- NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
-
- if (NetHost = nil) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
- Result := False;
- g_Net_Cleanup;
- Exit;
- end;
-
- NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
- if NetPongSock <> ENET_SOCKET_NULL then
- begin
- NetPongAddr.host := IPAddr;
- NetPongAddr.port := NET_PING_PORT;
- if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
- begin
- enet_socket_destroy(NetPongSock);
- NetPongSock := ENET_SOCKET_NULL;
- end
- else
- enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
- end;
-
- NetMode := NET_SERVER;
- NetOut.Clear();
- NetBuf[NET_UNRELIABLE].Clear();
- NetBuf[NET_RELIABLE].Clear();
-
- if NetDump then
- g_Net_DumpStart();
-end;
-
-procedure g_Net_Host_Die();
-var
- I: Integer;
-begin
- if NetMode <> NET_SERVER then Exit;
-
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
- for I := 0 to High(NetClients) do
- if NetClients[I].Used then
- enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
-
- while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
- if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
- enet_packet_destroy(NetEvent.packet);
-
- for I := 0 to High(NetClients) do
- if NetClients[I].Used then
- begin
- FreeMemory(NetClients[I].Peer^.data);
- NetClients[I].Peer^.data := nil;
- 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;
-
- clearNetClients(false); // don't clear array
- if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
- if NetPongSock <> ENET_SOCKET_NULL then
- enet_socket_destroy(NetPongSock);
-
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
- enet_host_destroy(NetHost);
-
- NetMode := NET_NONE;
-
- g_Net_Cleanup;
- e_WriteLog('NET: Server stopped', TMsgType.Notify);
-end;
-
-
-procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
-var
- T: Integer;
-begin
- if (Reliable) then
- T := NET_RELIABLE
- else
- T := NET_UNRELIABLE;
-
- if (ID >= 0) then
- begin
- if ID > High(NetClients) then Exit;
- if NetClients[ID].Peer = nil then Exit;
- // write size first
- NetClients[ID].NetOut[T].Write(Integer(NetOut.CurSize));
- NetClients[ID].NetOut[T].Write(NetOut);
- end
- else
- begin
- // write size first
- NetBuf[T].Write(Integer(NetOut.CurSize));
- NetBuf[T].Write(NetOut);
- end;
-
- if NetDump then g_Net_DumpSendBuffer();
- NetOut.Clear();
-end;
-
-procedure g_Net_Host_CheckPings();
-var
- ClAddr: ENetAddress;
- Buf: ENetBuffer;
- Len: Integer;
- ClTime: Int64;
- Ping: array [0..9] of Byte;
- NPl: Byte;
-begin
- if NetPongSock = ENET_SOCKET_NULL then Exit;
-
- Buf.data := Addr(Ping[0]);
- Buf.dataLength := 2+8;
-
- Ping[0] := 0;
-
- Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
- if Len < 0 then Exit;
-
- if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
- begin
- ClTime := Int64(Addr(Ping[2])^);
-
- NetOut.Clear();
- NetOut.Write(Byte(Ord('D')));
- NetOut.Write(Byte(Ord('F')));
- NetOut.Write(NetPort);
- NetOut.Write(ClTime);
- g_Net_Slist_WriteInfo();
- NPl := 0;
- if gPlayer1 <> nil then Inc(NPl);
- if gPlayer2 <> nil then Inc(NPl);
- NetOut.Write(NPl);
- NetOut.Write(gNumBots);
-
- Buf.data := NetOut.Data;
- Buf.dataLength := NetOut.CurSize;
- enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
-
- NetOut.Clear();
- end;
-end;
-
-
-const
- // server packet type
- NTF_SERVER_DONE = 10; // done with this file
- NTF_SERVER_FILE_INFO = 11; // sent after client request
- NTF_SERVER_CHUNK = 12; // next chunk; chunk number follows
- NTF_SERVER_ABORT = 13; // server abort
- NTF_SERVER_MAP_INFO = 14;
-
- // client packet type
- NTF_CLIENT_MAP_REQUEST = 100; // map file request; also, returns list of additional wads to download
- NTF_CLIENT_FILE_REQUEST = 101; // resource file request (by index)
- NTF_CLIENT_ABORT = 102; // do not send requested file, or abort current transfer
- NTF_CLIENT_START = 103; // start transfer; client may resume download by sending non-zero starting chunk
- NTF_CLIENT_ACK = 104; // chunk ack; chunk number follows
-
-
-procedure KillClientByFT (var nc: TNetClient);
-begin
- e_LogWritefln('disconnected client #%d due to file transfer error', [nc.ID], TMsgType.Warning);
- enet_peer_disconnect(nc.Peer, NET_DISC_FILE_TIMEOUT);
- clearNetClientTransfers(nc);
-end;
-
-
-function ftransSendServerMsg (var nc: TNetClient; var m: TMsg): Boolean;
-var
- pkt: PENetPacket;
-begin
- result := false;
- if (m.CurSize < 1) then exit;
- pkt := enet_packet_create(m.Data, m.CurSize, ENET_PACKET_FLAG_RELIABLE);
- if not Assigned(pkt) then begin KillClientByFT(nc); exit; end;
- if (enet_peer_send(nc.Peer, NET_CHAN_DOWNLOAD_EX, pkt) <> 0) then begin KillClientByFT(nc); exit; end;
- result := true;
-end;
-
-
-function ftransSendClientMsg (var m: TMsg): Boolean;
-var
- pkt: PENetPacket;
-begin
- result := false;
- if (m.CurSize < 1) then exit;
- pkt := enet_packet_create(m.Data, m.CurSize, ENET_PACKET_FLAG_RELIABLE);
- if not Assigned(pkt) then exit;
- if (enet_peer_send(NetPeer, NET_CHAN_DOWNLOAD_EX, pkt) <> 0) then exit;
- result := true;
-end;
-
-
-procedure ProcessChunkSend (var nc: TNetClient);
-var
- tf: ^TNetFileTransfer;
- ct: Int64;
- chunks: Integer;
- rd: Integer;
-begin
- tf := @nc.Transfer;
- if (tf.stream = nil) then exit;
- ct := GetTimerMS();
- // arbitrary timeout number
- if (ct-tf.lastAckTime >= 5000) then
- begin
- KillClientByFT(nc);
- exit;
- end;
- // check if we need to send something
- if (not tf.inProgress) then exit; // waiting for the initial ack
- // ok, we're sending chunks
- if (tf.lastAckChunk <> tf.lastSentChunk) then exit;
- Inc(tf.lastSentChunk);
- // do it one chunk at a time; client ack will advance our chunk counter
- chunks := (tf.size+tf.chunkSize-1) div tf.chunkSize;
-
- if (tf.lastSentChunk > chunks) then
- begin
- KillClientByFT(nc);
- exit;
+ killClientByFT(nc);
+ exit;
end;
trans_omsg.Clear();
tf.stream.ReadBuffer(tf.diskBuffer^, rd);
trans_omsg.WriteData(tf.diskBuffer, rd);
except // sorry
- KillClientByFT(nc);
+ killClientByFT(nc);
exit;
end;
end;
end;
+// server file transfer packet processor
// received packet is in `NetEvent`
procedure ProcessDownloadExPacket ();
var
if (NetEvent.packet^.dataLength = 0) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
begin
if (tf.stream <> nil) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (NetEvent.packet^.dataLength < 2) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// new transfer request; build packet
if not msg.Init(NetEvent.packet^.data+1, NetEvent.packet^.dataLength-1, True) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// get resource index
if (ridx < -1) or (ridx >= gExternalResources.Count) then
begin
e_LogWritefln('Invalid resource index %d', [ridx], TMsgType.Warning);
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (ridx < 0) then fname := MapsDir+gGameSettings.WAD else fname := GameDir+'/wads/'+gExternalResources[ridx];
if (length(fname) = 0) then
begin
e_WriteLog('Invalid filename: '+fname, TMsgType.Warning);
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
tf.diskName := findDiskWad(fname);
if (length(tf.diskName) = 0) then
begin
e_LogWritefln('NETWORK: file "%s" not found!', [fname], TMsgType.Fatal);
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// calculate hash
if (tf.stream = nil) then
begin
e_WriteLog(Format('NETWORK: file "%s" not found!', [fname]), TMsgType.Fatal);
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
e_LogWritefln('client #%d requested resource #%d (file is `%s` : `%s`)', [nc.ID, ridx, fname, tf.diskName]);
begin
if not Assigned(tf.stream) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (tf.lastSentChunk <> -1) or (tf.lastAckChunk <> -1) or (tf.inProgress) then
begin
// double ack, get lost
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (NetEvent.packet^.dataLength < 2) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// build packet
if not msg.Init(NetEvent.packet^.data+1, NetEvent.packet^.dataLength-1, True) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
chunk := msg.ReadLongInt();
if (chunk < 0) or (chunk > (tf.size+tf.chunkSize-1) div tf.chunkSize) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
e_LogWritefln('client #%d started file transfer from chunk %d', [nc.ID, chunk]);
begin
if not Assigned(tf.stream) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (tf.lastSentChunk < 0) or (not tf.inProgress) then
begin
// double ack, get lost
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
if (NetEvent.packet^.dataLength < 2) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// build packet
if not msg.Init(NetEvent.packet^.data+1, NetEvent.packet^.dataLength-1, True) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
chunk := msg.ReadLongInt();
if (chunk < 0) or (chunk > (tf.size+tf.chunkSize-1) div tf.chunkSize) then
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
// do it this way, so client may seek, or request retransfers for some reason
end;
else
begin
- KillClientByFT(nc^);
+ killClientByFT(nc^);
exit;
end;
end;
end;
-function g_Net_Host_Update(): enet_size_t;
-var
- IP: string;
- Port: Word;
- ID: Integer;
- TC: pTNetClient;
- TP: TPlayer;
- //f: Integer;
- //ctt: Int64;
-begin
- IP := '';
- Result := 0;
-
- if NetUseMaster then g_Net_Slist_Check;
- g_Net_Host_CheckPings;
+//**************************************************************************
+//
+// file transfer crap (both client and server)
+//
+//**************************************************************************
- //ctt := -GetTimerMS();
- // process file transfers
- {
- for f := Low(NetClients) to High(NetClients) do
+function getNewTimeoutEnd (): Int64;
+begin
+ result := GetTimerMS();
+ if (g_Net_DownloadTimeout <= 0) then
begin
- if (not NetClients[f].Used) then continue;
- if (NetClients[f].Transfer.stream = nil) then continue;
- ProcessChunkSend(NetClients[f]);
+ result := result+1000*60*3; // 3 minutes
+ end
+ else
+ begin
+ result := result+trunc(g_Net_DownloadTimeout*1000);
end;
- }
- {
- ctt := ctt+GetTimerMS();
- if (ctt > 1) then e_LogWritefln('all transfers: [%d]', [Integer(ctt)]);
- }
+end;
- while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
- begin
- case (NetEvent.kind) of
- ENET_EVENT_TYPE_CONNECT:
- begin
- IP := IpToStr(NetEvent.Peer^.address.host);
- Port := NetEvent.Peer^.address.port;
- g_Console_Add(_lc[I_NET_MSG] +
- Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
-
- if (NetEvent.data <> NET_PROTOCOL_VER) then
- begin
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
- _lc[I_NET_DISC_PROTOCOL]);
- NetEvent.peer^.data := GetMemory(SizeOf(Byte));
- Byte(NetEvent.peer^.data^) := 255;
- enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
- enet_host_flush(NetHost);
- Exit;
- end;
-
- ID := g_Net_FindSlot();
-
- if ID < 0 then
- begin
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
- _lc[I_NET_DISC_FULL]);
- NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
- Byte(NetEvent.peer^.data^) := 255;
- enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
- enet_host_flush(NetHost);
- Exit;
- end;
-
- NetClients[ID].Peer := NetEvent.peer;
- NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
- 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);
- clearNetClientTransfers(NetClients[ID]); // just in case
- enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
+// send map request to server, and wait for "map info" server reply
+//
+// returns `false` on error or user abort
+// fills:
+// hash
+// size
+// chunkSize
+// returns:
+// <0 on error
+// 0 on success
+// 1 on user abort
+// 2 on server abort
+// for maps, first `tf.diskName` name will be map wad name, and `tf.hash`/`tf.size` will contain map info
+function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; resList: TStringList): Integer;
+var
+ ev: ENetEvent;
+ rMsgId: Byte;
+ Ptr: Pointer;
+ msg: TMsg;
+ freePacket: Boolean = false;
+ ct, ett: Int64;
+ status: cint;
+ s: AnsiString;
+ rc, f: LongInt;
+begin
+ // send request
+ trans_omsg.Clear();
+ trans_omsg.Write(Byte(NTF_CLIENT_MAP_REQUEST));
+ if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
- Inc(NetClientCount);
- g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
+ FillChar(ev, SizeOf(ev), 0);
+ Result := -1;
+ try
+ ett := getNewTimeoutEnd();
+ repeat
+ status := enet_host_service(NetHost, @ev, 300);
+ if (status < 0) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' network error', True);
+ Result := -1;
+ exit;
end;
-
- ENET_EVENT_TYPE_RECEIVE:
+ if (status = 0) then
begin
- //e_LogWritefln('RECEIVE: chan=%u', [NetEvent.channelID]);
- if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then
- begin
- ProcessDownloadExPacket();
- end
- else
+ // check for timeout
+ ct := GetTimerMS();
+ if (ct >= ett) then
begin
- ID := Byte(NetEvent.peer^.data^);
- if ID > High(NetClients) then Exit;
- TC := @NetClients[ID];
-
- if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
- g_Net_Host_HandlePacket(TC, NetEvent.packet, g_Net_HostMsgHandler);
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' timeout reached', True);
+ Result := -1;
+ exit;
end;
- end;
-
- ENET_EVENT_TYPE_DISCONNECT:
+ end
+ else
begin
- ID := Byte(NetEvent.peer^.data^);
- if ID > High(NetClients) then Exit;
- clearNetClient(NetClients[ID]);
- TC := @NetClients[ID];
- if TC = nil then Exit;
-
- if not (TC^.Used) then Exit;
-
- TP := g_Player_Get(TC^.Player);
-
- if TP <> nil then
- begin
- TP.Lives := 0;
- TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
- g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
- e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', TMsgType.Notify);
- g_Player_Remove(TP.UID);
+ // some event
+ case ev.kind of
+ ENET_EVENT_TYPE_RECEIVE:
+ begin
+ freePacket := true;
+ if (ev.channelID <> NET_CHAN_DOWNLOAD_EX) then
+ begin
+ //e_LogWritefln('g_Net_Wait_MapInfo: skip message from non-transfer channel', []);
+ freePacket := false;
+ g_Net_Client_HandlePacket(ev.packet, g_Net_ClientLightMsgHandler);
+ if (g_Res_received_map_start < 0) then begin result := -666; exit; end;
+ end
+ else
+ begin
+ ett := getNewTimeoutEnd();
+ if (ev.packet.dataLength < 1) then
+ begin
+ e_LogWritefln('g_Net_Wait_MapInfo: invalid server packet (no data)', []);
+ Result := -1;
+ exit;
+ end;
+ Ptr := ev.packet^.data;
+ rMsgId := Byte(Ptr^);
+ e_LogWritefln('g_Net_Wait_MapInfo: got message %u from server (dataLength=%u)', [rMsgId, ev.packet^.dataLength]);
+ if (rMsgId = NTF_SERVER_FILE_INFO) then
+ begin
+ e_LogWritefln('g_Net_Wait_MapInfo: waiting for map info reply, but got file info reply', []);
+ Result := -1;
+ exit;
+ end
+ else if (rMsgId = NTF_SERVER_ABORT) then
+ begin
+ e_LogWritefln('g_Net_Wait_MapInfo: server aborted transfer', []);
+ Result := 2;
+ exit;
+ end
+ else if (rMsgId = NTF_SERVER_MAP_INFO) then
+ begin
+ e_LogWritefln('g_Net_Wait_MapInfo: creating map info packet...', []);
+ if not msg.Init(ev.packet^.data+1, ev.packet^.dataLength-1, True) then exit;
+ e_LogWritefln('g_Net_Wait_MapInfo: parsing map info packet (rd=%d; max=%d)...', [msg.ReadCount, msg.MaxSize]);
+ resList.Clear();
+ // map wad name
+ tf.diskName := msg.ReadString();
+ e_LogWritefln('g_Net_Wait_MapInfo: map wad is `%s`', [tf.diskName]);
+ // map wad md5
+ tf.hash := msg.ReadMD5();
+ // map wad size
+ tf.size := msg.ReadLongInt();
+ e_LogWritefln('g_Net_Wait_MapInfo: map wad size is %d', [tf.size]);
+ // number of external resources for map
+ rc := msg.ReadLongInt();
+ if (rc < 0) or (rc > 1024) then
+ begin
+ e_LogWritefln('g_Net_Wait_Event: invalid number of map external resources (%d)', [rc]);
+ Result := -1;
+ exit;
+ end;
+ e_LogWritefln('g_Net_Wait_MapInfo: map external resource count is %d', [rc]);
+ // external resource names
+ for f := 0 to rc-1 do
+ begin
+ s := ExtractFileName(msg.ReadString());
+ if (length(s) = 0) then
+ begin
+ Result := -1;
+ exit;
+ end;
+ resList.append(s);
+ end;
+ e_LogWritefln('g_Net_Wait_MapInfo: got map info', []);
+ Result := 0; // success
+ exit;
+ end
+ else
+ begin
+ e_LogWritefln('g_Net_Wait_Event: invalid server packet type', []);
+ Result := -1;
+ exit;
+ end;
+ end;
+ end;
+ ENET_EVENT_TYPE_DISCONNECT:
+ begin
+ if (ev.data <= NET_DISC_MAX) then
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' + _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + ev.data)], True);
+ Result := -1;
+ exit;
+ end;
+ else
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' unknown ENet event ' + IntToStr(Ord(ev.kind)), True);
+ result := -1;
+ exit;
+ end;
end;
-
- TC^.Used := False;
- TC^.State := NET_STATE_NONE;
- 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;
- g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
- Dec(NetClientCount);
-
- if NetUseMaster then g_Net_Slist_Update;
+ if (freePacket) then begin freePacket := false; enet_packet_destroy(ev.packet); end;
end;
- end;
+ ProcessLoading();
+ if g_Net_UserRequestExit() then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' user abort', True);
+ Result := 1;
+ exit;
+ end;
+ until false;
+ finally
+ if (freePacket) then enet_packet_destroy(ev.packet);
end;
end;
-{ /// CLIENT FUNCTIONS /// }
-
-
-procedure g_Net_Disconnect(Forced: Boolean = False);
-begin
- if NetMode <> NET_CLIENT then Exit;
- if (NetHost = nil) or (NetPeer = nil) then Exit;
-
- if not Forced then
- begin
- enet_peer_disconnect(NetPeer, NET_DISC_NONE);
-
- while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
- begin
- if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
- begin
- NetPeer := nil;
- break;
- end;
-
- if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
- enet_packet_destroy(NetEvent.packet);
- end;
-
- if NetPeer <> nil then
- begin
- enet_peer_reset(NetPeer);
- NetPeer := nil;
- end;
- end
- else
- begin
- e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), TMsgType.Notify);
- if (NetEvent.data <= NET_DISC_MAX) then
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
- _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
- end;
-
- if NetHost <> nil then
- begin
- enet_host_destroy(NetHost);
- NetHost := nil;
- end;
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
-
- g_Net_Cleanup;
- e_WriteLog('NET: Disconnected', TMsgType.Notify);
-end;
-
-procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
+// send file request to server, and wait for server reply
+//
+// returns `false` on error or user abort
+// fills:
+// diskName (actually, base name)
+// hash
+// size
+// chunkSize
+// returns:
+// <0 on error
+// 0 on success
+// 1 on user abort
+// 2 on server abort
+// for maps, first `tf.diskName` name will be map wad name, and `tf.hash`/`tf.size` will contain map info
+function g_Net_RequestResFileInfo (resIndex: LongInt; out tf: TNetFileTransfer): Integer;
var
- T: Integer;
+ ev: ENetEvent;
+ rMsgId: Byte;
+ Ptr: Pointer;
+ msg: TMsg;
+ freePacket: Boolean = false;
+ ct, ett: Int64;
+ status: cint;
begin
- if (Reliable) then
- T := NET_RELIABLE
- else
- T := NET_UNRELIABLE;
-
- // write size first
- NetBuf[T].Write(Integer(NetOut.CurSize));
- NetBuf[T].Write(NetOut);
-
- if NetDump then g_Net_DumpSendBuffer();
- NetOut.Clear();
- g_Net_Flush(); // FIXME: for now, send immediately
-end;
+ // send request
+ trans_omsg.Clear();
+ trans_omsg.Write(Byte(NTF_CLIENT_FILE_REQUEST));
+ trans_omsg.Write(resIndex);
+ if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
-function g_Net_Client_Update(): enet_size_t;
-begin
- Result := 0;
- while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
- begin
- case NetEvent.kind of
- ENET_EVENT_TYPE_RECEIVE:
+ FillChar(ev, SizeOf(ev), 0);
+ Result := -1;
+ try
+ ett := getNewTimeoutEnd();
+ repeat
+ status := enet_host_service(NetHost, @ev, 300);
+ if (status < 0) then
begin
- if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then continue; // ignore all download packets, they're processed by separate code
- if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
- g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientMsgHandler);
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' network error', True);
+ Result := -1;
+ exit;
end;
-
- ENET_EVENT_TYPE_DISCONNECT:
+ if (status = 0) then
begin
- g_Net_Disconnect(True);
- Result := 1;
- Exit;
- end;
- end;
- end
-end;
-
-function g_Net_Client_UpdateWhileLoading(): enet_size_t;
-begin
- Result := 0;
- while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
- begin
- case NetEvent.kind of
- ENET_EVENT_TYPE_RECEIVE:
+ // check for timeout
+ ct := GetTimerMS();
+ if (ct >= ett) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' timeout reached', True);
+ Result := -1;
+ exit;
+ end;
+ end
+ else
begin
- if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then continue; // ignore all download packets, they're processed by separate code
- if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
- g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientLightMsgHandler);
+ // some event
+ case ev.kind of
+ ENET_EVENT_TYPE_RECEIVE:
+ begin
+ freePacket := true;
+ if (ev.channelID <> NET_CHAN_DOWNLOAD_EX) then
+ begin
+ //e_LogWriteln('g_Net_Wait_Event: skip message from non-transfer channel');
+ freePacket := false;
+ g_Net_Client_HandlePacket(ev.packet, g_Net_ClientLightMsgHandler);
+ if (g_Res_received_map_start < 0) then begin result := -666; exit; end;
+ end
+ else
+ begin
+ ett := getNewTimeoutEnd();
+ if (ev.packet.dataLength < 1) then
+ begin
+ e_LogWriteln('g_Net_Wait_Event: invalid server packet (no data)');
+ Result := -1;
+ exit;
+ end;
+ Ptr := ev.packet^.data;
+ rMsgId := Byte(Ptr^);
+ e_LogWritefln('received transfer packet with id %d (%u bytes)', [rMsgId, ev.packet^.dataLength]);
+ if (rMsgId = NTF_SERVER_FILE_INFO) then
+ begin
+ if not msg.Init(ev.packet^.data+1, ev.packet^.dataLength-1, True) then exit;
+ tf.hash := msg.ReadMD5();
+ tf.size := msg.ReadLongInt();
+ tf.chunkSize := msg.ReadLongInt();
+ tf.diskName := ExtractFileName(msg.readString());
+ if (tf.size < 0) or (tf.chunkSize <> FILE_CHUNK_SIZE) or (length(tf.diskName) = 0) then
+ begin
+ e_LogWritefln('g_Net_RequestResFileInfo: invalid file info packet', []);
+ Result := -1;
+ exit;
+ end;
+ e_LogWritefln('got file info for resource #%d: size=%d; name=%s', [resIndex, tf.size, tf.diskName]);
+ Result := 0; // success
+ exit;
+ end
+ else if (rMsgId = NTF_SERVER_ABORT) then
+ begin
+ e_LogWriteln('g_Net_RequestResFileInfo: server aborted transfer');
+ Result := 2;
+ exit;
+ end
+ else if (rMsgId = NTF_SERVER_MAP_INFO) then
+ begin
+ e_LogWriteln('g_Net_RequestResFileInfo: waiting for map info reply, but got file info reply');
+ Result := -1;
+ exit;
+ end
+ else
+ begin
+ e_LogWriteln('g_Net_RequestResFileInfo: invalid server packet type');
+ Result := -1;
+ exit;
+ end;
+ end;
+ end;
+ ENET_EVENT_TYPE_DISCONNECT:
+ begin
+ if (ev.data <= NET_DISC_MAX) then
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' + _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + ev.data)], True);
+ Result := -1;
+ exit;
+ end;
+ else
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' unknown ENet event ' + IntToStr(Ord(ev.kind)), True);
+ result := -1;
+ exit;
+ end;
+ end;
+ if (freePacket) then begin freePacket := false; enet_packet_destroy(ev.packet); end;
end;
-
- ENET_EVENT_TYPE_DISCONNECT:
+ ProcessLoading();
+ if g_Net_UserRequestExit() then
begin
- g_Net_Disconnect(True);
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' user abort', True);
Result := 1;
- Exit;
+ exit;
end;
- end;
+ until false;
+ finally
+ if (freePacket) then enet_packet_destroy(ev.packet);
end;
- g_Net_Flush();
end;
-function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
-var
- OuterLoop: Boolean;
- TimeoutTime, T: Int64;
-begin
- if NetMode <> NET_NONE then
- begin
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
- Result := False;
- Exit;
- end;
-
- Result := True;
- g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
- [IP, Port]));
- if not NetInitDone then
- begin
- if (not g_Net_Init()) then
- begin
- g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
- Result := False;
- Exit;
- end
- else
- NetInitDone := True;
- end;
-
- NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
-
- if (NetHost = nil) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
- g_Net_Cleanup;
- Result := False;
- Exit;
- end;
-
- enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
- NetAddr.port := Port;
-
- NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
-
- if (NetPeer = nil) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
- enet_host_destroy(NetHost);
- g_Net_Cleanup;
- Result := False;
- Exit;
- end;
-
- // предупредить что ждем слишком долго через N секунд
- TimeoutTime := GetTimer() + NET_CONNECT_TIMEOUT;
-
- OuterLoop := True;
- while OuterLoop do
- begin
- while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
- begin
- if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
- begin
- g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
- NetMode := NET_CLIENT;
- NetOut.Clear();
- enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
- NetClientIP := IP;
- NetClientPort := Port;
- if NetDump then
- g_Net_DumpStart();
- Exit;
- end;
- end;
-
- T := GetTimer();
- if T > TimeoutTime then
- begin
- TimeoutTime := T + NET_CONNECT_TIMEOUT * 100; // одного предупреждения хватит
- g_Console_Add(_lc[I_NET_MSG_TIMEOUT_WARN], True);
- g_Console_Add(Format(_lc[I_NET_MSG_PORTS], [Integer(Port), Integer(NET_PING_PORT)]), True);
- end;
-
- ProcessLoading(true);
-
- if e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_ESCAPE) or e_KeyPressed(VK_ESCAPE) or
- e_KeyPressed(JOY0_JUMP) or e_KeyPressed(JOY1_JUMP) or e_KeyPressed(JOY2_JUMP) or e_KeyPressed(JOY3_JUMP) then
- OuterLoop := False;
- end;
-
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
- g_Console_Add(Format(_lc[I_NET_MSG_PORTS], [Integer(Port), Integer(NET_PING_PORT)]), True);
- if NetPeer <> nil then enet_peer_reset(NetPeer);
- if NetHost <> nil then
- begin
- enet_host_destroy(NetHost);
- NetHost := nil;
- end;
- g_Net_Cleanup();
- Result := False;
-end;
-
-function IpToStr(IP: LongWord): string;
-var
- Ptr: Pointer;
-begin
- Ptr := Addr(IP);
- Result := IntToStr(PByte(Ptr + 0)^) + '.';
- Result := Result + IntToStr(PByte(Ptr + 1)^) + '.';
- Result := Result + IntToStr(PByte(Ptr + 2)^) + '.';
- Result := Result + IntToStr(PByte(Ptr + 3)^);
-end;
-
-function StrToIp(IPstr: string; var IP: LongWord): Boolean;
-var
- EAddr: ENetAddress;
-begin
- Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
- IP := EAddr.host;
-end;
-
-function g_Net_Client_ByName(Name: string): pTNetClient;
-var
- a: Integer;
- pl: TPlayer;
-begin
- Result := nil;
- for a := Low(NetClients) to High(NetClients) do
- if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
- begin
- pl := g_Player_Get(NetClients[a].Player);
- if pl = nil then continue;
- if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
- if NetClients[a].Peer <> nil then
- begin
- Result := @NetClients[a];
- Exit;
- end;
- end;
-end;
-
-function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
-var
- a: Integer;
-begin
- Result := nil;
- for a := Low(NetClients) to High(NetClients) do
- if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
- if NetClients[a].Player = PID then
- begin
- Result := @NetClients[a];
- Exit;
- end;
-end;
-
-function g_Net_ClientName_ByID(ID: Integer): string;
-var
- a: Integer;
- pl: TPlayer;
-begin
- Result := '';
- if ID = NET_EVERYONE then
- Exit;
- for a := Low(NetClients) to High(NetClients) do
- if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
- begin
- pl := g_Player_Get(NetClients[a].Player);
- if pl = nil then Exit;
- Result := pl.Name;
- end;
-end;
-
-procedure g_Net_SendData(Data: AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
-var
- P: pENetPacket;
- F: enet_uint32;
- dataLength: Cardinal;
-begin
- dataLength := Length(Data);
-
- if (Reliable) then
- F := LongWord(ENET_PACKET_FLAG_RELIABLE)
- else
- F := 0;
-
- if (peer <> nil) then
- begin
- P := enet_packet_create(@Data[0], dataLength, F);
- if not Assigned(P) then Exit;
- enet_peer_send(peer, Chan, P);
- end
- else
- begin
- P := enet_packet_create(@Data[0], dataLength, F);
- if not Assigned(P) then Exit;
- enet_host_broadcast(NetHost, Chan, P);
- end;
-
- enet_host_flush(NetHost);
-end;
-
-function g_Net_UserRequestExit: Boolean;
-begin
- Result := e_KeyPressed(IK_SPACE) or
- e_KeyPressed(IK_ESCAPE) or
- e_KeyPressed(VK_ESCAPE) or
- e_KeyPressed(JOY0_JUMP) or
- e_KeyPressed(JOY1_JUMP) or
- e_KeyPressed(JOY2_JUMP) or
- e_KeyPressed(JOY3_JUMP)
-end;
-
-
-function getNewTimeoutEnd (): Int64;
-begin
- result := GetTimerMS();
- if (g_Net_DownloadTimeout <= 0) then
- begin
- result := result+1000*60*3; // 3 minutes
- end
- else
- begin
- result := result+trunc(g_Net_DownloadTimeout*1000);
- end;
-end;
-
-
-function g_Net_SendMapRequest (): Boolean;
-begin
- result := false;
- e_LogWritefln('sending map request...', []);
- // send request
- trans_omsg.Clear();
- trans_omsg.Write(Byte(NTF_CLIENT_MAP_REQUEST));
- e_LogWritefln(' request size is %d', [trans_omsg.CurSize]);
- result := ftransSendClientMsg(trans_omsg);
- if result then enet_host_flush(NetHost);
-end;
+// call this to cancel file transfer requested by `g_Net_RequestResFileInfo()`
+function g_Net_AbortResTransfer (var tf: TNetFileTransfer): Boolean;
+begin
+ result := false;
+ e_LogWritefln('aborting file transfer...', []);
+ // send request
+ trans_omsg.Clear();
+ trans_omsg.Write(Byte(NTF_CLIENT_ABORT));
+ result := ftransSendClientMsg(trans_omsg);
+ if result then enet_host_flush(NetHost);
+end;
+// call this to start file transfer requested by `g_Net_RequestResFileInfo()`
+//
// returns `false` on error or user abort
// fills:
// hash
// 1 on user abort
// 2 on server abort
// for maps, first `tf.diskName` name will be map wad name, and `tf.hash`/`tf.size` will contain map info
-function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; resList: TStringList): Integer;
+function g_Net_ReceiveResourceFile (resIndex: LongInt; var tf: TNetFileTransfer; strm: TStream): Integer;
var
ev: ENetEvent;
rMsgId: Byte;
freePacket: Boolean = false;
ct, ett: Int64;
status: cint;
- s: AnsiString;
- rc, f: LongInt;
+ nextChunk: Integer = 0;
+ chunkTotal: Integer;
+ chunk: Integer;
+ csize: Integer;
+ buf: PChar = nil;
+ resumed: Boolean;
+ //stx: Int64;
begin
+ tf.resumed := false;
+ e_LogWritefln('file `%s`, size=%d (%d)', [tf.diskName, Integer(strm.size), tf.size], TMsgType.Notify);
+ // check if we should resume downloading
+ resumed := (strm.size > tf.chunkSize) and (strm.size < tf.size);
+ // send request
+ trans_omsg.Clear();
+ trans_omsg.Write(Byte(NTF_CLIENT_START));
+ if resumed then chunk := strm.size div tf.chunkSize else chunk := 0;
+ trans_omsg.Write(LongInt(chunk));
+ if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
+
+ strm.Seek(chunk*tf.chunkSize, soFromBeginning);
+ chunkTotal := (tf.size+tf.chunkSize-1) div tf.chunkSize;
+ e_LogWritefln('receiving file `%s` (%d chunks)', [tf.diskName, chunkTotal], TMsgType.Notify);
+ g_Game_SetLoadingText('downloading "'+ExtractFileName(tf.diskName)+'"', chunkTotal, False);
+ tf.resumed := resumed;
+
+ if (chunk > 0) then g_Game_StepLoading(chunk);
+ nextChunk := chunk;
+
+ // wait for reply data
FillChar(ev, SizeOf(ev), 0);
Result := -1;
+ GetMem(buf, tf.chunkSize);
try
ett := getNewTimeoutEnd();
repeat
+ //stx := -GetTimerMS();
status := enet_host_service(NetHost, @ev, 300);
if (status < 0) then
begin
freePacket := true;
if (ev.channelID <> NET_CHAN_DOWNLOAD_EX) then
begin
- //e_LogWritefln('g_Net_Wait_MapInfo: skip message from non-transfer channel', []);
+ //e_LogWritefln('g_Net_Wait_Event: skip message from non-transfer channel', []);
freePacket := false;
g_Net_Client_HandlePacket(ev.packet, g_Net_ClientLightMsgHandler);
if (g_Res_received_map_start < 0) then begin result := -666; exit; end;
end
else
begin
+ //stx := stx+GetTimerMS();
+ //e_LogWritefln('g_Net_ReceiveResourceFile: stx=%d', [Integer(stx)]);
+ //stx := -GetTimerMS();
ett := getNewTimeoutEnd();
if (ev.packet.dataLength < 1) then
begin
- e_LogWritefln('g_Net_Wait_MapInfo: invalid server packet (no data)', []);
+ e_LogWritefln('g_Net_ReceiveResourceFile: invalid server packet (no data)', []);
Result := -1;
exit;
end;
Ptr := ev.packet^.data;
rMsgId := Byte(Ptr^);
- e_LogWritefln('g_Net_Wait_MapInfo: got message %u from server (dataLength=%u)', [rMsgId, ev.packet^.dataLength]);
- if (rMsgId = NTF_SERVER_FILE_INFO) then
+ if (rMsgId = NTF_SERVER_DONE) then
begin
- e_LogWritefln('g_Net_Wait_MapInfo: waiting for map info reply, but got file info reply', []);
- Result := -1;
+ e_LogWritefln('file transfer complete.', []);
+ result := 0;
exit;
end
- else if (rMsgId = NTF_SERVER_ABORT) then
+ else if (rMsgId = NTF_SERVER_CHUNK) then
begin
- e_LogWritefln('g_Net_Wait_MapInfo: server aborted transfer', []);
- Result := 2;
- exit;
- end
- else if (rMsgId = NTF_SERVER_MAP_INFO) then
- begin
- e_LogWritefln('g_Net_Wait_MapInfo: creating map info packet...', []);
if not msg.Init(ev.packet^.data+1, ev.packet^.dataLength-1, True) then exit;
- e_LogWritefln('g_Net_Wait_MapInfo: parsing map info packet (rd=%d; max=%d)...', [msg.ReadCount, msg.MaxSize]);
- resList.Clear();
- // map wad name
- tf.diskName := msg.ReadString();
- e_LogWritefln('g_Net_Wait_MapInfo: map wad is `%s`', [tf.diskName]);
- // map wad md5
- tf.hash := msg.ReadMD5();
- // map wad size
- tf.size := msg.ReadLongInt();
- e_LogWritefln('g_Net_Wait_MapInfo: map wad size is %d', [tf.size]);
- // number of external resources for map
- rc := msg.ReadLongInt();
- if (rc < 0) or (rc > 1024) then
+ chunk := msg.ReadLongInt();
+ csize := msg.ReadLongInt();
+ if (chunk <> nextChunk) then
begin
- e_LogWritefln('g_Net_Wait_Event: invalid number of map external resources (%d)', [rc]);
+ e_LogWritefln('received chunk %d, but expected chunk %d', [chunk, nextChunk]);
Result := -1;
exit;
end;
- e_LogWritefln('g_Net_Wait_MapInfo: map external resource count is %d', [rc]);
- // external resource names
- for f := 0 to rc-1 do
+ if (csize < 0) or (csize > tf.chunkSize) then
begin
- s := ExtractFileName(msg.ReadString());
- if (length(s) = 0) then
- begin
- Result := -1;
- exit;
- end;
- resList.append(s);
+ e_LogWritefln('received chunk with size %d, but expected chunk size is %d', [csize, tf.chunkSize]);
+ Result := -1;
+ exit;
end;
- e_LogWritefln('g_Net_Wait_MapInfo: got map info', []);
- Result := 0; // success
+ //e_LogWritefln('got chunk #%d of #%d (csize=%d)', [chunk, (tf.size+tf.chunkSize-1) div tf.chunkSize, csize]);
+ msg.ReadData(buf, csize);
+ strm.WriteBuffer(buf^, csize);
+ nextChunk := chunk+1;
+ g_Game_StepLoading();
+ // send ack
+ trans_omsg.Clear();
+ trans_omsg.Write(Byte(NTF_CLIENT_ACK));
+ trans_omsg.Write(LongInt(chunk));
+ if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
+ end
+ else if (rMsgId = NTF_SERVER_ABORT) then
+ begin
+ e_LogWritefln('g_Net_ReceiveResourceFile: server aborted transfer', []);
+ Result := 2;
exit;
end
else
begin
- e_LogWritefln('g_Net_Wait_Event: invalid server packet type', []);
+ e_LogWritefln('g_Net_ReceiveResourceFile: invalid server packet type', []);
Result := -1;
exit;
end;
+ //stx := stx+GetTimerMS();
+ //e_LogWritefln('g_Net_ReceiveResourceFile: process stx=%d', [Integer(stx)]);
end;
end;
ENET_EVENT_TYPE_DISCONNECT:
end;
until false;
finally
+ FreeMem(buf);
if (freePacket) then enet_packet_destroy(ev.packet);
end;
end;
-// returns `false` on error or user abort
-// fills:
-// diskName (actually, base name)
-// hash
-// size
-// chunkSize
-// returns:
-// <0 on error
-// 0 on success
-// 1 on user abort
-// 2 on server abort
-// for maps, first `tf.diskName` name will be map wad name, and `tf.hash`/`tf.size` will contain map info
-function g_Net_RequestResFileInfo (resIndex: LongInt; out tf: TNetFileTransfer): Integer;
+//**************************************************************************
+//
+// common functions
+//
+//**************************************************************************
+
+function g_Net_FindSlot(): Integer;
var
- ev: ENetEvent;
- rMsgId: Byte;
- Ptr: Pointer;
- msg: TMsg;
- freePacket: Boolean = false;
- ct, ett: Int64;
- status: cint;
+ I: Integer;
+ F: Boolean;
+ N, C: Integer;
begin
- // send request
- trans_omsg.Clear();
- trans_omsg.Write(Byte(NTF_CLIENT_FILE_REQUEST));
- trans_omsg.Write(resIndex);
- if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
+ N := -1;
+ F := False;
+ C := 0;
+ for I := Low(NetClients) to High(NetClients) do
+ begin
+ if NetClients[I].Used then
+ Inc(C)
+ else
+ if not F then
+ begin
+ F := True;
+ N := I;
+ end;
+ end;
+ if C >= NetMaxClients then
+ begin
+ Result := -1;
+ Exit;
+ end;
- FillChar(ev, SizeOf(ev), 0);
- Result := -1;
- try
- ett := getNewTimeoutEnd();
- repeat
- status := enet_host_service(NetHost, @ev, 300);
- if (status < 0) then
+ if not F then
+ begin
+ if (Length(NetClients) >= NetMaxClients) then
+ N := -1
+ else
+ begin
+ SetLength(NetClients, Length(NetClients) + 1);
+ N := High(NetClients);
+ end;
+ end;
+
+ if N >= 0 then
+ begin
+ NetClients[N].Used := True;
+ NetClients[N].ID := N;
+ NetClients[N].RequestedFullUpdate := False;
+ NetClients[N].RCONAuth := False;
+ NetClients[N].Voted := False;
+ NetClients[N].Player := 0;
+ clearNetClientTransfers(NetClients[N]); // just in case
+ end;
+
+ Result := N;
+end;
+
+function g_Net_Init(): Boolean;
+var
+ F: TextFile;
+ IPstr: string;
+ IP: LongWord;
+begin
+ NetIn.Clear();
+ NetOut.Clear();
+ NetBuf[NET_UNRELIABLE].Clear();
+ NetBuf[NET_RELIABLE].Clear();
+ //SetLength(NetClients, 0);
+ clearNetClients(true); // clear array
+ NetPeer := nil;
+ NetHost := nil;
+ NetMyID := -1;
+ NetPlrUID1 := -1;
+ NetPlrUID2 := -1;
+ NetAddr.port := 25666;
+ SetLength(NetBannedHosts, 0);
+ if FileExists(DataDir + BANLIST_FILENAME) then
+ begin
+ Assign(F, DataDir + BANLIST_FILENAME);
+ Reset(F);
+ while not EOF(F) do
+ begin
+ Readln(F, IPstr);
+ if StrToIp(IPstr, IP) then
+ g_Net_BanHost(IP);
+ end;
+ CloseFile(F);
+ g_Net_SaveBanList();
+ end;
+
+ Result := (enet_initialize() = 0);
+end;
+
+procedure g_Net_Flush();
+var
+ T: Integer;
+ P: pENetPacket;
+ F, Chan: enet_uint32;
+ I: Integer;
+begin
+ 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
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' network error', True);
- Result := -1;
- exit;
+ 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;
- if (status = 0) then
+
+ for I := Low(NetClients) to High(NetClients) do
begin
- // check for timeout
- ct := GetTimerMS();
- if (ct >= ett) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' timeout reached', True);
- Result := -1;
- exit;
- end;
- end
- else
+ 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
- // some event
- case ev.kind of
- ENET_EVENT_TYPE_RECEIVE:
- begin
- freePacket := true;
- if (ev.channelID <> NET_CHAN_DOWNLOAD_EX) then
- begin
- //e_LogWriteln('g_Net_Wait_Event: skip message from non-transfer channel');
- freePacket := false;
- g_Net_Client_HandlePacket(ev.packet, g_Net_ClientLightMsgHandler);
- if (g_Res_received_map_start < 0) then begin result := -666; exit; end;
- end
- else
- begin
- ett := getNewTimeoutEnd();
- if (ev.packet.dataLength < 1) then
- begin
- e_LogWriteln('g_Net_Wait_Event: invalid server packet (no data)');
- Result := -1;
- exit;
- end;
- Ptr := ev.packet^.data;
- rMsgId := Byte(Ptr^);
- e_LogWritefln('received transfer packet with id %d (%u bytes)', [rMsgId, ev.packet^.dataLength]);
- if (rMsgId = NTF_SERVER_FILE_INFO) then
- begin
- if not msg.Init(ev.packet^.data+1, ev.packet^.dataLength-1, True) then exit;
- tf.hash := msg.ReadMD5();
- tf.size := msg.ReadLongInt();
- tf.chunkSize := msg.ReadLongInt();
- tf.diskName := ExtractFileName(msg.readString());
- if (tf.size < 0) or (tf.chunkSize <> FILE_CHUNK_SIZE) or (length(tf.diskName) = 0) then
- begin
- e_LogWritefln('g_Net_RequestResFileInfo: invalid file info packet', []);
- Result := -1;
- exit;
- end;
- e_LogWritefln('got file info for resource #%d: size=%d; name=%s', [resIndex, tf.size, tf.diskName]);
- Result := 0; // success
- exit;
- end
- else if (rMsgId = NTF_SERVER_ABORT) then
- begin
- e_LogWriteln('g_Net_RequestResFileInfo: server aborted transfer');
- Result := 2;
- exit;
- end
- else if (rMsgId = NTF_SERVER_MAP_INFO) then
- begin
- e_LogWriteln('g_Net_RequestResFileInfo: waiting for map info reply, but got file info reply');
- Result := -1;
- exit;
- end
- else
- begin
- e_LogWriteln('g_Net_RequestResFileInfo: invalid server packet type');
- Result := -1;
- exit;
- end;
- end;
- end;
- ENET_EVENT_TYPE_DISCONNECT:
- begin
- if (ev.data <= NET_DISC_MAX) then
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' + _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + ev.data)], True);
- Result := -1;
- exit;
- end;
- else
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' unknown ENet event ' + IntToStr(Ord(ev.kind)), True);
- result := -1;
- exit;
- end;
+ 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);
+ clearNetClients(true); // clear array
+ NetClientCount := 0;
+
+ NetPeer := nil;
+ NetHost := nil;
+ NetMPeer := nil;
+ NetMHost := nil;
+ NetMyID := -1;
+ NetPlrUID1 := -1;
+ NetPlrUID2 := -1;
+ NetState := NET_STATE_NONE;
+
+ NetPongSock := ENET_SOCKET_NULL;
+
+ NetTimeToMaster := 0;
+ NetTimeToUpdate := 0;
+ NetTimeToReliable := 0;
+
+ NetMode := NET_NONE;
+
+ if NetPortThread <> NilThreadId then
+ WaitForThreadTerminate(NetPortThread, 66666);
+
+ NetPortThread := NilThreadId;
+ g_Net_UnforwardPorts();
+
+ if NetDump then
+ g_Net_DumpEnd();
+end;
+
+procedure g_Net_Free();
+begin
+ g_Net_Cleanup();
+
+ enet_deinitialize();
+ NetInitDone := False;
+end;
+
+
+//**************************************************************************
+//
+// SERVER FUNCTIONS
+//
+//**************************************************************************
+
+function ForwardThread(Param: Pointer): PtrInt;
+begin
+ Result := 0;
+ if not g_Net_ForwardPorts() then Result := -1;
+end;
+
+function g_Net_Host(IPAddr: LongWord; Port: enet_uint16; MaxClients: Cardinal = 16): Boolean;
+begin
+ if NetMode <> NET_NONE then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_INGAME]);
+ Result := False;
+ Exit;
+ end;
+
+ Result := True;
+
+ g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST], [Port]));
+ if not NetInitDone then
+ begin
+ if (not g_Net_Init()) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET]);
+ Result := False;
+ Exit;
+ end
+ else
+ NetInitDone := True;
+ end;
+
+ NetAddr.host := IPAddr;
+ NetAddr.port := Port;
+
+ if NetForwardPorts then NetPortThread := BeginThread(ForwardThread);
+
+ NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0);
+
+ if (NetHost = nil) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + Format(_lc[I_NET_ERR_HOST], [Port]));
+ Result := False;
+ g_Net_Cleanup;
+ Exit;
+ end;
+
+ NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+ if NetPongSock <> ENET_SOCKET_NULL then
+ begin
+ NetPongAddr.host := IPAddr;
+ NetPongAddr.port := NET_PING_PORT;
+ if enet_socket_bind(NetPongSock, @NetPongAddr) < 0 then
+ begin
+ enet_socket_destroy(NetPongSock);
+ NetPongSock := ENET_SOCKET_NULL;
+ end
+ else
+ enet_socket_set_option(NetPongSock, ENET_SOCKOPT_NONBLOCK, 1);
+ end;
+
+ NetMode := NET_SERVER;
+ NetOut.Clear();
+ NetBuf[NET_UNRELIABLE].Clear();
+ NetBuf[NET_RELIABLE].Clear();
+
+ if NetDump then
+ g_Net_DumpStart();
+end;
+
+procedure g_Net_Host_Die();
+var
+ I: Integer;
+begin
+ if NetMode <> NET_SERVER then Exit;
+
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DISCALL]);
+ for I := 0 to High(NetClients) do
+ if NetClients[I].Used then
+ enet_peer_disconnect(NetClients[I].Peer, NET_DISC_DOWN);
+
+ while enet_host_service(NetHost, @NetEvent, 1000) > 0 do
+ if NetEvent.kind = ENET_EVENT_TYPE_RECEIVE then
+ enet_packet_destroy(NetEvent.packet);
+
+ for I := 0 to High(NetClients) do
+ if NetClients[I].Used then
+ begin
+ FreeMemory(NetClients[I].Peer^.data);
+ NetClients[I].Peer^.data := nil;
+ 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;
+
+ clearNetClients(false); // don't clear array
+ if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
+ if NetPongSock <> ENET_SOCKET_NULL then
+ enet_socket_destroy(NetPongSock);
+
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_DIE]);
+ enet_host_destroy(NetHost);
+
+ NetMode := NET_NONE;
+
+ g_Net_Cleanup;
+ e_WriteLog('NET: Server stopped', TMsgType.Notify);
+end;
+
+
+procedure g_Net_Host_Send(ID: Integer; Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
+var
+ T: Integer;
+begin
+ if (Reliable) then
+ T := NET_RELIABLE
+ else
+ T := NET_UNRELIABLE;
+
+ if (ID >= 0) then
+ begin
+ if ID > High(NetClients) then Exit;
+ if NetClients[ID].Peer = nil then Exit;
+ // write size first
+ NetClients[ID].NetOut[T].Write(Integer(NetOut.CurSize));
+ NetClients[ID].NetOut[T].Write(NetOut);
+ end
+ else
+ begin
+ // write size first
+ NetBuf[T].Write(Integer(NetOut.CurSize));
+ NetBuf[T].Write(NetOut);
+ end;
+
+ if NetDump then g_Net_DumpSendBuffer();
+ NetOut.Clear();
+end;
+
+procedure g_Net_Host_CheckPings();
+var
+ ClAddr: ENetAddress;
+ Buf: ENetBuffer;
+ Len: Integer;
+ ClTime: Int64;
+ Ping: array [0..9] of Byte;
+ NPl: Byte;
+begin
+ if NetPongSock = ENET_SOCKET_NULL then Exit;
+
+ Buf.data := Addr(Ping[0]);
+ Buf.dataLength := 2+8;
+
+ Ping[0] := 0;
+
+ Len := enet_socket_receive(NetPongSock, @ClAddr, @Buf, 1);
+ if Len < 0 then Exit;
+
+ if (Ping[0] = Ord('D')) and (Ping[1] = Ord('F')) then
+ begin
+ ClTime := Int64(Addr(Ping[2])^);
+
+ NetOut.Clear();
+ NetOut.Write(Byte(Ord('D')));
+ NetOut.Write(Byte(Ord('F')));
+ NetOut.Write(NetPort);
+ NetOut.Write(ClTime);
+ g_Net_Slist_WriteInfo();
+ NPl := 0;
+ if gPlayer1 <> nil then Inc(NPl);
+ if gPlayer2 <> nil then Inc(NPl);
+ NetOut.Write(NPl);
+ NetOut.Write(gNumBots);
+
+ Buf.data := NetOut.Data;
+ Buf.dataLength := NetOut.CurSize;
+ enet_socket_send(NetPongSock, @ClAddr, @Buf, 1);
+
+ NetOut.Clear();
+ end;
+end;
+
+
+function g_Net_Host_Update(): enet_size_t;
+var
+ IP: string;
+ Port: Word;
+ ID: Integer;
+ TC: pTNetClient;
+ TP: TPlayer;
+begin
+ IP := '';
+ Result := 0;
+
+ if NetUseMaster then g_Net_Slist_Check;
+ g_Net_Host_CheckPings;
+
+ while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
+ begin
+ case (NetEvent.kind) of
+ ENET_EVENT_TYPE_CONNECT:
+ begin
+ IP := IpToStr(NetEvent.Peer^.address.host);
+ Port := NetEvent.Peer^.address.port;
+ g_Console_Add(_lc[I_NET_MSG] +
+ Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port]));
+
+ if (NetEvent.data <> NET_PROTOCOL_VER) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
+ _lc[I_NET_DISC_PROTOCOL]);
+ NetEvent.peer^.data := GetMemory(SizeOf(Byte));
+ Byte(NetEvent.peer^.data^) := 255;
+ enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL);
+ enet_host_flush(NetHost);
+ Exit;
end;
- if (freePacket) then begin freePacket := false; enet_packet_destroy(ev.packet); end;
+
+ ID := g_Net_FindSlot();
+
+ if ID < 0 then
+ begin
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] +
+ _lc[I_NET_DISC_FULL]);
+ NetEvent.Peer^.data := GetMemory(SizeOf(Byte));
+ Byte(NetEvent.peer^.data^) := 255;
+ enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL);
+ enet_host_flush(NetHost);
+ Exit;
+ end;
+
+ NetClients[ID].Peer := NetEvent.peer;
+ NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte));
+ 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);
+ clearNetClientTransfers(NetClients[ID]); // just in case
+
+ enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
+
+ Inc(NetClientCount);
+ g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_ADD], [ID]));
+ end;
+
+ ENET_EVENT_TYPE_RECEIVE:
+ begin
+ //e_LogWritefln('RECEIVE: chan=%u', [NetEvent.channelID]);
+ if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then
+ begin
+ ProcessDownloadExPacket();
+ end
+ else
+ begin
+ ID := Byte(NetEvent.peer^.data^);
+ if ID > High(NetClients) then Exit;
+ TC := @NetClients[ID];
+
+ if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
+ g_Net_Host_HandlePacket(TC, NetEvent.packet, g_Net_HostMsgHandler);
+ end;
+ end;
+
+ ENET_EVENT_TYPE_DISCONNECT:
+ begin
+ ID := Byte(NetEvent.peer^.data^);
+ if ID > High(NetClients) then Exit;
+ clearNetClient(NetClients[ID]);
+ TC := @NetClients[ID];
+ if TC = nil then Exit;
+
+ if not (TC^.Used) then Exit;
+
+ TP := g_Player_Get(TC^.Player);
+
+ if TP <> nil then
+ begin
+ TP.Lives := 0;
+ TP.Kill(K_SIMPLEKILL, 0, HIT_DISCON);
+ g_Console_Add(Format(_lc[I_PLAYER_LEAVE], [TP.Name]), True);
+ e_WriteLog('NET: Client ' + TP.Name + ' [' + IntToStr(ID) + '] disconnected.', TMsgType.Notify);
+ g_Player_Remove(TP.UID);
+ end;
+
+ TC^.Used := False;
+ TC^.State := NET_STATE_NONE;
+ 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;
+ g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
+ Dec(NetClientCount);
+
+ if NetUseMaster then g_Net_Slist_Update;
end;
- ProcessLoading();
- if g_Net_UserRequestExit() then
+ end;
+ end;
+end;
+
+
+//**************************************************************************
+//
+// CLIENT FUNCTIONS
+//
+//**************************************************************************
+
+procedure g_Net_Disconnect(Forced: Boolean = False);
+begin
+ if NetMode <> NET_CLIENT then Exit;
+ if (NetHost = nil) or (NetPeer = nil) then Exit;
+
+ if not Forced then
+ begin
+ enet_peer_disconnect(NetPeer, NET_DISC_NONE);
+
+ while (enet_host_service(NetHost, @NetEvent, 1500) > 0) do
+ begin
+ if (NetEvent.kind = ENET_EVENT_TYPE_DISCONNECT) then
+ begin
+ NetPeer := nil;
+ break;
+ end;
+
+ if (NetEvent.kind = ENET_EVENT_TYPE_RECEIVE) then
+ enet_packet_destroy(NetEvent.packet);
+ end;
+
+ if NetPeer <> nil then
+ begin
+ enet_peer_reset(NetPeer);
+ NetPeer := nil;
+ end;
+ end
+ else
+ begin
+ e_WriteLog('NET: Kicked from server: ' + IntToStr(NetEvent.data), TMsgType.Notify);
+ if (NetEvent.data <= NET_DISC_MAX) then
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_KICK] +
+ _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + NetEvent.data)], True);
+ end;
+
+ if NetHost <> nil then
+ begin
+ enet_host_destroy(NetHost);
+ NetHost := nil;
+ end;
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DISC]);
+
+ g_Net_Cleanup;
+ e_WriteLog('NET: Disconnected', TMsgType.Notify);
+end;
+
+procedure g_Net_Client_Send(Reliable: Boolean; Chan: Byte = NET_CHAN_GAME);
+var
+ T: Integer;
+begin
+ if (Reliable) then
+ T := NET_RELIABLE
+ else
+ T := NET_UNRELIABLE;
+
+ // write size first
+ NetBuf[T].Write(Integer(NetOut.CurSize));
+ NetBuf[T].Write(NetOut);
+
+ if NetDump then g_Net_DumpSendBuffer();
+ NetOut.Clear();
+ g_Net_Flush(); // FIXME: for now, send immediately
+end;
+
+function g_Net_Client_Update(): enet_size_t;
+begin
+ Result := 0;
+ while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
+ begin
+ case NetEvent.kind of
+ ENET_EVENT_TYPE_RECEIVE:
+ begin
+ if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then continue; // ignore all download packets, they're processed by separate code
+ if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
+ g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientMsgHandler);
+ end;
+
+ ENET_EVENT_TYPE_DISCONNECT:
+ begin
+ g_Net_Disconnect(True);
+ Result := 1;
+ Exit;
+ end;
+ end;
+ end
+end;
+
+function g_Net_Client_UpdateWhileLoading(): enet_size_t;
+begin
+ Result := 0;
+ while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
+ begin
+ case NetEvent.kind of
+ ENET_EVENT_TYPE_RECEIVE:
+ begin
+ if (NetEvent.channelID = NET_CHAN_DOWNLOAD_EX) then continue; // ignore all download packets, they're processed by separate code
+ if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength);
+ g_Net_Client_HandlePacket(NetEvent.packet, g_Net_ClientLightMsgHandler);
+ end;
+
+ ENET_EVENT_TYPE_DISCONNECT:
+ begin
+ g_Net_Disconnect(True);
+ Result := 1;
+ Exit;
+ end;
+ end;
+ end;
+ g_Net_Flush();
+end;
+
+function g_Net_Connect(IP: string; Port: enet_uint16): Boolean;
+var
+ OuterLoop: Boolean;
+ TimeoutTime, T: Int64;
+begin
+ if NetMode <> NET_NONE then
+ begin
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_ERR_INGAME], True);
+ Result := False;
+ Exit;
+ end;
+
+ Result := True;
+
+ g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_CLIENT_CONN],
+ [IP, Port]));
+ if not NetInitDone then
+ begin
+ if (not g_Net_Init()) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_FERROR] + _lc[I_NET_ERR_ENET], True);
+ Result := False;
+ Exit;
+ end
+ else
+ NetInitDone := True;
+ end;
+
+ NetHost := enet_host_create(nil, 1, NET_CHANS, 0, 0);
+
+ if (NetHost = nil) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
+ g_Net_Cleanup;
+ Result := False;
+ Exit;
+ end;
+
+ enet_address_set_host(@NetAddr, PChar(Addr(IP[1])));
+ NetAddr.port := Port;
+
+ NetPeer := enet_host_connect(NetHost, @NetAddr, NET_CHANS, NET_PROTOCOL_VER);
+
+ if (NetPeer = nil) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CLIENT], True);
+ enet_host_destroy(NetHost);
+ g_Net_Cleanup;
+ Result := False;
+ Exit;
+ end;
+
+ // предупредить что ждем слишком долго через N секунд
+ TimeoutTime := GetTimer() + NET_CONNECT_TIMEOUT;
+
+ OuterLoop := True;
+ while OuterLoop do
+ begin
+ while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
+ begin
+ if (NetEvent.kind = ENET_EVENT_TYPE_CONNECT) then
+ begin
+ g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_CLIENT_DONE]);
+ NetMode := NET_CLIENT;
+ NetOut.Clear();
+ enet_peer_timeout(NetPeer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2);
+ NetClientIP := IP;
+ NetClientPort := Port;
+ if NetDump then
+ g_Net_DumpStart();
+ Exit;
+ end;
+ end;
+
+ T := GetTimer();
+ if T > TimeoutTime then
+ begin
+ TimeoutTime := T + NET_CONNECT_TIMEOUT * 100; // одного предупреждения хватит
+ g_Console_Add(_lc[I_NET_MSG_TIMEOUT_WARN], True);
+ g_Console_Add(Format(_lc[I_NET_MSG_PORTS], [Integer(Port), Integer(NET_PING_PORT)]), True);
+ end;
+
+ ProcessLoading(true);
+
+ if e_KeyPressed(IK_SPACE) or e_KeyPressed(IK_ESCAPE) or e_KeyPressed(VK_ESCAPE) or
+ e_KeyPressed(JOY0_JUMP) or e_KeyPressed(JOY1_JUMP) or e_KeyPressed(JOY2_JUMP) or e_KeyPressed(JOY3_JUMP) then
+ OuterLoop := False;
+ end;
+
+ g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_TIMEOUT], True);
+ g_Console_Add(Format(_lc[I_NET_MSG_PORTS], [Integer(Port), Integer(NET_PING_PORT)]), True);
+ if NetPeer <> nil then enet_peer_reset(NetPeer);
+ if NetHost <> nil then
+ begin
+ enet_host_destroy(NetHost);
+ NetHost := nil;
+ end;
+ g_Net_Cleanup();
+ Result := False;
+end;
+
+function IpToStr(IP: LongWord): string;
+var
+ Ptr: Pointer;
+begin
+ Ptr := Addr(IP);
+ Result := IntToStr(PByte(Ptr + 0)^) + '.';
+ Result := Result + IntToStr(PByte(Ptr + 1)^) + '.';
+ Result := Result + IntToStr(PByte(Ptr + 2)^) + '.';
+ Result := Result + IntToStr(PByte(Ptr + 3)^);
+end;
+
+function StrToIp(IPstr: string; var IP: LongWord): Boolean;
+var
+ EAddr: ENetAddress;
+begin
+ Result := enet_address_set_host(@EAddr, PChar(@IPstr[1])) = 0;
+ IP := EAddr.host;
+end;
+
+function g_Net_Client_ByName(Name: string): pTNetClient;
+var
+ a: Integer;
+ pl: TPlayer;
+begin
+ Result := nil;
+ for a := Low(NetClients) to High(NetClients) do
+ if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
+ begin
+ pl := g_Player_Get(NetClients[a].Player);
+ if pl = nil then continue;
+ if Copy(LowerCase(pl.Name), 1, Length(Name)) <> LowerCase(Name) then continue;
+ if NetClients[a].Peer <> nil then
begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' user abort', True);
- Result := 1;
- exit;
+ Result := @NetClients[a];
+ Exit;
end;
- until false;
- finally
- if (freePacket) then enet_packet_destroy(ev.packet);
- end;
+ end;
end;
-
-function g_Net_AbortResTransfer (var tf: TNetFileTransfer): Boolean;
+function g_Net_Client_ByPlayer(PID: Word): pTNetClient;
+var
+ a: Integer;
begin
- result := false;
- e_LogWritefln('aborting file transfer...', []);
- // send request
- trans_omsg.Clear();
- trans_omsg.Write(Byte(NTF_CLIENT_ABORT));
- result := ftransSendClientMsg(trans_omsg);
- if result then enet_host_flush(NetHost);
+ Result := nil;
+ for a := Low(NetClients) to High(NetClients) do
+ if (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
+ if NetClients[a].Player = PID then
+ begin
+ Result := @NetClients[a];
+ Exit;
+ end;
end;
-
-// returns `false` on error or user abort
-// fills:
-// hash
-// size
-// chunkSize
-// returns:
-// <0 on error
-// 0 on success
-// 1 on user abort
-// 2 on server abort
-// for maps, first `tf.diskName` name will be map wad name, and `tf.hash`/`tf.size` will contain map info
-function g_Net_ReceiveResourceFile (resIndex: LongInt; var tf: TNetFileTransfer; strm: TStream): Integer;
+function g_Net_ClientName_ByID(ID: Integer): string;
var
- ev: ENetEvent;
- rMsgId: Byte;
- Ptr: Pointer;
- msg: TMsg;
- freePacket: Boolean = false;
- ct, ett: Int64;
- status: cint;
- nextChunk: Integer = 0;
- chunkTotal: Integer;
- chunk: Integer;
- csize: Integer;
- buf: PChar = nil;
- resumed: Boolean;
- //stx: Int64;
+ a: Integer;
+ pl: TPlayer;
begin
- tf.resumed := false;
- e_LogWritefln('file `%s`, size=%d (%d)', [tf.diskName, Integer(strm.size), tf.size], TMsgType.Notify);
- // check if we should resume downloading
- resumed := (strm.size > tf.chunkSize) and (strm.size < tf.size);
- // send request
- trans_omsg.Clear();
- trans_omsg.Write(Byte(NTF_CLIENT_START));
- if resumed then chunk := strm.size div tf.chunkSize else chunk := 0;
- trans_omsg.Write(LongInt(chunk));
- if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
+ Result := '';
+ if ID = NET_EVERYONE then
+ Exit;
+ for a := Low(NetClients) to High(NetClients) do
+ if (NetClients[a].ID = ID) and (NetClients[a].Used) and (NetClients[a].State = NET_STATE_GAME) then
+ begin
+ pl := g_Player_Get(NetClients[a].Player);
+ if pl = nil then Exit;
+ Result := pl.Name;
+ end;
+end;
- strm.Seek(chunk*tf.chunkSize, soFromBeginning);
- chunkTotal := (tf.size+tf.chunkSize-1) div tf.chunkSize;
- e_LogWritefln('receiving file `%s` (%d chunks)', [tf.diskName, chunkTotal], TMsgType.Notify);
- g_Game_SetLoadingText('downloading "'+ExtractFileName(tf.diskName)+'"', chunkTotal, False);
- tf.resumed := resumed;
+procedure g_Net_SendData(Data: AByte; peer: pENetPeer; Reliable: Boolean; Chan: Byte = NET_CHAN_DOWNLOAD);
+var
+ P: pENetPacket;
+ F: enet_uint32;
+ dataLength: Cardinal;
+begin
+ dataLength := Length(Data);
- if (chunk > 0) then g_Game_StepLoading(chunk);
- nextChunk := chunk;
+ if (Reliable) then
+ F := LongWord(ENET_PACKET_FLAG_RELIABLE)
+ else
+ F := 0;
- // wait for reply data
- FillChar(ev, SizeOf(ev), 0);
- Result := -1;
- GetMem(buf, tf.chunkSize);
- try
- ett := getNewTimeoutEnd();
- repeat
- //stx := -GetTimerMS();
- status := enet_host_service(NetHost, @ev, 300);
- if (status < 0) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' network error', True);
- Result := -1;
- exit;
- end;
- if (status = 0) then
- begin
- // check for timeout
- ct := GetTimerMS();
- if (ct >= ett) then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' timeout reached', True);
- Result := -1;
- exit;
- end;
- end
- else
- begin
- // some event
- case ev.kind of
- ENET_EVENT_TYPE_RECEIVE:
- begin
- freePacket := true;
- if (ev.channelID <> NET_CHAN_DOWNLOAD_EX) then
- begin
- //e_LogWritefln('g_Net_Wait_Event: skip message from non-transfer channel', []);
- freePacket := false;
- g_Net_Client_HandlePacket(ev.packet, g_Net_ClientLightMsgHandler);
- if (g_Res_received_map_start < 0) then begin result := -666; exit; end;
- end
- else
- begin
- //stx := stx+GetTimerMS();
- //e_LogWritefln('g_Net_ReceiveResourceFile: stx=%d', [Integer(stx)]);
- //stx := -GetTimerMS();
- ett := getNewTimeoutEnd();
- if (ev.packet.dataLength < 1) then
- begin
- e_LogWritefln('g_Net_ReceiveResourceFile: invalid server packet (no data)', []);
- Result := -1;
- exit;
- end;
- Ptr := ev.packet^.data;
- rMsgId := Byte(Ptr^);
- if (rMsgId = NTF_SERVER_DONE) then
- begin
- e_LogWritefln('file transfer complete.', []);
- result := 0;
- exit;
- end
- else if (rMsgId = NTF_SERVER_CHUNK) then
- begin
- if not msg.Init(ev.packet^.data+1, ev.packet^.dataLength-1, True) then exit;
- chunk := msg.ReadLongInt();
- csize := msg.ReadLongInt();
- if (chunk <> nextChunk) then
- begin
- e_LogWritefln('received chunk %d, but expected chunk %d', [chunk, nextChunk]);
- Result := -1;
- exit;
- end;
- if (csize < 0) or (csize > tf.chunkSize) then
- begin
- e_LogWritefln('received chunk with size %d, but expected chunk size is %d', [csize, tf.chunkSize]);
- Result := -1;
- exit;
- end;
- //e_LogWritefln('got chunk #%d of #%d (csize=%d)', [chunk, (tf.size+tf.chunkSize-1) div tf.chunkSize, csize]);
- msg.ReadData(buf, csize);
- strm.WriteBuffer(buf^, csize);
- nextChunk := chunk+1;
- g_Game_StepLoading();
- // send ack
- trans_omsg.Clear();
- trans_omsg.Write(Byte(NTF_CLIENT_ACK));
- trans_omsg.Write(LongInt(chunk));
- if not ftransSendClientMsg(trans_omsg) then begin result := -1; exit; end;
- end
- else if (rMsgId = NTF_SERVER_ABORT) then
- begin
- e_LogWritefln('g_Net_ReceiveResourceFile: server aborted transfer', []);
- Result := 2;
- exit;
- end
- else
- begin
- e_LogWritefln('g_Net_ReceiveResourceFile: invalid server packet type', []);
- Result := -1;
- exit;
- end;
- //stx := stx+GetTimerMS();
- //e_LogWritefln('g_Net_ReceiveResourceFile: process stx=%d', [Integer(stx)]);
- end;
- end;
- ENET_EVENT_TYPE_DISCONNECT:
- begin
- if (ev.data <= NET_DISC_MAX) then
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' ' + _lc[TStrings_Locale(Cardinal(I_NET_DISC_NONE) + ev.data)], True);
- Result := -1;
- exit;
- end;
- else
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' unknown ENet event ' + IntToStr(Ord(ev.kind)), True);
- result := -1;
- exit;
- end;
- end;
- if (freePacket) then begin freePacket := false; enet_packet_destroy(ev.packet); end;
- end;
- ProcessLoading();
- if g_Net_UserRequestExit() then
- begin
- g_Console_Add(_lc[I_NET_MSG_ERROR] + _lc[I_NET_ERR_CONN] + ' user abort', True);
- Result := 1;
- exit;
- end;
- until false;
- finally
- FreeMem(buf);
- if (freePacket) then enet_packet_destroy(ev.packet);
+ if (peer <> nil) then
+ begin
+ P := enet_packet_create(@Data[0], dataLength, F);
+ if not Assigned(P) then Exit;
+ enet_peer_send(peer, Chan, P);
+ end
+ else
+ begin
+ P := enet_packet_create(@Data[0], dataLength, F);
+ if not Assigned(P) then Exit;
+ enet_host_broadcast(NetHost, Chan, P);
end;
-end;
+ enet_host_flush(NetHost);
+end;
function g_Net_IsHostBanned(IP: LongWord; Perm: Boolean = False): Boolean;
var
end;
{$ENDIF}
+
initialization
conRegVar('cl_downloadtimeout', @g_Net_DownloadTimeout, 0.0, 1000000.0, '', 'timeout in seconds, 0 to disable it');
SetLength(NetClients, 0);
index 098e0dd706313aa02bc4a6037541bd241f66f21b..68ceb0a71aa8be99baf763cbf8d98ba5b8ad366c 100644 (file)
g_Console_Add(Format(_lc[I_NET_MAP_DL], [FileName]));
e_WriteLog('Downloading map `' + FileName + '` from server', TMsgType.Notify);
g_Game_SetLoadingText(FileName + '...', 0, False);
- if (not g_Net_SendMapRequest()) then exit;
FileName := ExtractFileName(FileName);
if (length(FileName) = 0) then FileName := 'fucked_map_wad.wad';
+
+ // this also sends map request
res := g_Net_Wait_MapInfo(tf, resList);
if (res <> 0) then exit;