⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sv_main.pas

📁 雷神之锤2(Quake2)Delphi源码
💻 PAS
📖 第 1 页 / 共 3 页
字号:

	Result := status;
end;


(*
================
SVC_Status

Responds with all the info that qplug or qspy can see
================
*)
procedure SVC_Status ();
begin
	Netchan_OutOfBandPrint (NS_SERVER, net_from, 'print'#10'%s', [SV_StatusString()]);
{$ifdef neverever}
	Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
	Com_Printf (SV_StatusString());
	Com_EndRedirect ();
{$endif}
end;

(*
================
SVC_Ack

================
*)
procedure SVC_Ack ();
begin
	Com_Printf ('Ping acknowledge from %s'#10, [NET_AdrToString(net_from)]);
end;

(*
================
SVC_Info

Responds with short info for broadcast scans
The second parameter should be the current protocol version number.
================
*)
procedure SVC_Info ();
var
	string_: array[0..64-1] of char;
	i, count: Integer;
	version: integer;
begin
	if (maxclients^.value = 1) then
		exit;		// ignore in single player

	version := StrToInt (Cmd_Argv(1));

	if (version <> PROTOCOL_VERSION) then
		Com_sprintf (string_, sizeof(string_), '%s: wrong version'#10, [hostname^.string_, sizeof(string_)])
	else begin
		count := 0;
		for i := 0 to Round(maxclients^.value)-1 do 
			if (svs.clients^[i].state >= cs_connected) then
				Inc(count);
		Com_sprintf (string_, sizeof(string_), '%16s %8s %2i/%2i'#10, [hostname^.string_, sv.name, count, round(maxclients^.value)]);
	end;

	Netchan_OutOfBandPrint (NS_SERVER, net_from, 'info'#10'%s', [string_]);
end;

(*
================
SVC_Ping

Just responds with an acknowledgement
================
*)
procedure SVC_Ping ();
begin
	Netchan_OutOfBandPrint (NS_SERVER, net_from, 'ack', []);
end;


(*
=================
SVC_GetChallenge

Returns a challenge number that can be used
in a subsequent client_connect command.
We do this to prevent denial of service attacks that
flood the server with invalid connection IPs.  With a
challenge, they must give a valid IP address.
=================
*)
procedure SVC_GetChallenge ();
var
	i: integer;
	oldest: integer;
	oldestTime: integer;
begin
	oldest := 0;
	oldestTime := $7fffffff;

	// see if we already have a challenge for this ip
  i := 0;
  while (i < MAX_CHALLENGES) do begin
		if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) then
			break;
		if (svs.challenges[i].time < oldestTime) then begin
			oldestTime := svs.challenges[i].time;
			oldest := i;
		end;
    Inc(i);
	end;

	if (i = MAX_CHALLENGES) then begin
		// overwrite the oldest
		svs.challenges[oldest].challenge := rand() and $7fff;
		svs.challenges[oldest].adr := net_from;
		svs.challenges[oldest].time := curtime;
		i := oldest;
	end;

	// send it back
	Netchan_OutOfBandPrint (NS_SERVER, net_from, 'challenge %i', [svs.challenges[i].challenge]);
end;


(*
==================
SVC_DirectConnect

A connection request that did not come from the master
==================
*)
procedure SVC_DirectConnect ();
var
	userinfo: array[0..MAX_INFO_STRING-1] of char;
	adr: netadr_t;
	i: integer;
	cl, newcl: client_p;
	temp: client_t;
	ent: edict_p;
	edictnum: integer;
	version: integer;
	qport: integer;
	challenge: integer;
label
  gotnewcl;
begin
	adr := net_from;

	Com_DPrintf ('SVC_DirectConnect ()'#10);

	version := StrToInt(Cmd_Argv(1));
	if (version <> PROTOCOL_VERSION) then begin
		Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'Server is version %4.2f.'#10, [VERSION]);
		Com_DPrintf ('    rejected connect from version %i'#10, [version]);
		exit;
	end;

	qport := StrToInt(Cmd_Argv(2));

	challenge := StrToInt(Cmd_Argv(3));

	strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);
	userinfo[sizeof(userinfo) - 1] := #0;

	// force the IP key/value pair so the game can filter based on ip
	Info_SetValueForKey (userinfo, 'ip', NET_AdrToString(net_from));

	// attractloop servers are ONLY for local clients
	if (sv.attractloop) then begin
		if (not NET_IsLocalAddress (adr)) then begin
			Com_Printf ('Remote connect in attract loop.  Ignored.'#10);
			Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'Connection refused.'#10, []);
			exit;
		end;
	end;

	// see if the challenge is valid
	if (not NET_IsLocalAddress (adr)) then begin
		for i := 0 to MAX_CHALLENGES-1 do begin
			if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr)) then begin
				if (challenge = svs.challenges[i].challenge) then
					break;		// good
				Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'Bad challenge.'#10, []);
				exit;
			end;
		end;
		if (i = MAX_CHALLENGES) then begin
			Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'No challenge for address.'#10, []);
			exit;
		end;
	end;

	newcl := @temp;
	FillChar (newcl^, sizeof(client_t), #0);

	// if there is already a slot for this ip, reuse it
	for i := 0 to Round(maxclients^.value)-1 do begin
    cl := @svs.clients^[i];
		if (cl^.state = cs_free) then
			continue;
		if NET_CompareBaseAdr (adr, cl^.netchan.remote_address) and
			 ((cl^.netchan.qport = qport) or (adr.port = cl^.netchan.remote_address.port)) then begin
			if (not NET_IsLocalAddress (adr)) and ((svs.realtime - cl^.lastconnect) < (sv_reconnect_limit^.value * 1000)) then begin
				Com_DPrintf ('%s:reconnect rejected : too soon'#10, [NET_AdrToString (adr)]);
				exit;
			end;
			Com_Printf ('%s:reconnect'#10, [NET_AdrToString (adr)]);
			newcl := cl;
			goto gotnewcl;
		end;
	end;

	// find a client slot
	newcl := nil;

	for i := 0 to round(maxclients^.value)-1 do begin
    cl := @svs.clients^[i];
		if (cl^.state = cs_free) then begin
			newcl := cl;
			break;
		end;
	end;

	if (newcl = nil) then begin
		Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'Server is full.'#10, []);
		Com_DPrintf ('Rejected a connection.'#10);
		exit;
	end;

gotnewcl:
	// build a new connection
	// accept the new client
	// this is the only place a client_t is ever initialized
	newcl^ := temp;
	sv_client := newcl;
	edictnum := (Cardinal(newcl)-Cardinal(svs.clients)) div  sizeof(client_s) + 1;
	ent := EDICT_NUM(edictnum);
	newcl^.edict := ent;
	newcl^.challenge := challenge; // save challenge for checksumming

	// get the game a chance to reject this connection or modify the userinfo
	if (not (ge^.ClientConnect (ent, @userinfo))) then begin
		if (Info_ValueForKey (userinfo, 'rejmsg')^ <> #0) then
			Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'%s'#10'Connection refused.'#10,
				[Info_ValueForKey (userinfo, 'rejmsg')])
		else
			Netchan_OutOfBandPrint (NS_SERVER, adr, 'print'#10'Connection refused.'#10, [] );
		Com_DPrintf ('Game rejected a connection.'#10);
		exit;
	end;

	// parse some info from the info strings
	strncpy (newcl^.userinfo, userinfo, sizeof(newcl^.userinfo)-1);
	SV_UserinfoChanged (newcl);

	// send the connect packet to the client
	Netchan_OutOfBandPrint (NS_SERVER, adr, 'client_connect', []);

	Netchan_Setup (NS_SERVER, @newcl^.netchan , adr, qport);

	newcl^.state := cs_connected;

	SZ_Init (newcl^.datagram, @newcl^.datagram_buf, sizeof(newcl^.datagram_buf) );
	newcl^.datagram.allowoverflow := true;
	newcl^.lastmessage := svs.realtime;	// don't timeout
	newcl^.lastconnect := svs.realtime;
end;


function Rcon_Validate (): integer;
begin
	if (strlen (rcon_password^.string_)=0) then begin
		Result := 0;
    exit;
  end;

	if (strcmp (Cmd_Argv(1), rcon_password^.string_)<>0) then begin
		Result := 0;
    exit;
  end;

	Result := 1;
end;

(*
===============
SVC_RemoteCommand

A client issued an rcon command.
Shift down the remaining args
Redirect all printfs
===============
*)
procedure SVC_RemoteCommand ();
var
	i: integer;
	remaining: array[0..1024-1] of char;
begin
	i := Rcon_Validate ();

	if (i = 0) then
		Com_Printf ('Bad rcon from %s:'#10'%s'#10, [NET_AdrToString (net_from), PChar(Pointer(Cardinal(net_message.data)+4))])
	else
		Com_Printf ('Rcon from %s:'#10'%s'#10, [NET_AdrToString (net_from), PChar(Pointer(Cardinal(net_message.data)+4))]);

	Com_BeginRedirect (Integer(RD_PACKET), sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);

	if (Rcon_Validate ()=0) then begin
		Com_Printf ('Bad rcon_password.'#10);
  end
	else
	begin
		remaining[0] := #0;

		for i := 2 to Cmd_Argc()-1 do begin
			strcat (remaining, Cmd_Argv(i) );
			strcat (remaining, ' ');
		end;

		Cmd_ExecuteString (remaining);
	end;

	Com_EndRedirect ();
end;

(*
=================
SV_ConnectionlessPacket

A connectionless packet has four leading $ff
characters to distinguish it from a game channel.
Clients that are in the game can still send
connectionless packets.
=================
*)
procedure SV_ConnectionlessPacket ();
var
	s: pchar;
	c: pchar;
begin
	MSG_BeginReading (net_message);
	MSG_ReadLong (net_message);		// skip the -1 marker

	s := MSG_ReadStringLine (net_message);

	Cmd_TokenizeString (s, false);

	c := Cmd_Argv(0);
	Com_DPrintf ('Packet %s : %s'#10, [NET_AdrToString(net_from), c]);

	if (strcmp(c, 'ping') = 0) then
		SVC_Ping ()
	else if (strcmp(c, 'ack') = 0) then
		SVC_Ack ()
	else if (strcmp(c, 'status') = 0) then
		SVC_Status ()
	else if (strcmp(c, 'info') = 0) then
		SVC_Info ()
	else if (strcmp(c, 'getchallenge') = 0) then
		SVC_GetChallenge ()
	else if (strcmp(c, 'connect') = 0) then
		SVC_DirectConnect ()
	else if (strcmp(c, 'rcon') = 0) then
		SVC_RemoteCommand ()
	else
		Com_Printf ('bad connectionless packet from %s:'#10'%s'#10
		, [NET_AdrToString (net_from), s]);
end;


//============================================================================

(*
===================
SV_CalcPings

Updates the cl^.ping variables
===================
*)
procedure SV_CalcPings ();
var
	i, j: integer;
	cl: client_p;
	total, count: integer;
begin
	for i := 0 to round(maxclients^.value)-1 do begin
		cl := @svs.clients^[i];
		if (cl^.state <> cs_spawned ) then
			continue;

{$ifdef neverever}
		if (cl^.lastframe > 0)
			cl^.frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl^.lastframe + 1;
		else
			cl^.frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
{$endif}

		total := 0;
		count := 0;
		for j := 0 to LATENCY_COUNTS-1 do begin
			if (cl^.frame_latency[j] > 0) then begin
				Inc(count);
				total := total + cl^.frame_latency[j];
			end;
		end;
		if (count = 0) then
			cl^.ping := 0
		else
{$ifdef neverever}
			cl^.ping := total*100/count - 100;
{$else}
			cl^.ping := total div count;
{$endif}

		// let the game dll know about the ping
		cl^.edict^.client^.ping := cl^.ping;
	end;
end;


(*
===================
SV_GiveMsec

Every few frames, gives all clients an allotment of milliseconds
for their command moves.  If they exceed it, assume cheating.
===================
*)
procedure SV_GiveMsec ();
var
	i: integer;
	cl: client_p;
begin
	if (sv.framenum and 15 <> 0) then
		exit;

	for i := 0 to Round(maxclients^.value)-1 do begin
		cl := @svs.clients^[i];
		if (cl^.state = cs_free ) then
			continue;

		cl^.commandMsec := 1800;		// 1600 + some slop
	end;
end;


(*
=================
SV_ReadPackets
=================
*)
procedure SV_ReadPackets ();
var
	i: integer;
	cl: client_p;
	qport: integer;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -