diff --git a/src/game/g_net.pas b/src/game/g_net.pas
index d3e67cfd6ef34c4ad3fdfc9cc0ed8ab1c1f6b2f4..e6c1fbccb6b19deaec6cbb8e58ec3d41c9681eab 100644 (file)
--- a/src/game/g_net.pas
+++ b/src/game/g_net.pas
BANLIST_FILENAME = 'banlist.txt';
NETDUMP_FILENAME = 'netdump';
BANLIST_FILENAME = 'banlist.txt';
NETDUMP_FILENAME = 'netdump';
- {$IFDEF FREEBSD}
+ {$IF DEFINED(FREEBSD) OR DEFINED(DARWIN)}
NilThreadId = nil;
{$ELSE}
NilThreadId = 0;
{$ENDIF}
type
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;
TNetFileTransfer = record
diskName: string;
hash: TMD5Digest;
Peer: pENetPeer;
Player: Word;
RequestedFullUpdate: Boolean;
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
RCONAuth: Boolean;
Voted: Boolean;
Transfer: TNetFileTransfer; // only one transfer may be active
function g_Net_UserRequestExit: Boolean;
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_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
uses
SysUtils,
e_input, g_nethandler, g_netmsg, g_netmaster, g_player, g_window, g_console,
implementation
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,
+ g_main, g_game, g_language, g_weapons, utils, ctypes, g_system,
g_map;
const
FILE_CHUNK_SIZE = 8192;
var
g_map;
const
FILE_CHUNK_SIZE = 8192;
var
+ enet_init_success: Boolean = false;
g_Net_DownloadTimeout: Single;
trans_omsg: TMsg;
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
//**************************************************************************
//
// SERVICE FUNCTIONS
ridx: Integer;
dfn: AnsiString;
md5: TMD5Digest;
ridx: Integer;
dfn: AnsiString;
md5: TMD5Digest;
- st: TStream;
+ //st: TStream;
size: LongInt;
size: LongInt;
+ fi: TDiskFileInfo;
begin
// find client index by peer
for f := Low(NetClients) to High(NetClients) do
begin
// find client index by peer
for f := Low(NetClients) to High(NetClients) do
end;
// get resource index
ridx := msg.ReadLongInt();
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;
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 := MapsDir+gGameSettings.WAD else fname := {GameDir+'/wads/'+}gExternalResources[ridx].diskName;
if (length(fname) = 0) then
begin
e_WriteLog('Invalid filename: '+fname, TMsgType.Warning);
if (length(fname) = 0) then
begin
e_WriteLog('Invalid filename: '+fname, TMsgType.Warning);
exit;
end;
// calculate hash
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
// create file stream
tf.diskName := findDiskWad(fname);
try
trans_omsg.Clear();
dfn := findDiskWad(MapsDir+gGameSettings.WAD);
if (dfn = '') then dfn := '!wad_not_found!.wad'; //FIXME
trans_omsg.Clear();
dfn := findDiskWad(MapsDir+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;
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
// packet type
trans_omsg.Write(Byte(NTF_SERVER_MAP_INFO));
// map wad name
// map wad size
trans_omsg.Write(size);
// number of external resources for map
// 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
// external resource names
- for f := 0 to gExternalResources.Count-1 do
+ for f := 0 to High(gExternalResources) do
begin
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;
end;
// send packet
if not ftransSendServerMsg(nc^, trans_omsg) then exit;
//
// returns `false` on error or user abort
// fills:
//
// 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
// 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;
var
ev: ENetEvent;
rMsgId: Byte;
status: cint;
s: AnsiString;
rc, f: LongInt;
status: cint;
s: AnsiString;
rc, f: LongInt;
+ ri: ^TNetMapResourceInfo;
begin
begin
+ SetLength(resList, 0);
+
// send request
trans_omsg.Clear();
trans_omsg.Write(Byte(NTF_CLIENT_MAP_REQUEST));
// send request
trans_omsg.Clear();
trans_omsg.Write(Byte(NTF_CLIENT_MAP_REQUEST));
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]);
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]);
// map wad name
tf.diskName := msg.ReadString();
e_LogWritefln('g_Net_Wait_MapInfo: map wad is `%s`', [tf.diskName]);
exit;
end;
e_LogWritefln('g_Net_Wait_MapInfo: map external resource count is %d', [rc]);
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
// 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
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;
end;
- resList.append(s);
end;
e_LogWritefln('g_Net_Wait_MapInfo: got map info', []);
Result := 0; // success
end;
e_LogWritefln('g_Net_Wait_MapInfo: got map info', []);
Result := 0; // success
NetClients[N].Used := True;
NetClients[N].ID := N;
NetClients[N].RequestedFullUpdate := False;
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;
NetClients[N].RCONAuth := False;
NetClients[N].Voted := False;
NetClients[N].Player := 0;
Result := N;
end;
Result := N;
end;
+
function g_Net_Init(): Boolean;
var
F: TextFile;
function g_Net_Init(): Boolean;
var
F: TextFile;
g_Net_SaveBanList();
end;
g_Net_SaveBanList();
end;
- Result := (enet_initialize() = 0);
+ //Result := (enet_initialize() = 0);
+ Result := enet_init_success;
end;
procedure g_Net_Flush();
end;
procedure g_Net_Flush();
NetPeer := nil;
NetHost := nil;
NetPeer := nil;
NetHost := nil;
- NetMPeer := nil;
- NetMHost := nil;
+ g_Net_Slist_NetworkStopped();
+ //g_Net_Slist_Disconnect(false); // do not spam console
NetMyID := -1;
NetPlrUID1 := -1;
NetPlrUID2 := -1;
NetMyID := -1;
NetPlrUID1 := -1;
NetPlrUID2 := -1;
begin
g_Net_Cleanup();
begin
g_Net_Cleanup();
- enet_deinitialize();
+ //enet_deinitialize();
NetInitDone := False;
end;
NetInitDone := False;
end;
end;
clearNetClients(false); // don't clear array
end;
clearNetClients(false); // don't clear array
- if (NetMPeer <> nil) and (NetMHost <> nil) then g_Net_Slist_Disconnect;
+ //if (g_Net_Slist_IsConnectionActive) then g_Net_Slist_Disconnect;
+ g_Net_Slist_NetworkStopped();
if NetPongSock <> ENET_SOCKET_NULL then
enet_socket_destroy(NetPongSock);
if NetPongSock <> ENET_SOCKET_NULL then
enet_socket_destroy(NetPongSock);
NetOut.Write(Byte(Ord('F')));
NetOut.Write(NetPort);
NetOut.Write(ClTime);
NetOut.Write(Byte(Ord('F')));
NetOut.Write(NetPort);
NetOut.Write(ClTime);
- g_Net_Slist_WriteInfo();
+ //g_Net_Slist_WriteInfo();
+ TMasterHost.writeInfo(NetOut);
NPl := 0;
if gPlayer1 <> nil then Inc(NPl);
if gPlayer2 <> nil then Inc(NPl);
NPl := 0;
if gPlayer1 <> nil then Inc(NPl);
if gPlayer2 <> nil then Inc(NPl);
IP := '';
Result := 0;
IP := '';
Result := 0;
- if NetUseMaster then g_Net_Slist_Check;
+ if NetUseMaster then
+ begin
+ //g_Net_Slist_Check;
+ g_Net_Slist_Pulse();
+ end;
g_Net_Host_CheckPings;
while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
g_Net_Host_CheckPings;
while (enet_host_service(NetHost, @NetEvent, 0) > 0) do
TC^.Peer := nil;
TC^.Player := 0;
TC^.RequestedFullUpdate := False;
TC^.Peer := nil;
TC^.Player := 0;
TC^.RequestedFullUpdate := False;
+ TC^.WaitForFirstSpawn := False;
TC^.NetOut[NET_UNRELIABLE].Free();
TC^.NetOut[NET_RELIABLE].Free();
TC^.NetOut[NET_UNRELIABLE].Free();
TC^.NetOut[NET_RELIABLE].Free();
g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_DISC], [ID]));
Dec(NetClientCount);
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
+ begin
+ //g_Net_Slist_Update;
+ g_Net_Slist_Pulse();
+ end;
end;
end;
end;
end;
end;
end;
end;
// предупредить что ждем слишком долго через N секунд
end;
// предупредить что ждем слишком долго через N секунд
- TimeoutTime := GetTimer() + NET_CONNECT_TIMEOUT;
+ TimeoutTime := sys_GetTicks() + NET_CONNECT_TIMEOUT;
OuterLoop := True;
while OuterLoop do
OuterLoop := True;
while OuterLoop do
end;
end;
end;
end;
- T := GetTimer();
+ T := sys_GetTicks();
if T > TimeoutTime then
begin
TimeoutTime := T + NET_CONNECT_TIMEOUT * 100; // одного предупреждения хватит
if T > TimeoutTime then
begin
TimeoutTime := T + NET_CONNECT_TIMEOUT * 100; // одного предупреждения хватит