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

📄 sv_main.pas

📁 delphi编的不错的贪吃蛇
💻 PAS
📖 第 1 页 / 共 3 页
字号:
begin
  Netchan_OutOfBandPrint(NS_SERVER, net_from, 'print'#10'%s', [SV_StatusString()]);
  {
   Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
   Com_Printf (SV_StatusString());
   Com_EndRedirect ();
  }
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;

    {
      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;
    }

    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
      {
        cl^.ping := total*100/count - 100;
      }
      cl^.ping := total div count;

    // 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;
begin
  {$IFDEF WIN32}
  while (NET_GetPacket(NS_SERVER, net_from, net_message)) do

⌨️ 快捷键说明

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