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