📄 sv_user.pas
字号:
unit sv_user;
{----------------------------------------------------------------------------}
{ }
{ File(s): sv_user.c }
{ }
{ Initial conversion by : Scott Price (scott.price@totalise.co.uk) }
{ Initial conversion on : 23-Feb-2002 }
{ }
{ This File contains part of convertion of Quake2 source to ObjectPascal. }
{ More information about this project can be found at: }
{ http://www.sulaco.co.za/quake2/ }
{ }
{ Copyright (C) 1997-2001 Id Software, Inc. }
{ }
{ This program is free software; you can redistribute it and/or }
{ modify it under the terms of the GNU General Public License }
{ as published by the Free Software Foundation; either version 2 }
{ of the License, or (at your option) any later version. }
{ }
{ This program is distributed in the hope that it will be useful, }
{ but WITHOUT ANY WARRANTY; without even the implied warranty of }
{ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. }
{ }
{ See the GNU General Public License for more details. }
{ }
{----------------------------------------------------------------------------}
{ Updated on : 16-jul-2002 }
{ Updated by : Sly }
{ - Fixed bad translation of SV_ExecuteUserCommand }
{ }
{----------------------------------------------------------------------------}
{ * Still dependent (to compile correctly) on: }
{----------------------------------------------------------------------------}
{ * TODO: }
{----------------------------------------------------------------------------}
// 25.07.2002 Juha: Proof-readed this unit
interface
uses
SysUtils,
GameUnit,
Server,
sv_init;
const
MAX_STRINGCMDS = 8;
type
ucmd_t = packed record
name: PChar;
func: procedure;
end;
ucmd_p = ^ucmd_t;
// Global functions
procedure SV_Nextserver;
procedure SV_ExecuteClientMessage(cl: client_p);
// Functions for the local array
procedure SV_New_f;
procedure SV_Configstrings_f;
procedure SV_Baselines_f;
procedure SV_Begin_f;
procedure SV_Nextserver_f;
procedure SV_Disconnect_f;
procedure SV_ShowServerinfo_f;
procedure SV_BeginDownload_f;
procedure SV_NextDownload_f;
var
sv_player: edict_p;
ucmds: array[0..9] of ucmd_t = ( // auto issued
(name: 'new'; func: SV_New_f),
(name: 'configstrings'; func: SV_Configstrings_f),
(name: 'baselines'; func: SV_Baselines_f),
(name: 'begin'; func: SV_Begin_f),
(name: 'nextserver'; func: SV_Nextserver_f),
(name: 'disconnect'; func: SV_Disconnect_f),
// issued by hand at client consoles
(name: 'info'; func: SV_ShowServerinfo_f),
(name: 'download'; func: SV_BeginDownload_f),
(name: 'nextdl'; func: SV_NextDownload_f),
(name: nil; func: nil) );
implementation
uses
Files,
Common,
CVar,
CPas,
Cmd,
net_chan,
sv_main,
sv_game,
q_shared;
(* ============================================================
USER STRINGCMD EXECUTION
sv_client and sv_player will be valid.
============================================================ *)
(* ==================
SV_BeginDemoServer
================== *)
procedure SV_BeginDemoserver;
var
name: array[0..MAX_OSPATH-1] of char;
begin
Com_sprintf(name, SizeOf(name), 'demos/%s', [sv.name]);
FS_FOpenFile(name, sv.demofile);
if (sv.demofile = 0) then
Com_Error(ERR_DROP, 'Couldn''t open %s'#10, [name]);
end;
(* ================
SV_New_f
Sends the first message from the server to a connected client.
This will be sent on the initial connection and upon each server load.
================ *)
procedure SV_New_f;
var
gamedir: PChar;
playernum: Integer;
ent: edict_p;
begin
Com_DPrintf('New() from %s'#10, [sv_client^.name]);
if (sv_client^.state <> cs_connected) then
begin
Com_Printf('New not valid -- already spawned'#10, []);
Exit;
end;
{ demo servers just dump the file message }
if (sv.state = ss_demo) then
begin
SV_BeginDemoserver;
Exit;
end;
{ serverdata needs to go over for all types of servers
to make sure the protocol is right, and to set the gamedir }
gamedir := Cvar_VariableString('gamedir');
{ send the serverdata }
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_serverdata));
MSG_WriteLong(sv_client^.netchan.message, PROTOCOL_VERSION);
MSG_WriteLong(sv_client^.netchan.message, svs.spawncount);
MSG_WriteByte(sv_client^.netchan.message, Integer(sv.attractloop));
MSG_WriteString(sv_client^.netchan.message, gamedir);
if (sv.state = ss_cinematic) OR (sv.state = ss_pic) then
playernum := -1
else
playernum := (Cardinal(sv_client) - Cardinal(svs.clients)) div sizeof(client_t);
MSG_WriteShort(sv_client^.netchan.message, playernum);
{ send full levelname }
MSG_WriteString(sv_client^.netchan.message, sv.configstrings[CS_NAME]);
{ game server }
if (sv.state = ss_game) then
begin
{ set up the entity for the client }
ent := EDICT_NUM(playernum + 1);
ent^.s.number := playernum + 1;
sv_client^.edict := ent;
FillChar(sv_client^.lastcmd, SizeOf(sv_client^.lastcmd), 0);
{ begin fetching configstrings }
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_stufftext));
MSG_WriteString(sv_client^.netchan.message, va('cmd configstrings %d 0'#10, [svs.spawncount]));
end;
end;
(* ==================
SV_Configstrings_f
================== *)
procedure SV_Configstrings_f;
var
start, iHalfMesLen: Integer;
begin
Com_DPrintf('Configstrings() from %s'#10, [sv_client^.name]);
if (sv_client^.state <> cs_connected) then
begin
Com_Printf('configstrings not valid -- already spawned'#10, []);
Exit;
end;
{ handle the case of a level changing while a client was connecting }
if (StrToInt(Cmd_Argv(1)) <> svs.spawncount) then
begin
Com_Printf('SV_Configstrings_f from different level'#10, []);
SV_New_f;
Exit;
end;
start := StrToInt(Cmd_Argv(2));
{ write a packet full of data }
iHalfMesLen := (MAX_MSGLEN div 2);
while (sv_client^.netchan.message.cursize < iHalfMesLen) AND (start < MAX_CONFIGSTRINGS) do
begin
if (sv.configstrings[start][0] <> #0) then
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_configstring));
MSG_WriteShort(sv_client^.netchan.message, start);
MSG_WriteString(sv_client^.netchan.message, sv.configstrings[start]);
end;
Inc(start);
end;
{ send next command }
if (start = MAX_CONFIGSTRINGS) then
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_stufftext));
MSG_WriteString(sv_client^.netchan.message, va('cmd baselines %d 0'#10, [svs.spawncount]));
end
else
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_stufftext));
MSG_WriteString(sv_client^.netchan.message, va('cmd configstrings %d %d'#10, [svs.spawncount, start]));
end;
end;
(* ==================
SV_Baselines_f
================== *)
procedure SV_Baselines_f;
var
start, iHalfMesLen: Integer;
nullstate: entity_state_t;
base: entity_state_p;
begin
Com_DPrintf('Baselines() from %s'#10, [sv_client^.name]);
if (sv_client^.state <> cs_connected) then
begin
Com_Printf('baselines not valid -- already spawned'#10, []);
Exit;
end;
{ handle the case of a level changing while a client was connecting }
if (StrToInt(Cmd_Argv(1)) <> svs.spawncount) then
begin
Com_Printf('SV_Baselines_f from different level'#10, []);
SV_New_f;
Exit;
end;
start := StrToInt(Cmd_Argv(2));
FillChar(nullstate, SizeOf(nullstate), 0);
{ write a packet full of data }
iHalfMesLen := (MAX_MSGLEN div 2);
while (sv_client^.netchan.message.cursize < iHalfMesLen) AND (start < MAX_EDICTS) do
begin
base := @sv.baselines[start];
if (base^.modelindex<>0) OR (base^.sound<>0) OR (base^.effects<>0) then
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_spawnbaseline));
MSG_WriteDeltaEntity(nullstate, base^, sv_client^.netchan.message, true, true);
end;
Inc(start);
end;
{ send next command }
if (start = MAX_EDICTS) then
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_stufftext));
MSG_WriteString(sv_client^.netchan.message, va('precache %d'#10, [svs.spawncount]));
end
else
begin
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_stufftext));
MSG_WriteString(sv_client^.netchan.message, va('cmd baselines %d %d'#10, [svs.spawncount, start]));
end;
end;
(* ==================
SV_Begin_f
================== *)
procedure SV_Begin_f;
begin
Com_DPrintf('Begin() from %s'#10, [sv_client^.name]);
{ handle the case of a level changing while a client was connecting }
if (StrToInt(Cmd_Argv(1)) <> svs.spawncount) then
begin
Com_Printf('SV_Begin_f from different level'#10, []);
SV_New_f;
Exit;
end;
sv_client^.state := cs_spawned;
{ call the game begin function }
ge^.ClientBegin(sv_player);
Cbuf_InsertFromDefer;
end;
(* ==================
SV_NextDownload_f
================== *)
procedure SV_NextDownload_f;
var
r, percent, size: Integer;
begin
if (sv_client^.download = nil) then
Exit;
r := (sv_client^.downloadsize - sv_client^.downloadcount);
if (r > 1024) then
r := 1024;
MSG_WriteByte(sv_client^.netchan.message, Integer(svc_download));
MSG_WriteShort(sv_client^.netchan.message, r);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -