📄 p_client.pas
字号:
{*
===========
ClientConnect
Called when a player begins connecting to the server.
The game can refuse entrance to a client by returning false.
If the client is allowed, the connection process will continue
and eventually get to ClientBegin()
Changing levels will NOT cause this to be called again, but
loadgames will.
============
*}
function ClientConnect (ent : edict_p; userinfo : PChar) : qboolean; //g_main
var
value : PChar;
i, numspec : Integer;
begin
// check to see if they are on the banned IP list
value := Info_ValueForKey (userinfo, 'ip');
{$IFNDEF CTF} //onlyGAME (noneCTF)
if (SV_FilterPacket(value)) then
begin
Info_SetValueForKey (userinfo, 'rejmsg', 'Banned.');
Result := false;
Exit;
end;
// check for a spectator
value := Info_ValueForKey (userinfo, 'spectator');
{ 2003-05-08-SP: Alternative more Literal Conversion in case chosen option is under doubt
if (deathmatch^.Value <> 0) and (value^ <> #0) and (strcmp(value, '0') <> 0) then }
if (deathmatch^.Value <> 0) and (value <> nil) and (strcmp(value, '0') <> 0) then
begin
{ 2003-05-08-SP: Alternative more Literal Conversion in case chosen option is under doubt
if ( (spectator_password^.string_^ <> #0) AND }
if ( (spectator_password^.string_ <> nil) AND
(strcmp(spectator_password^.string_, 'none') <> 0) AND
(strcmp(spectator_password^.string_, value) <> 0)) then
begin
Info_SetValueForKey(userinfo, 'rejmsg', 'Spectator password required or incorrect.');
Result := false;
Exit;
end;
// count spectators
numspec := 0;
for i := 0 to trunc(maxclients^.Value)-1 do
if (g_edicts^[i+1].inuse) AND (g_edicts^[i+1].client^.pers.spectator) then
Inc(numspec);
if (numspec >= maxspectators^.value) then
begin
Info_SetValueForKey(userinfo, 'rejmsg', 'Server spectator limit is full.');
Result := false;
Exit;
end;
end
else
begin
{$ENDIF}
// check for a password
value := Info_ValueForKey (userinfo, 'password');
{ 2003-05-08-SP: Alternative more Literal Conversion in case chosen option is under doubt
if ( (password.string_^ <> #0) AND }
if ( (password^.string_ <> nil) AND
(strcmp(password^.string_, 'none') <> 0) AND
(strcmp(password^.string_, value) <> 0)) then
begin
Info_SetValueForKey(userinfo, 'rejmsg', 'Password required or incorrect.');
Result := false;
Exit;
end;
{$IFNDEF CTF}
end;
{$ENDIF}
// they can connect
ent^.client := @gclient_a(game.clients)[((Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t)) - 1];
// if there is already a body waiting for us (a loadgame), just
// take it, otherwise spawn one from scratch
if (ent^.inuse = false) then
begin
// clear the respawning variables
{$IFDEF CTF} //onlyCTF
//ZOID -- force team join
ent^.client^.resp.ctf_team := -1;
ent.^client^.resp.id_state := false;
//ZOID
{$ENDIF}
InitClientResp (ent^.client);
if (not game.autosaved) OR (ent^.client^.pers.weapon = nil) then
InitClientPersistant (ent^.client);
end;
ClientUserinfoChanged (ent, userinfo);
if (game.maxclients > 1) then
gi.dprintf ('%s connected'#10, ent^.client^.pers.netname);
ent^.svflags := 0;
ent^.client^.pers.connected := true;
Result := true;
end;
{*
===========
ClientDisconnect
Called when a player drops from the server.
Will not be called between levels.
============
*}
procedure ClientDisconnect (ent : edict_p); //g_main
var
playernum : integer;
begin
if (ent^.client = Nil) then
Exit;
gi.bprintf (PRINT_HIGH, '%s disconnected'#10, ent^.client^.pers.netname);
{$IFDEF CTF} //onlyCTF
(*Y//ZOID
CTFDeadDropFlag(ent);
CTFDeadDropTech(ent);
//ZOID*)
{$ENDIF}
// send effect
gi.WriteByte (svc_muzzleflash);
gi.WriteShort ((Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t));
gi.WriteByte (MZ_LOGOUT);
gi.multicast (@ent^.s.origin, MULTICAST_PVS);
gi.unlinkentity (ent);
ent^.s.modelindex := 0;
ent^.solid := SOLID_NOT;
ent^.inuse := false;
ent^.classname := 'disconnected';
ent^.client^.pers.connected := false;
playernum := (Cardinal(ent) - Cardinal(g_edicts)) div SizeOf(edict_t) - 1;
gi.configstring (CS_PLAYERSKINS+playernum, '');
end;
//==============================================================
var
pm_passent : edict_p;
// pmove doesn't need to know about passent and contentmask
function PM_trace (var start, mins, maxs, end_: vec3_t) : trace_t; cdecl;
begin
if (pm_passent^.health > 0) then
Result := gi.trace (@start, @mins, @maxs, @end_, pm_passent, MASK_PLAYERSOLID)
else
Result := gi.trace (@start, @mins, @maxs, @end_, pm_passent, MASK_DEADSOLID);
end;
function CheckBlock (b : PByteArray; c : integer) : integer;
var
v, i : integer;
begin
v := 0;
for i := 0 to c-1 do
Inc(v, b^[i]);
Result := v;
end;
procedure PrintPmove (pm : pmove_p);
var
c1, c2 : cardinal;
begin
c1 := CheckBlock (@pm^.s, sizeof(pm^.s));
c2 := CheckBlock (@pm^.cmd, sizeof(pm^.cmd));
Com_Printf ('sv %3i:%i %i'#10, [pm^.cmd.impulse, c1, c2]);
end;
{*
==============
ClientThink
This will be called once for each client frame, which will
usually be a couple times for each server frame.
==============
*}
procedure ClientThink (ent : edict_p; ucmd : usercmd_p);
var
client : gclient_p;
other : edict_p;
i, j : integer;
pm : pmove_t;
begin
level.current_entity := ent;
client := ent^.client;
if (level.intermissiontime <> 0) then
begin
client^.ps.pmove.pm_type := PM_FREEZE;
// can exit intermission after five seconds
if (level.time > level.intermissiontime + 5.0) AND ((ucmd^.buttons AND BUTTON_ANY) <> 0) then
level.exitintermission := Integer(True);
Exit;
end;
pm_passent := ent;
if (ent^.client^.chase_target <> Nil) then
begin
client^.resp.cmd_angles[0] := SHORT2ANGLE(ucmd^.angles[0]);
client^.resp.cmd_angles[1] := SHORT2ANGLE(ucmd^.angles[1]);
client^.resp.cmd_angles[2] := SHORT2ANGLE(ucmd^.angles[2]);
{$IFDEF CTF}
//ZOID
Exit;
end;
{$ELSE}
end
else
begin
{$ENDIF}
// set up for pmove
FillChar(pm, sizeof(pm), 0);
if (ent^.movetype = MOVETYPE_NOCLIP) then
client^.ps.pmove.pm_type := PM_SPECTATOR
else if (ent^.s.modelindex <> 255) then
client^.ps.pmove.pm_type := PM_GIB
else if (ent^.deadflag <> 0) then
client^.ps.pmove.pm_type := PM_DEAD
else
client^.ps.pmove.pm_type := PM_NORMAL;
client^.ps.pmove.gravity := trunc(sv_gravity^.Value);
pm.s := client^.ps.pmove;
for i := 0 to 2 do
begin
pm.s.origin[i] := trunc(ent^.s.origin[i]*8);
pm.s.velocity[i] := trunc(ent^.velocity[i]*8);
end;
if (memcmp(@client^.old_pmove, @pm.s, sizeof(pm.s)) <> 0) then
begin
pm.snapinitial := true;
//idsoft gi.dprintf ("pmove changed!\n");
end;
pm.cmd := ucmd^;
pm.trace := PM_trace; // adds default parms
pm.pointcontents := gi.pointcontents;
// perform a pmove
gi.Pmove (@pm);
// save results of pmove
client^.ps.pmove := pm.s;
client^.old_pmove := pm.s;
for i := 0 to 2 do
begin
ent^.s.origin[i] := pm.s.origin[i]*0.125;
ent^.velocity[i] := pm.s.velocity[i]*0.125;
end;
VectorCopy (pm.mins, ent^.mins);
VectorCopy (pm.maxs, ent^.maxs);
client^.resp.cmd_angles[0] := SHORT2ANGLE(ucmd^.angles[0]);
client^.resp.cmd_angles[1] := SHORT2ANGLE(ucmd^.angles[1]);
client^.resp.cmd_angles[2] := SHORT2ANGLE(ucmd^.angles[2]);
if (ent^.groundentity <> Nil) AND (pm.groundentity = Nil) AND (pm.cmd.upmove >= 10) AND (pm.waterlevel = 0) then
begin
gi.sound (ent, CHAN_VOICE, gi.soundindex('*jump1.wav'), 1, ATTN_NORM, 0);
PlayerNoise (ent, ent^.s.origin, PNOISE_SELF);
end;
ent^.viewheight := trunc(pm.viewheight);
ent^.waterlevel := pm.waterlevel;
ent^.watertype := pm.watertype;
ent^.groundentity := pm.groundentity;
if (pm.groundentity <> Nil) then
ent^.groundentity_linkcount := edict_p(pm.groundentity)^.linkcount;
if (ent^.deadflag <> 0) then
begin
client^.ps.viewangles[ROLL] := 40;
client^.ps.viewangles[PITCH] := -15;
client^.ps.viewangles[YAW] := client^.killer_yaw;
end
else
begin
VectorCopy (pm.viewangles, client^.v_angle);
VectorCopy (pm.viewangles, client^.ps.viewangles);
end;
{$IFDEF CTF} //onlyCTF
(*Y//ZOID
if (client.ctf_grapple <> Nil) then
CTFGrapplePull (client.ctf_grapple);
//ZOID*)
{$ENDIF}
gi.linkentity (ent);
if (ent^.movetype <> MOVETYPE_NOCLIP) then
G_TouchTriggers (ent);
// touch other objects
for i := 0 to pm.numtouch-1 do
begin
other := pm.touchents[i];
j := 0;
while j < i do
begin
if (pm.touchents[j] = other) then
Break;
Inc(j);
end;
if (j <> i) then
Continue; // duplicated
if NOT Assigned(other^.touch) then
Continue;
other^.touch (other, ent, Nil, Nil);
end;
{$IFNDEF CTF}
end;
{$ENDIF}
client^.oldbuttons := client^.buttons;
client^.buttons := ucmd^.buttons;
client^.latched_buttons := client^.latched_buttons OR (client^.buttons AND (NOT client^.oldbuttons));
// save light level the player is standing on for
// monster sighting AI
ent^.light_level := ucmd^.lightlevel;
// fire weapon from final position if needed
{$IFDEF CTF}
if ((client^.latched_buttons AND BUTTON_ATTACK) <> 0)
//ZOID
AND (ent^.movetype <> MOVETYPE_NOCLIP
//ZOID
)
then
if (NOT client^.weapon_thunk) then
begin
client^.weapon_thunk := true;
Think_Weapon (ent);
end;
{$ELSE}
if (client^.latched_buttons AND BUTTON_ATTACK) <> 0 then
begin
if (client^.resp.spectator) then
begin
client^.latched_buttons := 0;
if (client^.chase_target <> Nil) then
begin
client^.chase_target := Nil;
client^.ps.pmove.pm_flags := client^.ps.pmove.pm_flags AND (NOT PMF_NO_PREDICTION);
end
else
GetChaseTarget(ent);
end
else if (NOT client^.weapon_thunk) then
begin
client^.weapon_thunk := true;
Think_Weapon (ent);
end;
end;
{$ENDIF}
{$IFDEF CTF}
(*Y//ZOID
//regen tech
CTFApplyRegeneration(ent);
//ZOID*)
//ZOID
for i:=1 to maxclients.value do
begin
other := g_edicts + i;
if (other.inuse) AND (other.client.chase_target = ent) then
UpdateChaseCam(other);
end;
if (client.menudirty) AND (client.menutime <= level.time) then
begin
PMenu_Do_Update(ent);
gi.unicast (ent, true);
client.menutime := level.time;
client.menudirty := false;
end;
//ZOID
{$ELSE}
if (client^.resp.spectator) then
begin
if (ucmd^.upmove >= 10) then
begin
if (client^.ps.pmove.pm_flags AND PMF_JUMP_HELD) = 0 then
begin
client^.ps.pmove.pm_flags := client^.ps.pmove.pm_flags OR PMF_JUMP_HELD;
if (client^.chase_target <> nil) then
ChaseNext(ent)
else
GetChaseTarget(ent);
end;
end
else
client^.ps.pmove.pm_flags := client^.ps.pmove.pm_flags AND (NOT PMF_JUMP_HELD);
end;
// update chase cam if being followed
for i := 1 to trunc(maxclients^.Value) do
begin
other := @g_edicts^[i];
if (other^.inuse) AND (other^.client^.chase_target = ent) then
UpdateChaseCam(other);
end;
{$ENDIF}
end;
{*
==============
ClientBeginServerFrame
This will be called once for each server frame, before running
any other entities in the world.
==============
*}
procedure ClientBeginServerFrame (ent : edict_p);
var
client : gclient_p;
buttonMask : integer;
begin
if (level.intermissiontime <> 0) then
Exit;
client := ent^.client;
{$IFDEF CTF}
// run weapon animations if it hasn't been done by a ucmd_t
if (NOT client.weapon_thunk)
//ZOID
AND (ent.movetype <> MOVETYPE_NOCLIP
//ZOID
)
then Think_Weapon (ent)
else client.weapon_thunk := false;
{$ELSE}
if (deathmatch^.value <> 0) AND
(client^.pers.spectator <> client^.resp.spectator) AND
((level.time - client^.respawn_time) >= 5) then
begin
spectator_respawn(ent);
Exit;
end;
// run weapon animations if it hasn't been done by a ucmd_t
if (NOT client^.weapon_thunk) AND (NOT client^.resp.spectator) then
Think_Weapon (ent)
else
client^.weapon_thunk := false;
{$ENDIF}
if (ent^.deadflag <> 0) then
begin
// wait for any button just going down
if (level.time > client^.respawn_time) then
begin
// in deathmatch, only wait for attack button
if (deathmatch^.value <> 0) then
buttonMask := BUTTON_ATTACK
else
buttonMask := -1;
{$IFDEF CTF}
if ((client.latched_buttons AND buttonMask) <> 0) OR
( (deathmatch.value <> 0) AND (({(int)}Trunc(dmflags.value) AND DF_FORCE_RESPAWN) <> 0) ) (*YOR
CTFMatchOn()*) then
{$ELSE}
if ((client^.latched_buttons AND buttonMask) <> 0) OR
( (deathmatch^.value <> 0) AND ((Trunc(dmflags^.value) AND DF_FORCE_RESPAWN) <> 0) ) then
{$ENDIF}
begin
respawn(ent);
client^.latched_buttons := 0;
end;
end;
Exit;
end;
// add player trail so monsters can follow
if (deathmatch^.value = 0) then
if (not visible (ent, PlayerTrail_LastSpot())) then
PlayerTrail_Add (ent^.s.old_origin);
client^.latched_buttons := 0;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -