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

📄 sv_main.pas

📁 delphi编的不错的贪吃蛇
💻 PAS
📖 第 1 页 / 共 3 页
字号:
  {$ELSE}
  while (NET_GetPacket(NS_SERVER, @net_from, @net_message)) do
  {$ENDIF}
  begin
    // check for connectionless packet ($ffffffff) first
    if (PInteger(net_message.data)^ = -1) then
    begin
      SV_ConnectionlessPacket();
      continue;
    end;

    // read the qport out of the message so we can fix up
    // stupid address translating routers
    MSG_BeginReading(net_message);
    MSG_ReadLong(net_message);          // sequence number
    MSG_ReadLong(net_message);          // sequence number
    qport := MSG_ReadShort(net_message) and $FFFF;

    // check for packets from connected clients
    for i := 0 to Round(maxclients^.value) - 1 do
    begin
      cl := @svs.clients^[i];
      if (cl^.state = cs_free) then
        continue;
      if (not NET_CompareBaseAdr(net_from, cl^.netchan.remote_address)) then
        continue;
      if (cl^.netchan.qport <> qport) then
        continue;
      if (cl^.netchan.remote_address.port <> net_from.port) then
      begin
        Com_Printf('SV_ReadPackets: fixing up a translated port'#10);
        cl^.netchan.remote_address.port := net_from.port;
      end;

      if (Netchan_Process(cl^.netchan, net_message)) then
      begin
        // this is a valid, sequenced packet, so process it
        if (cl^.state <> cs_zombie) then
        begin
          cl^.lastmessage := svs.realtime; // don't timeout
          SV_ExecuteClientMessage(cl);
        end;
      end;
      break;
    end;

    if (i <> maxclients^.value) then
      continue;
  end;
end;

(*
==================
SV_CheckTimeouts

If a packet has not been received from a client for timeout^.value
seconds, drop the conneciton.  Server frames are used instead of
realtime to aprocedure dropping the local client while debugging.

When a client is normally dropped, the client_t goes into a zombie state
for a few seconds to make sure any final reliable message gets resent
if necessary
==================
*)

procedure SV_CheckTimeouts();
var
  i: integer;
  cl: client_p;
  droppoint: integer;
  zombiepoint: integer;
begin
  droppoint := svs.realtime - Round(1000 * timeout^.value);
  zombiepoint := svs.realtime - Round(1000 * zombietime^.value);

  for i := 0 to round(maxclients^.value) - 1 do
  begin
    cl := @svs.clients^[i];
    // message times may be wrong across a changelevel
    if (cl^.lastmessage > svs.realtime) then
      cl^.lastmessage := svs.realtime;

    if (cl^.state = cs_zombie) and (cl^.lastmessage < zombiepoint) then
    begin
      cl^.state := cs_free;             // can now be reused
      continue;
    end;
    if ((cl^.state = cs_connected) or (cl^.state = cs_spawned)) and
      (cl^.lastmessage < droppoint) then
    begin
      SV_BroadcastPrintf(PRINT_HIGH, '%s timed out'#10, [cl^.name]);
      SV_DropClient(cl);
      cl^.state := cs_free;             // don't bother with zombie state
    end;
  end;
end;

(*
================
SV_PrepWorldFrame

This has to be done before the world logic, because
player processing happens outside RunWorldFrame
================
*)

procedure SV_PrepWorldFrame();
var
  ent: edict_p;
  i: integer;
begin
  for i := 0 to ge^.num_edicts - 1 do
  begin
    ent := EDICT_NUM(i);
    // events only last for a single message
    ent^.s.event := entity_event_t(0);
    Inc(ent);
  end;
end;

(*
=================
SV_RunGameFrame
=================
*)

procedure SV_RunGameFrame();
begin
  if (host_speeds^.value <> 0) then
    time_before_game := Sys_Milliseconds();

  // we always need to bump framenum, even if we
  // don't run the world, otherwise the delta
  // compression can get confused when a client
  // has the "current" frame
  Inc(sv.framenum);
  sv.time := sv.framenum * 100;

  // don't run if paused
  if (sv_paused^.value = 0) or (maxclients^.value > 1) then
  begin
    ge^.RunFrame();

    // never get more than one tic behind
    if (sv.time < svs.realtime) then
    begin
      if (sv_showclamp^.value <> 0) then
        Com_Printf('sv highclamp'#10);
      svs.realtime := sv.time;
    end;
  end;

  if (host_speeds^.value <> 0) then
    time_after_game := Sys_Milliseconds();
end;

(*
==================
SV_Frame

==================
*)

procedure SV_Frame(msec: integer);
begin
  time_before_game := 0;
  time_after_game := 0;

  // if server is not active, do nothing
  if (not svs.initialized) then
    exit;

  svs.realtime := svs.realtime + msec;

  // keep the random time dependent
  Random;

  // check timeouts
  SV_CheckTimeouts();

  // get packets from clients
  SV_ReadPackets();

  // move autonomous things around if enough time has passed
  if (sv_timedemo^.value = 0) and (svs.realtime < sv.time) then
  begin
    // never let the time get too far off
    if (sv.time - svs.realtime > 100) then
    begin
      if (sv_showclamp^.value <> 0) then
        Com_Printf('sv lowclamp'#10);
      svs.realtime := sv.time - 100;
    end;
    NET_Sleep(sv.time - svs.realtime);
    exit;
  end;

  // update ping based on the last known frame from all clients
  SV_CalcPings();

  // give the clients some timeslices
  SV_GiveMsec();

  // let everything in the world think and move
  SV_RunGameFrame();

  // send messages back to the clients that had packets read this frame
  SV_SendClientMessages();

  // save the entire world state if recording a serverdemo
  SV_RecordDemoMessage();

  // send a heartbeat to the master if needed
  Master_Heartbeat();

  // clear teleport flags, etc for next frame
  SV_PrepWorldFrame();

end;

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

(*
================
Master_Heartbeat

Send a message to the master every few minutes to
let it know we are alive, and log information
================
*)

procedure Master_Heartbeat();
var
  string_: pchar;
  i: integer;
begin
  // pgm post3.19 change, cvar pointer not validated before dereferencing
  if (dedicated = nil) or (dedicated^.value = 0) then
    exit;                               // only dedicated servers send heartbeats

  // pgm post3.19 change, cvar pointer not validated before dereferencing
  if (public_server = nil) or (public_server^.value = 0) then
    exit;                               // a private dedicated game

  // check for time wraparound
  if (svs.last_heartbeat > svs.realtime) then
    svs.last_heartbeat := svs.realtime;

  if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000) then
    exit;                               // not time to send yet

  svs.last_heartbeat := svs.realtime;

  // send the same string that we would give for a status OOB command
  string_ := SV_StatusString();

  // send to group master
  for i := 0 to MAX_MASTERS - 1 do
    if (master_adr[i].port <> 0) then
    begin
      Com_Printf('Sending heartbeat to %s'#10, [NET_AdrToString(master_adr[i])]);
      Netchan_OutOfBandPrint(NS_SERVER, master_adr[i], 'heartbeat'#10'%s', [string_]);
    end;
end;

(*
=================
Master_Shutdown

Informs all masters that this server is going down
=================
*)

procedure Master_Shutdown();
var
  i: integer;
begin
  // pgm post3.19 change, cvar pointer not validated before dereferencing
  if (dedicated = nil) or (dedicated^.value = 0) then
    exit;                               // only dedicated servers send heartbeats

  // pgm post3.19 change, cvar pointer not validated before dereferencing
  if (public_server = nil) or (public_server^.value = 0) then
    exit;                               // a private dedicated game

  // send to group master
  for i := 0 to MAX_MASTERS - 1 do
    if (master_adr[i].port <> 0) then
    begin
      if (i > 0) then
        Com_Printf('Sending heartbeat to %s'#10, [NET_AdrToString(master_adr[i])]);
      Netchan_OutOfBandPrint(NS_SERVER, master_adr[i], 'shutdown', []);
    end;
end;

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

(*
=================
SV_UserinfoChanged

Pull specific info from a newly changed userinfo string
into a more C freindly form.
=================
*)

procedure SV_UserinfoChanged(cl: client_p);
var
  val: pchar;
  i: integer;
begin
  // call prog code to allow overrides
  ge^.ClientUserinfoChanged(cl^.edict, cl^.userinfo);

  // name for C code
  strncpy(cl^.name, Info_ValueForKey(cl^.userinfo, 'name'), sizeof(cl^.name) - 1);
  // mask off high bit
  for i := 0 to sizeof(cl^.name) - 1 do
    cl^.name[i] := char(byte(cl^.name[i]) and 127);

  // rate command
  val := Info_ValueForKey(cl^.userinfo, 'rate');
  if (strlen(val) <> 0) then
  begin
    i := StrToInt(val);
    cl^.rate := i;
    if (cl^.rate < 100) then
      cl^.rate := 100;
    if (cl^.rate > 15000) then
      cl^.rate := 15000;
  end
  else
    cl^.rate := 5000;

  // msg command
  val := Info_ValueForKey(cl^.userinfo, 'msg');
  if (strlen(val) <> 0) then
  begin
    cl^.messagelevel := StrToInt(val);
  end;

end;

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

(*
===============
SV_Init

Only called at quake2.exe startup, not for each game
===============
*)

procedure SV_Init();
begin
  SV_InitOperatorCommands();

  rcon_password := Cvar_Get('rcon_password', '', 0);
  Cvar_Get('skill', '1', 0);
  Cvar_Get('deathmatch', '0', CVAR_LATCH);
  Cvar_Get('coop', '0', CVAR_LATCH);
  Cvar_Get('dmflags', va('%d', [DF_INSTANT_ITEMS]), CVAR_SERVERINFO);
  Cvar_Get('fraglimit', '0', CVAR_SERVERINFO);
  Cvar_Get('timelimit', '0', CVAR_SERVERINFO);
  Cvar_Get('cheats', '0', CVAR_SERVERINFO or CVAR_LATCH);
  Cvar_Get('protocol', va('%d', [PROTOCOL_VERSION]), CVAR_SERVERINFO or CVAR_NOSET);
  ;
  maxclients := Cvar_Get('maxclients', '1', CVAR_SERVERINFO or CVAR_LATCH);
  hostname := Cvar_Get('hostname', 'noname', CVAR_SERVERINFO or CVAR_ARCHIVE);
  timeout := Cvar_Get('timeout', '125', 0);
  zombietime := Cvar_Get('zombietime', '2', 0);
  sv_showclamp := Cvar_Get('showclamp', '0', 0);
  sv_paused := Cvar_Get('paused', '0', 0);
  sv_timedemo := Cvar_Get('timedemo', '0', 0);
  sv_enforcetime := Cvar_Get('sv_enforcetime', '0', 0);
  allow_download := Cvar_Get('allow_download', '1', CVAR_ARCHIVE);
  allow_download_players := Cvar_Get('allow_download_players', '0', CVAR_ARCHIVE);
  allow_download_models := Cvar_Get('allow_download_models', '1', CVAR_ARCHIVE);
  allow_download_sounds := Cvar_Get('allow_download_sounds', '1', CVAR_ARCHIVE);
  allow_download_maps := Cvar_Get('allow_download_maps', '1', CVAR_ARCHIVE);

  sv_noreload := Cvar_Get('sv_noreload', '0', 0);

  sv_airaccelerate := Cvar_Get('sv_airaccelerate', '0', CVAR_LATCH);

  public_server := Cvar_Get('public', '0', 0);

  sv_reconnect_limit := Cvar_Get('sv_reconnect_limit', '3', CVAR_ARCHIVE);

  SZ_Init(net_message, @net_message_buffer, sizeof(net_message_buffer));
end;

(*
==================
SV_FinalMessage

Used by SV_Shutdown to send a final message to all
connected clients before the server goes down.  The messages are sent immediately,
not just stuck on the outgoing message list, because the server is going
to totally exit after returning from this function.
==================
*)

procedure SV_FinalMessage(message_: pchar; reconnect: qboolean);
var
  i: integer;
  cl: client_p;
begin
  SZ_Clear(net_message);
  MSG_WriteByte(net_message, Integer(svc_print));
  MSG_WriteByte(net_message, PRINT_HIGH);
  MSG_WriteString(net_message, message_);

  if (reconnect) then
    MSG_WriteByte(net_message, Integer(svc_reconnect))
  else
    MSG_WriteByte(net_message, Integer(svc_disconnect));

  // send it twice
  // stagger the packets to crutch operating system limited buffers

  for i := 0 to round(maxclients^.value) - 1 do
  begin
    cl := @svs.clients^[i];
    if (cl^.state >= cs_connected) then
      Netchan_Transmit(cl^.netchan, net_message.cursize, PByte(net_message.data));
  end;

  for i := 0 to Round(maxclients^.value) - 1 do
  begin
    cl := @svs.clients^[i];
    if (cl^.state >= cs_connected) then
      Netchan_Transmit(cl^.netchan, net_message.cursize, PByte(net_message.data));
  end;
end;

(*
================
SV_Shutdown

Called when each game quits,
before Sys_Quit or Sys_Error
================
*)

procedure SV_Shutdown(finalmsg: pchar; reconnect: qboolean);
begin
  if (svs.clients <> nil) then
    SV_FinalMessage(finalmsg, reconnect);

  Master_Shutdown();
  SV_ShutdownGameProgs();

  // free current level
  if (sv.demofile > 0) then
    FileClose(sv.demofile);
  sv.demofile := 0;

  FillChar(sv, sizeof(sv), #0);
  Com_SetServerState(Integer(sv.state));

  // free server static data
  if (svs.clients <> nil) then
    Z_Free(svs.clients);
  if (svs.client_entities <> nil) then
    Z_Free(svs.client_entities);
  if (svs.demofile > 0) then
    FileClose(svs.demofile);
  FillChar(svs, sizeof(svs), 0);
end;

end.

⌨️ 快捷键说明

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