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