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 "sv_local.h"
24
InitClient(client_t * client)25 void InitClient(client_t *client)
26 {
27 memset(client, 0, sizeof(client_t));
28 BlockInit(&client->unreliable, client->unreliable_buffer, sizeof(client->unreliable_buffer));
29 BlockInit(&client->reliable, client->reliable_buffer, sizeof(client->reliable_buffer));
30 BlockInit(&client->nextreliable, client->nextreliable_buffer, sizeof(client->nextreliable_buffer));
31 }
32
CL_ChangePlayer(client_t * client,int index)33 int CL_ChangePlayer(client_t *client, int index)
34 {
35 int oldplayer;
36
37 oldplayer = client->player;
38
39 if (index == -1)
40 {
41 client->player = -1;
42 WriteByte(&client->nextreliable, SVC_CONFIGSTRING);
43 DM2_WriteConfigstring(&client->nextreliable, CS_STATUSBAR, "");
44
45 VectorAdd(client->origin, client->ps.viewoffset, client->origin);
46 memset(client->ps.blend, 0, sizeof(client->ps.blend));
47 client->ps.viewangles[ROLL] = 0;
48 client->ps.pmove.pm_flags = 0;
49 client->ps.pmove.pm_time = 0;
50 client->ps.pmove.gravity = 0;
51 client->ps.gunindex = 0;
52 client->ps.gunframe = 0;
53 VectorClear(client->ps.gunangles);
54 VectorClear(client->ps.gunoffset);
55 VectorClear(client->ps.viewoffset);
56 VectorClear(client->ps.kick_angles);
57 client->ps.stats[STAT_LAYOUTS] = 0;
58 return 0;
59 }
60
61 if (dm2in.svd.isdemo == RECORD_RELAY)
62 {
63 if (index < 0 || index >= dm2in.maxclients || !ISBITSET(server.current_connected, index))
64 return -1;
65 client->player = index;
66 }
67 else
68 {
69 client->player = 0;
70 }
71
72 if (oldplayer == -1 && client->flags & RC_STATUSBAR)
73 {
74 WriteByte(&client->nextreliable, SVC_CONFIGSTRING);
75 DM2_WriteConfigstring(&client->nextreliable, CS_STATUSBAR, dm2in.configstrings[CS_STATUSBAR]);
76 }
77
78 if (!client->curmenu && client->flags & RC_LAYOUT)
79 {
80 strcpy(client->layout, dm2in.players[client->player].layout);
81 client->layout_modified = true;
82 }
83
84 return 0;
85 }
86
ClientDisconnect(client_t * client)87 void ClientDisconnect(client_t *client)
88 {
89 printf("%s disconnected\n", client->netname);
90 InitClient(client);
91 server.numclients--;
92 }
93
ClientTimeout(client_t * client)94 void ClientTimeout(client_t *client)
95 {
96 printf("%s timed out\n", client->netname);
97 InitClient(client);
98 server.numclients--;
99 }
100
ClientConnect(client_t * client,const char * userinfo)101 qboolean ClientConnect(client_t *client, const char *userinfo)
102 {
103 ClientUserinfoChanged(client, userinfo);
104 printf("%s connected\n", client->netname);
105 server.numclients++;
106 client->player = -1;
107
108 return true;
109 }
110
ClientBegin(client_t * client)111 void ClientBegin(client_t *client)
112 {
113 if (dm2in.svd.isdemo == RECORD_NETWORK || dm2in.svd.isdemo == RECORD_CLIENT)
114 client->player = 0;
115
116 client->flags = RC_LOCKPOS|RC_LOCKVIEW|RC_STATUSBAR|RC_LAYOUT|RC_TINT;
117 if (client->player == -1 || !(client->flags & RC_LAYOUT))
118 {
119 // erase the statusbar
120 WriteByte(&client->nextreliable, SVC_CONFIGSTRING);
121 DM2_WriteConfigstring(&client->nextreliable, CS_STATUSBAR, "");
122 }
123
124 ClientEndServerFrame(client);
125 }
126
ClientUserinfoChanged(client_t * client,const char * userinfo)127 void ClientUserinfoChanged(client_t *client, const char *userinfo)
128 {
129 if (!Info_Validate(userinfo))
130 userinfo = "\\name\\badinfo\\skin\\male/grunt";
131
132 strncpy(client->userinfo, userinfo, sizeof(client->userinfo)-1);
133
134 strncpy(client->netname, Info_ValueForKey(userinfo, "name"), sizeof(client->netname)-1);
135
136 client->ps.fov = atoi(Info_ValueForKey(userinfo, "fov"));
137 if (client->ps.fov < 1)
138 client->ps.fov = 90;
139 else if (client->ps.fov > 160)
140 client->ps.fov = 160;
141 }
142
ClientThink(client_t * client,const usercmd_t * ucmd)143 void ClientThink(client_t *client, const usercmd_t *ucmd)
144 {
145 int i;
146 pmove_t pm;
147
148 for (i = 0; i < 3; i++)
149 client->cmd_angles[i] = SHORT2ANGLE(ucmd->angles[i]);
150
151 if (client->player != -1)
152 {
153 if (client->flags & RC_LOCKPOS)
154 return;
155
156 if (client->flags & RC_LOCKVIEW)
157 {
158 client->dist -= ucmd->forwardmove * ucmd->msec * 0.0005;
159 if (client->dist < 32)
160 client->dist = 32;
161
162 return;
163 }
164 }
165
166 memset(&pm, 0, sizeof(pmove_t));
167 client->ps.pmove.pm_type = PM_SPECTATOR;
168 pm.s = client->ps.pmove;
169 for (i = 0; i < 3; i++)
170 {
171 pm.s.origin[i] = client->origin[i] * 8;
172 pm.s.velocity[i] = client->velocity[i] * 8;
173 }
174
175 pm.cmd = *ucmd;
176
177 Pmove(&pm);
178
179 client->ps.pmove = pm.s;
180 for (i = 0; i < 3; i++)
181 {
182 client->origin[i] = pm.s.origin[i] * 0.125;
183 client->velocity[i] = pm.s.velocity[i] * 0.125;
184 }
185
186 VectorCopy(pm.viewangles, client->ps.viewangles);
187
188 client->buttons = ucmd->buttons;
189 }
190
ClientBeginServerFrame(client_t * client)191 void ClientBeginServerFrame(client_t *client)
192 {
193 }
194
UpdateClientOrigin(client_t * client)195 void UpdateClientOrigin(client_t *client)
196 {
197 int i;
198 player_state_t *ps;
199 vec3_t start, dir;
200
201 if (client->player == -1)
202 return;
203
204 ps = &dm2in.players[client->player].ps[dm2in.current_frame & UPDATE_MASK];
205
206 if (client->flags & RC_LOCKPOS)
207 {
208 for (i = 0; i < 3; i++)
209 client->origin[i] = ps->pmove.origin[i] * 0.125;
210 }
211 else if (client->flags & RC_LOCKVIEW)
212 {
213 for (i = 0; i < 3; i++)
214 start[i] = ps->pmove.origin[i] * 0.125;
215
216 VectorAdd(start, ps->viewoffset, start);
217 if (client->flags & RC_CHASEVIEW)
218 VectorAdd(ps->viewangles, client->cmd_angles, dir);
219 else
220 VectorCopy(client->cmd_angles, dir);
221 AngleVectors(dir, dir, NULL, NULL);
222 VectorMA(start, -client->dist, dir, client->origin);
223 }
224 }
225
ClientEndServerFrame(client_t * client)226 void ClientEndServerFrame(client_t *client)
227 {
228 int i;
229 player_state_t *ps = NULL;
230 vec3_t vec;
231
232 if (client->player != -1)
233 {
234 UpdateClientOrigin(client);
235
236 ps = &dm2in.players[client->player].ps[dm2in.current_frame & UPDATE_MASK];
237
238 if (client->flags & RC_LOCKPOS)
239 {
240 VectorCopy(ps->pmove.velocity, client->ps.pmove.velocity);
241 VectorCopy(ps->viewoffset, client->ps.viewoffset);
242 client->ps.pmove.pm_flags = ps->pmove.pm_flags;
243 client->ps.pmove.pm_time = ps->pmove.pm_time;
244 client->ps.pmove.gravity = ps->pmove.gravity;
245 client->ps.rdflags = ps->rdflags;
246 }
247 else
248 {
249 VectorClear(client->ps.viewoffset);
250 VectorClear(client->ps.kick_angles);
251 VectorClear(client->ps.pmove.velocity);
252 client->ps.pmove.gravity = 0;
253 client->ps.pmove.pm_flags = 0;
254 client->ps.pmove.pm_time = 0;
255 client->ps.rdflags = 0;
256 }
257
258 if (client->flags & RC_LOCKVIEW)
259 {
260 if (client->flags & RC_LOCKPOS)
261 {
262 VectorCopy(ps->viewangles, client->ps.viewangles);
263 VectorCopy(ps->kick_angles, client->ps.kick_angles);
264 client->ps.fov = ps->fov;
265 }
266 else
267 {
268 for (i = 0; i < 3; i++)
269 vec[i] = ps->pmove.origin[i] * 0.125;
270
271 VectorSubtract(vec, client->origin, vec);
272 VectorNormalize(vec);
273
274 vectoangles(vec, client->ps.viewangles);
275 }
276 client->ps.pmove.pm_type = PM_FREEZE;
277 }
278 else if (client->flags & RC_LOCKPOS)
279 client->ps.pmove.pm_type = PM_SPECTATOR;
280
281 if (client->flags & RC_STATUSBAR)
282 {
283 client->ps.gunindex = ps->gunindex;
284 client->ps.gunframe = ps->gunframe;
285 VectorCopy(ps->gunoffset, client->ps.gunoffset);
286 VectorCopy(ps->gunangles, client->ps.gunangles);
287 for (i = 0; i < MAX_STATS; i++)
288 {
289 if (i != STAT_LAYOUTS)
290 client->ps.stats[i] = ps->stats[i];
291 }
292 }
293 else
294 {
295 client->ps.gunindex = 0;
296 client->ps.gunframe = 0;
297 VectorClear(client->ps.gunoffset);
298 VectorClear(client->ps.gunangles);
299 for (i = 0; i < MAX_STATS; i++)
300 {
301 if (i != STAT_LAYOUTS)
302 client->ps.stats[i] = 0;
303 }
304 }
305
306 if (client->flags & RC_LAYOUT)
307 {
308 client->ps.stats[STAT_LAYOUTS] &= ~1;
309 client->ps.stats[STAT_LAYOUTS] |= ps->stats[STAT_LAYOUTS] & 1;
310 }
311 }
312
313 if (client->flags & RC_TINT && client->player != -1)
314 {
315 client->ps.stats[STAT_FLASHES] = ps->stats[STAT_FLASHES];
316 memcpy(client->ps.blend, ps->blend, sizeof(client->ps.blend));
317 }
318 else
319 {
320 client->ps.stats[STAT_FLASHES] = 0;
321 memset(client->ps.blend, 0, sizeof(client->ps.blend));
322 }
323
324 if (client->ps.fov < 1)
325 client->ps.fov = 1;
326 else if (client->ps.fov > 160)
327 client->ps.fov = 90;
328
329 for (i = 0; i < 3; i++)
330 client->ps.pmove.origin[i] = client->origin[i] * 8;
331
332 if (client->ps.pmove.pm_type == PM_FREEZE)
333 {
334 for (i = 0; i < 3; i++)
335 client->ps.pmove.delta_angles[i] = ANGLE2SHORT(client->ps.viewangles[i] - client->cmd_angles[i]);
336 }
337
338 if (client->layout_modified && client->nextreliable.writeoffset+strlen(client->layout)+2 < client->nextreliable.size)
339 {
340 client->layout_modified = false;
341 WriteByte(&client->nextreliable, SVC_LAYOUT);
342 DM2_WriteLayout(&client->nextreliable, client->layout);
343 }
344 if (client->inventory_modified && client->nextreliable.writeoffset+sizeof(client->inventory)+1 < client->nextreliable.size)
345 {
346 client->inventory_modified = false;
347 WriteByte(&client->nextreliable, SVC_INVENTORY);
348 DM2_WriteInventory(&client->nextreliable, client->inventory);
349 }
350
351 if (client->curmenu)
352 client->ps.stats[STAT_LAYOUTS] = 1;
353 }
354