X-Git-Url: https://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fgame%2Fg_net.pas;h=2e00600ae3ffea0f7dc03ddbe2655724718bba28;hp=d3e67cfd6ef34c4ad3fdfc9cc0ed8ab1c1f6b2f4;hb=93f620df25ad634372657a64d609dcaf44e718bd;hpb=bdb5b5ae754a9579cb457267ad4ce496bd606389 diff --git a/src/game/g_net.pas b/src/game/g_net.pas index d3e67cf..2e00600 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -72,13 +72,21 @@ const BANLIST_FILENAME = 'banlist.txt'; NETDUMP_FILENAME = 'netdump'; - {$IFDEF FREEBSD} + {$IF DEFINED(FREEBSD) OR DEFINED(DARWIN)} NilThreadId = nil; {$ELSE} NilThreadId = 0; {$ENDIF} type + TNetMapResourceInfo = record + wadName: AnsiString; // wad file name, without a path + size: Integer; // wad file size (-1: size and hash are not known) + hash: TMD5Digest; // wad hash + end; + + TNetMapResourceInfoArray = array of TNetMapResourceInfo; + TNetFileTransfer = record diskName: string; hash: TMD5Digest; @@ -100,6 +108,7 @@ type Peer: pENetPeer; Player: Word; RequestedFullUpdate: Boolean; + WaitForFirstSpawn: Boolean; // set to `true` in server, used to spawn a player on first full state request RCONAuth: Boolean; Voted: Boolean; Transfer: TNetFileTransfer; // only one transfer may be active @@ -138,9 +147,9 @@ var NetPongSock: ENetSocket = ENET_SOCKET_NULL; NetUseMaster: Boolean = True; - NetSlistAddr: ENetAddress; NetSlistIP: string = 'mpms.doom2d.org'; NetSlistPort: Word = 25665; + NetSlistList: string = 'deadsoftware.ru:25665'; NetClientIP: string = '127.0.0.1'; NetClientPort: Word = 25666; @@ -230,28 +239,60 @@ procedure g_Net_UnforwardPorts(); function g_Net_UserRequestExit: Boolean; -function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; resList: TStringList): Integer; +function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; var resList: TNetMapResourceInfoArray): Integer; function g_Net_RequestResFileInfo (resIndex: LongInt; out tf: TNetFileTransfer): Integer; function g_Net_AbortResTransfer (var tf: TNetFileTransfer): Boolean; function g_Net_ReceiveResourceFile (resIndex: LongInt; var tf: TNetFileTransfer; strm: TStream): Integer; +function g_Net_IsNetworkAvailable (): Boolean; +procedure g_Net_InitLowLevel (); +procedure g_Net_DeinitLowLevel (); + implementation +// *enet_host_service()* +// fuck! https://www.mail-archive.com/enet-discuss@cubik.org/msg00852.html +// tl;dr: on shitdows, we can get -1 sometimes, and it is *NOT* a failure. +// thank you, enet. let's ignore failures altogether then. + uses SysUtils, - e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console, - g_main, g_game, g_language, g_weapons, utils, ctypes, + e_input, e_res, + g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console, + g_main, g_game, g_language, g_weapons, utils, ctypes, g_system, g_map; const FILE_CHUNK_SIZE = 8192; var + enet_init_success: Boolean = false; g_Net_DownloadTimeout: Single; trans_omsg: TMsg; +function g_Net_IsNetworkAvailable (): Boolean; +begin + result := enet_init_success; +end; + +procedure g_Net_InitLowLevel (); +begin + if enet_init_success then raise Exception.Create('wuta?!'); + enet_init_success := (enet_initialize() = 0); +end; + +procedure g_Net_DeinitLowLevel (); +begin + if enet_init_success then + begin + enet_deinitialize(); + enet_init_success := false; + end; +end; + + //************************************************************************** // // SERVICE FUNCTIONS @@ -323,6 +364,7 @@ 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); + g_Net_Slist_ServerPlayerLeaves(); end; @@ -433,8 +475,9 @@ var ridx: Integer; dfn: AnsiString; md5: TMD5Digest; - st: TStream; + //st: TStream; size: LongInt; + fi: TDiskFileInfo; begin // find client index by peer for f := Low(NetClients) to High(NetClients) do @@ -483,13 +526,13 @@ begin end; // get resource index ridx := msg.ReadLongInt(); - if (ridx < -1) or (ridx >= gExternalResources.Count) then + if (ridx < -1) or (ridx >= length(gExternalResources)) then begin e_LogWritefln('Invalid resource index %d', [ridx], TMsgType.Warning); killClientByFT(nc^); exit; end; - if (ridx < 0) then fname := MapsDir+gGameSettings.WAD else fname := GameDir+'/wads/'+gExternalResources[ridx]; + if (ridx < 0) then fname := gGameSettings.WAD else fname := gExternalResources[ridx].diskName; if (length(fname) = 0) then begin e_WriteLog('Invalid filename: '+fname, TMsgType.Warning); @@ -497,7 +540,6 @@ begin exit; end; tf.diskName := findDiskWad(fname); - //if (length(tf.diskName) = 0) then tf.diskName := findDiskWad(GameDir+'/wads/'+fname); if (length(tf.diskName) = 0) then begin e_LogWritefln('NETWORK: file "%s" not found!', [fname], TMsgType.Fatal); @@ -505,8 +547,8 @@ begin exit; end; // calculate hash - //TODO: cache hashes - tf.hash := MD5File(tf.diskName); + //tf.hash := MD5File(tf.diskName); + if (ridx < 0) then tf.hash := gWADHash else tf.hash := gExternalResources[ridx].hash; // create file stream tf.diskName := findDiskWad(fname); try @@ -619,13 +661,23 @@ begin begin e_LogWritefln('client #%d requested map info', [nc.ID]); trans_omsg.Clear(); - dfn := findDiskWad(MapsDir+gGameSettings.WAD); + dfn := findDiskWad(gGameSettings.WAD); if (dfn = '') then dfn := '!wad_not_found!.wad'; //FIXME - md5 := MD5File(dfn); + //md5 := MD5File(dfn); + md5 := gWADHash; + if (not GetDiskFileInfo(dfn, fi)) then + begin + e_LogWritefln('client #%d requested map info, but i cannot get file info', [nc.ID]); + killClientByFT(nc^); + exit; + end; + size := fi.size; + { st := openDiskFileRO(dfn); if not assigned(st) then exit; //wtf?! size := st.size; st.Free; + } // packet type trans_omsg.Write(Byte(NTF_SERVER_MAP_INFO)); // map wad name @@ -635,11 +687,17 @@ begin // map wad size trans_omsg.Write(size); // number of external resources for map - trans_omsg.Write(LongInt(gExternalResources.Count)); + trans_omsg.Write(LongInt(length(gExternalResources))); // external resource names - for f := 0 to gExternalResources.Count-1 do + for f := 0 to High(gExternalResources) do begin - trans_omsg.Write(ExtractFileName(gExternalResources[f])); // GameDir+'/wads/'+ResList.Strings[i] + // old style packet + //trans_omsg.Write(ExtractFileName(gExternalResources[f])); // GameDir+'/wads/'+ResList.Strings[i] + // new style packet + trans_omsg.Write('!'); + trans_omsg.Write(LongInt(gExternalResources[f].size)); + trans_omsg.Write(gExternalResources[f].hash); + trans_omsg.Write(ExtractFileName(gExternalResources[f].diskName)); end; // send packet if not ftransSendServerMsg(nc^, trans_omsg) then exit; @@ -677,16 +735,18 @@ end; // // returns `false` on error or user abort // fills: -// hash -// size -// chunkSize +// diskName: map wad file name (without a path) +// hash: map wad hash +// size: map wad size +// chunkSize: set too +// resList: list of resource wads // 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; +function g_Net_Wait_MapInfo (var tf: TNetFileTransfer; var resList: TNetMapResourceInfoArray): Integer; var ev: ENetEvent; rMsgId: Byte; @@ -697,7 +757,10 @@ var status: cint; s: AnsiString; rc, f: LongInt; + ri: ^TNetMapResourceInfo; begin + SetLength(resList, 0); + // send request trans_omsg.Clear(); trans_omsg.Write(Byte(NTF_CLIENT_MAP_REQUEST)); @@ -709,13 +772,15 @@ begin 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; - if (status = 0) then + } + if (status <= 0) then begin // check for timeout ct := GetTimerMS(); @@ -769,7 +834,7 @@ 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(); + SetLength(resList, 0); // just in case // map wad name tf.diskName := msg.ReadString(); e_LogWritefln('g_Net_Wait_MapInfo: map wad is `%s`', [tf.diskName]); @@ -787,16 +852,28 @@ begin exit; end; e_LogWritefln('g_Net_Wait_MapInfo: map external resource count is %d', [rc]); + SetLength(resList, rc); // external resource names for f := 0 to rc-1 do begin - s := ExtractFileName(msg.ReadString()); - if (length(s) = 0) then + ri := @resList[f]; + s := msg.ReadString(); + if (length(s) = 0) then begin result := -1; exit; end; + if (s = '!') then + begin + // extended packet + ri.size := msg.ReadLongInt(); + ri.hash := msg.ReadMD5(); + ri.wadName := ExtractFileName(msg.ReadString()); + if (length(ri.wadName) = 0) or (ri.size < 0) then begin result := -1; exit; end; + end + else begin - Result := -1; - exit; + // old-style packet, only name + ri.wadName := ExtractFileName(s); + if (length(ri.wadName) = 0) then begin result := -1; exit; end; + ri.size := -1; // unknown end; - resList.append(s); end; e_LogWritefln('g_Net_Wait_MapInfo: got map info', []); Result := 0; // success @@ -876,13 +953,15 @@ begin 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; - if (status = 0) then + } + if (status <= 0) then begin // check for timeout ct := GetTimerMS(); @@ -1058,13 +1137,15 @@ begin 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 + } + if (status <= 0) then begin // check for timeout ct := GetTimerMS(); @@ -1232,6 +1313,7 @@ begin NetClients[N].Used := True; NetClients[N].ID := N; NetClients[N].RequestedFullUpdate := False; + NetClients[N].WaitForFirstSpawn := False; NetClients[N].RCONAuth := False; NetClients[N].Voted := False; NetClients[N].Player := 0; @@ -1241,11 +1323,13 @@ begin Result := N; end; + function g_Net_Init(): Boolean; var F: TextFile; IPstr: string; IP: LongWord; + path: AnsiString; begin NetIn.Clear(); NetOut.Clear(); @@ -1260,9 +1344,10 @@ begin NetPlrUID2 := -1; NetAddr.port := 25666; SetLength(NetBannedHosts, 0); - if FileExists(DataDir + BANLIST_FILENAME) then + path := BANLIST_FILENAME; + if e_FindResource(DataDirs, path) = true then begin - Assign(F, DataDir + BANLIST_FILENAME); + Assign(F, path); Reset(F); while not EOF(F) do begin @@ -1274,7 +1359,8 @@ begin g_Net_SaveBanList(); end; - Result := (enet_initialize() = 0); + //Result := (enet_initialize() = 0); + Result := enet_init_success; end; procedure g_Net_Flush(); @@ -1341,8 +1427,7 @@ begin NetPeer := nil; NetHost := nil; - NetMPeer := nil; - NetMHost := nil; + g_Net_Slist_ServerClosed(); NetMyID := -1; NetPlrUID1 := -1; NetPlrUID2 := -1; @@ -1370,7 +1455,7 @@ procedure g_Net_Free(); begin g_Net_Cleanup(); - enet_deinitialize(); + //enet_deinitialize(); NetInitDone := False; end; @@ -1477,7 +1562,7 @@ begin end; clearNetClients(false); // don't clear array - if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect; + g_Net_Slist_ServerClosed(); if NetPongSock <> ENET_SOCKET_NULL then enet_socket_destroy(NetPongSock); @@ -1547,7 +1632,7 @@ begin NetOut.Write(Byte(Ord('F'))); NetOut.Write(NetPort); NetOut.Write(ClTime); - g_Net_Slist_WriteInfo(); + TMasterHost.writeInfo(NetOut); NPl := 0; if gPlayer1 <> nil then Inc(NPl); if gPlayer2 <> nil then Inc(NPl); @@ -1574,8 +1659,8 @@ begin IP := ''; Result := 0; - if NetUseMaster then g_Net_Slist_Check; - g_Net_Host_CheckPings; + if NetUseMaster then g_Net_Slist_Pulse(); + g_Net_Host_CheckPings(); while (enet_host_service(NetHost, @NetEvent, 0) > 0) do begin @@ -1670,6 +1755,7 @@ begin TC^.Peer := nil; TC^.Player := 0; TC^.RequestedFullUpdate := False; + TC^.WaitForFirstSpawn := False; TC^.NetOut[NET_UNRELIABLE].Free(); TC^.NetOut[NET_RELIABLE].Free(); @@ -1678,7 +1764,7 @@ begin 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 NetUseMaster then g_Net_Slist_ServerPlayerLeaves(); end; end; end; @@ -1855,7 +1941,7 @@ begin end; // предупредить что ждем слишком долго через N секунд - TimeoutTime := GetTimer() + NET_CONNECT_TIMEOUT; + TimeoutTime := sys_GetTicks() + NET_CONNECT_TIMEOUT; OuterLoop := True; while OuterLoop do @@ -1876,7 +1962,7 @@ begin end; end; - T := GetTimer(); + T := sys_GetTicks(); if T > TimeoutTime then begin TimeoutTime := T + NET_CONNECT_TIMEOUT * 100; // одного предупреждения хватит @@ -2100,22 +2186,28 @@ procedure g_Net_SaveBanList(); var F: TextFile; I: Integer; + path: AnsiString; begin - Assign(F, DataDir + BANLIST_FILENAME); - Rewrite(F); - if NetBannedHosts <> nil then - for I := 0 to High(NetBannedHosts) do - if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then - Writeln(F, IpToStr(NetBannedHosts[I].IP)); - CloseFile(F); + path := e_GetWriteableDir(DataDirs); + if path <> '' then + begin + path := e_CatPath(path, BANLIST_FILENAME); + Assign(F, path); + Rewrite(F); + if NetBannedHosts <> nil then + for I := 0 to High(NetBannedHosts) do + if NetBannedHosts[I].Perm and (NetBannedHosts[I].IP > 0) then + Writeln(F, IpToStr(NetBannedHosts[I].IP)); + CloseFile(F) + end end; procedure g_Net_DumpStart(); begin if NetMode = NET_SERVER then - NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_server') + NetDumpFile := e_CreateResource(LogDirs, NETDUMP_FILENAME + '_server') else - NetDumpFile := createDiskFile(NETDUMP_FILENAME + '_client'); + NetDumpFile := e_CreateResource(LogDirs, NETDUMP_FILENAME + '_client'); end; procedure g_Net_DumpSendBuffer();