1 /*
2 Relay -- a tool to record and play Quake2 demos
3 Copyright (C) 2000 Conor Davis
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 Conor Davis
20 cedavis@planetquake.com
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "rp_local.h"
27 #include "dm2.h"
28 #include "q2utils.h"
29
30 void ClientUserinfoChanged (edict_t *ent, char *userinfo);
31
32 /*
33 ===========
34 ClientBegin
35
36 called when a client has finished connecting, and is ready
37 to be placed into the game. This will happen every level load.
38 ============
39 */
ClientBegin(edict_t * ent)40 void ClientBegin (edict_t *ent)
41 {
42 int i, j;
43 player_state_t *ps;
44
45 ent->client = game.clients + (ent - g_edicts - 1);
46 G_InitEdict(ent);
47
48 if (game.player != -1)
49 {
50 ent->client->player = game.player;
51
52 // make sure the player being watched is in the game
53 if (dm2in.current_frame == 0)
54 {
55 while (infile && !ISBITSET(current_connected, game.player))
56 AdvanceFrame();
57 }
58
59 ClientEndServerFrame(ent);
60 return;
61 }
62
63 if (dm2in.svd.isdemo == RECORD_NETWORK || dm2in.svd.isdemo == RECORD_CLIENT)
64 {
65 ent->client->player = 0;
66 if (ent->client->relayflags & RC_STATUSBAR)
67 {
68 gi.WriteByte(SVC_CONFIGSTRING);
69 gi.WriteShort(CS_STATUSBAR);
70 gi.WriteString(dm2in.configstrings[CS_STATUSBAR]);
71 gi.unicast(ent, true);
72 }
73 }
74 else
75 {
76 ent->client->player = -1;
77 if (ent->client->relayflags & RC_STATUSBAR)
78 {
79 gi.WriteByte(SVC_CONFIGSTRING);
80 gi.WriteShort(CS_STATUSBAR);
81 gi.WriteString("");
82 gi.unicast(ent, true);
83 }
84 }
85
86 // find a good spot to put the client
87 for (i = 0; i <= dm2in.maxclients; i++)
88 {
89 if (i == dm2in.maxclients)
90 { // couldn't find a player, try a spawnpoint
91 VectorCopy(spawnpoints, ent->s.origin);
92 break;
93 }
94
95 if (!ISBITSET(current_connected, i))
96 continue;
97
98 ps = &dm2in.players[i].ps[dm2in.current_frame & UPDATE_MASK];
99
100 for (j = 0; j < 3; j++)
101 ent->s.origin[j] = ps->pmove.origin[j] * 0.125;
102 VectorAdd(ent->s.origin, ps->viewoffset, ent->s.origin);
103 VectorAdd(ent->s.origin, tv(0, 0, 32), ent->s.origin);
104 VectorCopy(ps->viewangles, ent->client->ps.viewangles);
105 for (j = 0; j < 3; j++)
106 {
107 ent->client->ps.pmove.delta_angles[j] = ANGLE2SHORT(ent->client->ps.viewangles[j] - ent->client->cmd_angles[j]);
108 ent->client->ps.pmove.origin[j] = ent->s.origin[j] * 8;
109 }
110 break;
111 }
112
113 // make sure all view stuff is valid
114 ClientEndServerFrame (ent);
115 }
116
117 /*
118 ===========
119 ClientUserInfoChanged
120
121 called whenever the player updates a userinfo variable.
122
123 The game can override any of the settings in place
124 (forcing skins or names, etc) before copying it off.
125 ============
126 */
ClientUserinfoChanged(edict_t * ent,char * userinfo)127 void ClientUserinfoChanged (edict_t *ent, char *userinfo)
128 {
129 char *s;
130
131 // check for malformed or illegal info strings
132 if (!Info_Validate(userinfo))
133 {
134 strcpy (userinfo, "\\name\\badinfo\\skin\\male/grunt");
135 }
136
137 // set name
138 s = Info_ValueForKey (userinfo, "name");
139 strncpy (ent->client->netname, s, sizeof(ent->client->netname)-1);
140
141 // fov
142 ent->client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
143 if (ent->client->ps.fov < 1)
144 ent->client->ps.fov = 90;
145 else if (ent->client->ps.fov > 160)
146 ent->client->ps.fov = 160;
147
148 // save off the userinfo in case we want to check something later
149 strncpy (ent->client->userinfo, userinfo, sizeof(ent->client->userinfo)-1);
150 }
151
ClientConnect(edict_t * ent,char * userinfo)152 qboolean ClientConnect (edict_t *ent, char *userinfo)
153 {
154 char *value;
155
156 // check to see if they are on the banned IP list
157 value = Info_ValueForKey (userinfo, "ip");
158 if (SV_FilterPacket(value))
159 {
160 Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
161 return false;
162 }
163
164 // check for a password
165 value = Info_ValueForKey (userinfo, "password");
166 if (*password->string && strcmp(password->string, "none") &&
167 strcmp(password->string, value)) {
168 Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect.");
169 return false;
170 }
171
172 // they can connect
173 ent->client = game.clients + (ent - g_edicts - 1);
174 memset(ent->client, 0, sizeof(*ent->client));
175
176 ent->client->relayflags = RC_LOCKPOS|RC_LOCKVIEW|RC_STATUSBAR|RC_LAYOUT;
177 ent->client->dist = 100;
178
179 ClientUserinfoChanged (ent, userinfo);
180
181 if (game.maxclients > 1)
182 gi.dprintf ("%s connected\n", ent->client->netname);
183
184 ent->svflags = 0; // make sure we start with known default
185 return true;
186 }
187
ClientDisconnect(edict_t * ent)188 void ClientDisconnect (edict_t *ent)
189 {
190 if (!ent->client)
191 return;
192
193 gi.dprintf ("%s disconnected\n", ent->client->netname);
194
195 gi.unlinkentity (ent);
196 ent->s.modelindex = 0;
197 ent->solid = SOLID_NOT;
198 ent->inuse = false;
199 ent->classname = "disconnected";
200 }
201
202
203 //==============================================================
204
205
206 edict_t *pm_passent;
207
208 // pmove doesn't need to know about passent and contentmask
PM_trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)209 trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
210 {
211 return gi.trace (start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID);
212 }
213
ClientThink(edict_t * ent,usercmd_t * ucmd)214 void ClientThink (edict_t *ent, usercmd_t *ucmd)
215 {
216 gclient_t *client;
217 int i;
218 pmove_t pm;
219
220 client = ent->client;
221
222 for (i = 0; i < 3; i++)
223 client->cmd_angles[i] = SHORT2ANGLE(ucmd->angles[i]);
224
225 if (client->player != -1)
226 {
227 if (client->relayflags & RC_LOCKPOS)
228 return;
229 if (client->relayflags & RC_LOCKVIEW)
230 {
231 client->dist -= ucmd->forwardmove * ucmd->msec * 0.0005;
232 if (client->dist < 32)
233 client->dist = 32;
234 return;
235 }
236 }
237
238 pm_passent = ent;
239
240 // set up for pmove
241 memset (&pm, 0, sizeof(pm));
242
243 client->ps.pmove.pm_type = PM_SPECTATOR;
244 pm.s = client->ps.pmove;
245
246 for (i=0 ; i<3 ; i++)
247 {
248 pm.s.origin[i] = ent->s.origin[i]*8;
249 pm.s.velocity[i] = ent->velocity[i]*8;
250 }
251
252 if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
253 {
254 pm.snapinitial = true;
255 // gi.dprintf ("pmove changed!\n");
256 }
257
258 pm.cmd = *ucmd;
259
260 pm.trace = PM_trace; // adds default parms
261 pm.pointcontents = gi.pointcontents;
262
263 // perform a pmove
264 gi.Pmove (&pm);
265
266 // save results of pmove
267 client->ps.pmove = pm.s;
268 client->old_pmove = pm.s;
269
270 for (i=0 ; i<3 ; i++)
271 {
272 ent->s.origin[i] = pm.s.origin[i]*0.125;
273 ent->velocity[i] = pm.s.velocity[i]*0.125;
274 }
275
276 client->oldbuttons = client->buttons;
277 client->buttons = ucmd->buttons;
278 client->latched_buttons |= client->buttons & ~client->oldbuttons;
279
280 // save light level the player is standing on for
281 // monster sighting AI
282 ent->light_level = ucmd->lightlevel;
283 }
284
ClientBeginServerFrame(edict_t * ent)285 void ClientBeginServerFrame (edict_t *ent)
286 {
287 ent->client->latched_buttons = 0;
288 }
289