1 /**
2  * @file
3  * @brief Main part of the game logic.
4  *
5  * @section Connection
6  *
7  * In case the connection is established (@c G_ClientConnect), the client
8  * state is @c cs_connected. The client will send SV_STATE_NEW, @c SV_New_f sets the
9  * state to @c cs_spawning, and asks the client to precache the data after
10  * sending the configstrings. After the client is done with it, it will send
11  * "begin" to the server. @c SV_Begin_f will now set the client state to @c cs_began
12  * and calls @c G_ClientBegin. The server will ask the client to execute
13  * "spawnsoldiers" now. The client is answering with @c clc_teaminfo and the server
14  * will call @c G_ClientTeamInfo. The client state is now @c cs_spawned. Last but not
15  * least the server informs the client that the match can now get started by
16  * asking to execute "startmatch". The client answers with NET_STATE_STARTMATCH and
17  * @c G_ClientStartMatch is executed.
18  */
19 
20 /*
21  Copyright (C) 2002-2013 UFO: Alien Invasion.
22 
23  This program is free software; you can redistribute it and/or
24  modify it under the terms of the GNU General Public License
25  as published by the Free Software Foundation; either version 2
26  of the License, or (at your option) any later version.
27 
28  This program is distributed in the hope that it will be useful,
29  but WITHOUT ANY WARRANTY; without even the implied warranty of
30  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31 
32  See the GNU General Public License for more details.
33 
34  You should have received a copy of the GNU General Public License
35  along with this program; if not, write to the Free Software
36  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
37 
38  */
39 
40 #include "g_client.h"
41 #include "g_actor.h"
42 #include "g_combat.h"
43 #include "g_edicts.h"
44 #include "g_inventory.h"
45 #include "g_match.h"
46 #include "g_move.h"
47 #include "g_reaction.h"
48 #include "g_utils.h"
49 #include "g_vis.h"
50 
51 static chrScoreMission_t scoreMission[MAX_EDICTS];
52 static int scoreMissionNum = 0;
53 
54 /**
55  * @brief Iterate through the list of players
56  * @param lastPlayer The player found in the previous iteration; if nullptr, we start at the beginning
57  */
G_PlayerGetNextHuman(Player * lastPlayer)58 Player *G_PlayerGetNextHuman (Player *lastPlayer)
59 {
60 	Player *endOfPlayers = &game.players[game.sv_maxplayersperteam];
61 	Player *player;
62 
63 	if (!game.sv_maxplayersperteam)
64 		return nullptr;
65 
66 	if (!lastPlayer)
67 		return game.players;
68 	assert(lastPlayer >= game.players);
69 	assert(lastPlayer < endOfPlayers);
70 
71 	player = lastPlayer;
72 
73 	player++;
74 	if (player >= endOfPlayers)
75 		return nullptr;
76 	else
77 		return player;
78 }
79 
80 /**
81  * @brief Iterate through the list of players
82  * @param lastPlayer The player found in the previous iteration; if nullptr, we start at the beginning
83  */
G_PlayerGetNextAI(Player * lastPlayer)84 Player *G_PlayerGetNextAI (Player *lastPlayer)
85 {
86 	Player *endOfPlayers = &game.players[game.sv_maxplayersperteam * 2];
87 	Player *player;
88 
89 	if (!game.sv_maxplayersperteam)
90 		return nullptr;
91 
92 	if (!lastPlayer)
93 		return &game.players[game.sv_maxplayersperteam];
94 	assert(lastPlayer >= &game.players[game.sv_maxplayersperteam]);
95 	assert(lastPlayer < endOfPlayers);
96 
97 	player = lastPlayer;
98 
99 	player++;
100 	if (player >= endOfPlayers)
101 		return nullptr;
102 	else
103 		return player;
104 }
105 
106 /**
107  * @brief Iterate through the list of players
108  * @param lastPlayer The player found in the previous iteration; if nullptr, we start at the beginning
109  */
G_PlayerGetNextActiveHuman(Player * lastPlayer)110 Player *G_PlayerGetNextActiveHuman (Player *lastPlayer)
111 {
112 	Player *player = lastPlayer;
113 
114 	while ((player = G_PlayerGetNextHuman(player))) {
115 		if (player->isInUse())
116 			return player;
117 	}
118 
119 	return nullptr;
120 }
121 
122 /**
123  * @brief Iterate through the list of players
124  * @param lastPlayer The player found in the previous iteration; if nullptr, we start at the beginning
125  */
G_PlayerGetNextActiveAI(Player * lastPlayer)126 Player *G_PlayerGetNextActiveAI (Player *lastPlayer)
127 {
128 	Player *player = lastPlayer;
129 
130 	while ((player = G_PlayerGetNextAI(player))) {
131 		if (player->isInUse())
132 			return player;
133 	}
134 
135 	return nullptr;
136 }
137 
138 /**
139  * @brief Generates the player bit mask for a given team
140  * @param[in] team The team to create the player bit mask for
141  * @note E.g. multiplayer team play can have more than one human player on the
142  * same team.
143  */
G_TeamToPM(int team)144 playermask_t G_TeamToPM (int team)
145 {
146 	playermask_t playerMask = 0;
147 	Player *p = nullptr;
148 
149 	/* don't handle the ai players, here */
150 	while ((p = G_PlayerGetNextHuman(p))) {
151 		if (p->isInUse() && team == p->getTeam())
152 			playerMask |= G_PlayerToPM(*p);
153 	}
154 
155 	return playerMask;
156 }
157 
158 /**
159  * @brief Converts player mask to vis mask
160  * @param[in] playerMask The player bit mask (contains the player numbers) that
161  * is converted to a vis mask
162  * @return Returns a vis mask for all the teams of the connected players that
163  * are marked in the given @c playerMask.
164  */
G_PMToVis(playermask_t playerMask)165 teammask_t G_PMToVis (playermask_t playerMask)
166 {
167 	teammask_t teamMask = 0;
168 	Player *p = nullptr;
169 
170 	/* don't handle the ai players, here */
171 	while ((p = G_PlayerGetNextActiveHuman(p))) {
172 		if (playerMask & G_PlayerToPM(*p))
173 			teamMask |= G_TeamToVisMask(p->getTeam());
174 	}
175 
176 	return teamMask;
177 }
178 
179 /**
180  * @brief Converts vis mask to player mask
181  * @param[in] teamMask The visibility bit mask (contains the team numbers) that
182  * is converted to a player mask
183  * @return Returns a playermask for all the teams of the connected players that
184  * are marked in the given @c vis_mask.
185  */
G_VisToPM(teammask_t teamMask)186 playermask_t G_VisToPM (teammask_t teamMask)
187 {
188 	playermask_t playerMask = 0;
189 	Player *p = nullptr;
190 
191 	/* don't handle the ai players, here */
192 	while ((p = G_PlayerGetNextActiveHuman(p))) {
193 		if (teamMask & G_TeamToVisMask(p->getTeam()))
194 			playerMask |= G_PlayerToPM(*p);
195 	}
196 
197 	return playerMask;
198 }
199 
200 /**
201  * Send messages to human players
202  * @param player A player (AI players are ignored here)
203  * @param printLevel A numeric value to restrict and channel the printing (CONSOLE, HUD, CHAT...)
204  * @param fmt A format string as in printf
205  */
G_ClientPrintf(const Player & player,int printLevel,const char * fmt,...)206 void G_ClientPrintf (const Player &player, int printLevel, const char* fmt, ...)
207 {
208 	va_list ap;
209 
210 	/* there is no client for an AI controlled player on the server where we
211 	 * could send the message to */
212 	if (G_IsAIPlayer(&player))
213 		return;
214 
215 	va_start(ap, fmt);
216 	gi.PlayerPrintf(&player, printLevel, fmt, ap);
217 	va_end(ap);
218 }
219 
220 /**
221  * @brief Network function to update the time units (TUs) for each team-member.
222  * @param[in] team The index of the team to update the values for.
223  * @sa G_SendStats
224  */
G_GiveTimeUnits(int team)225 void G_GiveTimeUnits (int team)
226 {
227 	Edict* ent = nullptr;
228 
229 	while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team))) {
230 		G_ActorGiveTimeUnits(ent);
231 		G_SendStats(*ent);
232 	}
233 }
234 
235 /**
236  * @brief Send the appear or perish event to the affected clients
237  * @param[in] playerMask These are the affected players or clients
238  * In case of e.g. teamplay there can be more than one client affected - thus
239  * this is a player mask
240  * @param[in] appear Is this event about an appearing actor (or a perishing one)
241  * @param[in] check The edict we are talking about (that appears or perishes)
242  * @param[in] ent The edict that was responsible for letting the check edict appear
243  * or perish. Might be @c nullptr.
244  * @sa CL_ActorAppear
245  */
G_AppearPerishEvent(playermask_t playerMask,bool appear,Edict & check,const Edict * ent)246 void G_AppearPerishEvent (playermask_t playerMask, bool appear, Edict &check, const Edict* ent)
247 {
248 	teammask_t teamMaskDiff;
249 
250 	/* test for pointless player mask */
251 	if (!playerMask)
252 		return;
253 
254 	teamMaskDiff = G_PMToVis(playerMask);
255 	G_VisFlagsSwap(check, teamMaskDiff);
256 
257 	if (appear) {
258 		/* appear */
259 		switch (check.type) {
260 		case ET_ACTOR:
261 		case ET_ACTOR2x2:
262 			G_EventActorAppear(playerMask, check, ent);
263 			break;
264 
265 		case ET_CAMERA:
266 			G_EventCameraAppear(playerMask, check);
267 			break;
268 
269 		case ET_ITEM:
270 			G_EventEdictAppear(playerMask, check);
271 			G_SendInventory(playerMask, check);
272 			break;
273 
274 		case ET_PARTICLE:
275 			G_EventEdictAppear(playerMask, check);
276 			G_EventSendParticle(playerMask, check);
277 			break;
278 
279 		case ET_TRIGGER_RESCUE:
280 			G_EventAddBrushModel(playerMask, check);
281 			break;
282 
283 		default:
284 			if (G_IsVisibleOnBattlefield(&check))
285 				gi.Error("Missing edict type %i in G_AppearPerishEvent", check.type);
286 			break;
287 		}
288 	} else if (G_IsVisibleOnBattlefield(&check)) {
289 		G_EventEdictPerish(playerMask, check);
290 	}
291 }
292 
293 /**
294  * @brief This function sends all the actors to the client that are not visible
295  * initially - this is needed because an actor can e.g. produce sounds that are
296  * send over the net. And the client can only handle them if he knows the
297  * @c le_t (local entity) already
298  * @note Call this for the first @c G_CheckVis call for every new
299  * actor or player
300  * @sa G_CheckVis
301  * @sa CL_ActorAdd
302  */
G_SendInvisible(const Player & player)303 void G_SendInvisible (const Player &player)
304 {
305 	const int team = player.getTeam();
306 
307 	assert(team != TEAM_NO_ACTIVE);
308 	if (!level.num_alive[team])
309 		return;
310 
311 	Edict* ent = nullptr;
312 	/* check visibility */
313 	while ((ent = G_EdictsGetNextActor(ent))) {
314 		if (ent->team == team)
315 			continue;
316 		/* not visible for this team - so add the le only */
317 		if (!G_IsVisibleForTeam(ent, team)) {
318 			G_EventActorAdd(G_PlayerToPM(player), *ent);
319 		}
320 	}
321 }
322 
323 /**
324  * @brief Returns the current active team to the server
325  * @par If this has the value @c TEAM_NO_ACTIVE there is either no
326  * living actor for any player left, or the turn wasn't started yet.
327  */
G_GetActiveTeam(void)328 int G_GetActiveTeam (void)
329 {
330 	return level.activeTeam;
331 }
332 
333 /**
334  * @brief Checks whether the requested action is possible
335  * @param[in] player Which player (human player) is trying to do the action
336  * @param[in] ent Which of his units is trying to do the action.
337  */
G_ActionCheck(const Player & player,Edict * ent)338 static bool G_ActionCheck (const Player &player, Edict* ent)
339 {
340 	if (!ent || !ent->inuse) {
341 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not present!"));
342 		return false;
343 	}
344 
345 	if (ent->type != ET_ACTOR && ent->type != ET_ACTOR2x2) {
346 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not an actor!"));
347 		return false;
348 	}
349 
350 	if (G_IsStunned(ent)) {
351 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is stunned!"));
352 		return false;
353 	}
354 
355 	if (G_IsDead(ent)) {
356 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is dead!"));
357 		return false;
358 	}
359 
360 	if (ent->team != player.getTeam()) {
361 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not on same team!"));
362 		return false;
363 	}
364 
365 	if (ent->pnum != player.getNum()) {
366 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no control over allied actors!"));
367 		return false;
368 	}
369 
370 	/* could be possible */
371 	return true;
372 }
373 
374 /**
375  * @brief Checks whether the requested action is possible for the current active team
376  * @param[in] player Which player (human player) is trying to do the action
377  * @param[in] ent Which of his units is trying to do the action.
378  * @param[in] TU The time units to check against the ones ent has.
379  * the action with
380  */
G_ActionCheckForCurrentTeam(const Player & player,Edict * ent,int TU)381 bool G_ActionCheckForCurrentTeam (const Player &player, Edict* ent, int TU)
382 {
383 	/* a generic tester if an action could be possible */
384 	if (level.activeTeam != player.getTeam()) {
385 		G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - it is not your turn!"));
386 		return false;
387 	}
388 
389 	if (TU > G_ActorUsableTUs(ent)) {
390 		return false;
391 	}
392 
393 	return G_ActionCheck(player, ent);
394 }
395 
396 /**
397  * @brief Checks whether the requested action is possible
398  * @param[in] player Which player (human player) is trying to do the action
399  * @param[in] ent Which of his units is trying to do the action.
400  * @param[in] TU The time units to check against the ones ent has.
401  * the action with
402  * @sa G_ActionCheck
403  */
G_ActionCheckForReaction(const Player & player,Edict * ent,int TU)404 bool G_ActionCheckForReaction (const Player &player, Edict* ent, int TU)
405 {
406 	if (TU > ent->TU) {
407 		return false;
408 	}
409 
410 	return G_ActionCheck(player, ent);
411 }
412 
413 /**
414  * @brief Sends the actual actor turn event over the netchannel
415  */
G_ClientTurn(Player & player,Edict * ent,dvec_t dvec)416 static void G_ClientTurn (Player &player, Edict* ent, dvec_t dvec)
417 {
418 	const int dir = getDVdir(dvec);
419 
420 	/* check if action is possible */
421 	if (!G_ActionCheckForCurrentTeam(player, ent, TU_TURN))
422 		return;
423 
424 	/* check if we're already facing that direction */
425 	if (ent->dir == dir)
426 		return;
427 
428 	/* do the turn */
429 	G_ActorDoTurn(ent, dir);
430 	G_ActorUseTU(ent, TU_TURN);
431 
432 	/* send the turn */
433 	G_EventActorTurn(*ent);
434 
435 	/* send the new TUs */
436 	G_SendStats(*ent);
437 
438 	/* end the event */
439 	G_EventEnd();
440 }
441 
442 /**
443  * @brief After an actor changed his state, he might get visible for other
444  * players. Check the vis here and send the state change to the clients that
445  * are seeing him already.
446  * @param ent The actor edict
447  */
G_ClientStateChangeUpdate(Edict & ent)448 static void G_ClientStateChangeUpdate (Edict &ent)
449 {
450 	/* Send the state change. */
451 	G_EventSendState(G_VisToPM(ent.visflags), ent);
452 
453 	/* Check if the player appears/perishes, seen from other teams. */
454 	G_CheckVis(&ent);
455 
456 	/* Calc new vis for this player. */
457 	G_CheckVisTeamAll(ent.team, 0, &ent);
458 
459 	/* Send the new TUs. */
460 	G_SendStats(ent);
461 
462 	/* End the event. */
463 	G_EventEnd();
464 }
465 
466 /**
467  * @brief Changes the state of a player/soldier.
468  * @param[in,out] player The player who controlls the actor
469  * @param[in] ent the edict to perform the state change for
470  * @param[in] reqState The bit-map of the requested state change
471  * @param[in] checkaction only activate the events - network stuff is handled in the calling function
472  * don't even use the G_ActionCheckForCurrentTeam function
473  * @note Use checkaction true only for e.g. spawning values
474  */
G_ClientStateChange(const Player & player,Edict * ent,int reqState,bool checkaction)475 void G_ClientStateChange (const Player &player, Edict* ent, int reqState, bool checkaction)
476 {
477 	/* Check if any action is possible. */
478 	if (checkaction && !G_ActionCheckForCurrentTeam(player, ent, 0))
479 		return;
480 
481 	if (!reqState)
482 		return;
483 
484 	switch (reqState) {
485 	case STATE_CROUCHED: /* Toggle between crouch/stand. */
486 		/* Check if player has enough TUs (TU_CROUCH TUs for crouch/uncrouch). */
487 		if (!checkaction || G_ActionCheckForCurrentTeam(player, ent, TU_CROUCH)) {
488 			if (G_IsCrouched(ent)) {
489 				if (!gi.CanActorStandHere(ent->fieldSize, ent->pos))
490 					break;
491 			}
492 			G_ToggleCrouched(ent);
493 			G_ActorUseTU(ent, TU_CROUCH);
494 			G_ActorSetMaxs(ent);
495 		}
496 		break;
497 	case ~STATE_REACTION: /* Request to turn off reaction fire. */
498 		if (G_IsReaction(ent)) {
499 			if (G_IsShaken(ent) && G_ReactionFireSettingsReserveTUs(ent)) {
500 				G_ClientPrintf(player, PRINT_HUD, _("Currently shaken, won't let their guard down."));
501 			} else {
502 				/* Turn off reaction fire. */
503 				G_RemoveReaction(ent);
504 				G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
505 				if (!G_IsAI(ent))
506 					G_EventReactionFireChange(*ent);
507 			}
508 		}
509 		break;
510 	/* Request to turn on multi- or single-reaction fire mode. */
511 	case STATE_REACTION:
512 		/* Disable reaction fire. */
513 		G_RemoveReaction(ent);
514 
515 		if (G_ReactionFireSettingsReserveTUs(ent)) {
516 			/* Enable requested reaction fire. */
517 			G_SetState(ent, reqState);
518 		}
519 		if (!G_IsAI(ent))
520 			G_EventReactionFireChange(*ent);
521 		break;
522 	default:
523 		gi.DPrintf("G_ClientStateChange: unknown request %i, ignoring\n", reqState);
524 		return;
525 	}
526 
527 	/* Only activate the events - network stuff is handled in the calling function */
528 	if (!checkaction)
529 		return;
530 
531 	G_ClientStateChangeUpdate(*ent);
532 }
533 
534 /**
535  * @brief Returns true if actor can reload weapon
536  * @sa AI_ActorThink
537  */
G_ClientCanReload(Edict * ent,containerIndex_t containerID)538 bool G_ClientCanReload (Edict* ent, containerIndex_t containerID)
539 {
540 	const objDef_t* weapon;
541 
542 	if (ent->getContainer(containerID)) {
543 		weapon = ent->getContainer(containerID)->def();
544 	} else if (containerID == CID_LEFT && ent->getRightHandItem()->isHeldTwoHanded()) {
545 		/* Check for two-handed weapon */
546 		weapon = ent->getRightHandItem()->def();
547 	} else
548 		return false;
549 
550 	assert(weapon);
551 
552 	/* also try the temp containers */
553 	const Container* cont = nullptr;
554 	while ((cont = ent->chr.inv.getNextCont(cont, true))) {
555 		Item* item = nullptr;
556 		while ((item = cont->getNextItem(item))) {
557 			if (item->def()->isLoadableInWeapon(weapon))
558 				return true;
559 		}
560 	}
561 	return false;
562 }
563 
564 /**
565  * @brief Retrieve or collect weapon from any linked container for the actor's right hand
566  * @note This function will also collect items from floor containers when the actor
567  * is standing on a given point.
568  * @sa AI_ActorThink
569  */
G_ClientGetWeaponFromInventory(Edict * ent)570 void G_ClientGetWeaponFromInventory (Edict* ent)
571 {
572 	/* e.g. bloodspiders are not allowed to carry or collect weapons */
573 	if (!ent->chr.teamDef->weapons)
574 		return;
575 
576 	/* search for weapons and select the one that is available easily */
577 	const invDef_t* bestContainer = nullptr;
578 	Item* theWeapon = nullptr;
579 	int tu = 100;
580 	const Container* cont = nullptr;
581 	while ((cont = ent->chr.inv.getNextCont(cont, true))) {
582 		if (cont->def()->out >= tu)
583 			continue;
584 		Item* item = nullptr;
585 		while ((item = cont->getNextItem(item))) {
586 			/* We are looking for the *fastest* way to get a weapon,
587 			 * no matter what kind of weapon it is. */
588 			assert(item->def());
589 			if (item->isWeapon() && !item->mustReload()) {
590 				theWeapon = item;
591 				bestContainer = cont->def();
592 				tu = bestContainer->out;
593 				break;
594 			}
595 		}
596 	}
597 
598 	/* send request */
599 	const invDef_t* invDef = INVDEF(CID_RIGHT);
600 	if (bestContainer)
601 		G_ActorInvMove(ent, bestContainer, theWeapon, invDef, 0, 0, true);
602 }
603 
604 /**
605  * @brief This function 'uses' the edict. E.g. it opens the door when the player wants it to open
606  * @sa PA_USE_DOOR
607  * @param[in] player The player is trying to activate the door
608  * @param[in,out] actor The actor the player is using to activate the entity
609  * @param[in,out] edict The entity that is to be used
610  * @todo Do we have to change the trigger position here, too? I don't think this is really needed.
611  * @sa CL_ActorUse
612  * @sa G_UseEdict
613  */
G_ClientUseEdict(const Player & player,Edict * actor,Edict * edict)614 bool G_ClientUseEdict (const Player &player, Edict* actor, Edict* edict)
615 {
616 	/* check whether the actor has sufficient TUs to 'use' this edicts */
617 	if (!G_ActionCheckForCurrentTeam(player, actor, edict->TU))
618 		return false;
619 
620 	if (!G_UseEdict(edict, actor))
621 		return false;
622 
623 	/* using a group of edicts only costs TUs once (for the master) */
624 	G_ActorUseTU(actor, edict->TU);
625 	/* send the new TUs */
626 	G_SendStats(*actor);
627 
628 	G_EventEnd();
629 
630 	return true;
631 }
632 
633 /**
634  * @brief The client sent us a message that he did something. We now execute the related function(s) and notify him if necessary.
635  * @param[in] player The player to execute the action for (the actor belongs to this player)
636  * @note a client action will also send the server side edict number to determine the actor
637  */
G_ClientAction(Player & player)638 int G_ClientAction (Player &player)
639 {
640 	player_action_t action;
641 	int num;
642 	pos3_t pos;
643 	int i;
644 	fireDefIndex_t firemode;
645 	int from, fx, fy, to, tx, ty;
646 	actorHands_t hand;
647 	int fmIdx, objIdx;
648 	int resCrouch, resShot;
649 	Edict* ent;
650 	const char* format;
651 
652 	/* read the header */
653 	action = (player_action_t)gi.ReadByte();
654 	num = gi.ReadShort();
655 
656 	ent = G_EdictsGetByNum(num);
657 	if (ent == nullptr)
658 		return action;
659 
660 	format = pa_format[action];
661 
662 	switch (action) {
663 	case PA_NULL:
664 		/* do nothing on a null action */
665 		break;
666 
667 	case PA_TURN:
668 		gi.ReadFormat(format, &i);
669 		G_ClientTurn(player, ent, (dvec_t) i);
670 		break;
671 
672 	case PA_MOVE:
673 		gi.ReadFormat(format, &pos);
674 		G_ClientMove(player, player.getTeam(), ent, pos);
675 		break;
676 
677 	case PA_STATE:
678 		gi.ReadFormat(format, &i);
679 		G_ClientStateChange(player, ent, i, true);
680 		break;
681 
682 	case PA_SHOOT:
683 		gi.ReadFormat(format, &pos, &i, &firemode, &from);
684 		G_ClientShoot(player, ent, pos, i, firemode, nullptr, true, from);
685 		break;
686 
687 	case PA_INVMOVE:
688 		gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty);
689 
690 		if (!isValidContId(from) || !isValidContId(to)) {
691 			gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to);
692 		} else {
693 			const invDef_t* fromPtr = INVDEF(from);
694 			const invDef_t* toPtr = INVDEF(to);
695 			Item* fromItem = ent->chr.inv.getItemAtPos(fromPtr, fx, fy);
696 			if (fromItem)
697 				G_ActorInvMove(ent, fromPtr, fromItem, toPtr, tx, ty, true);
698 		}
699 		break;
700 
701 	case PA_USE:
702 		if (ent->clientAction) {
703 			Edict* actionEnt;
704 
705 			/* read the door the client wants to open */
706 			gi.ReadFormat(format, &i);
707 
708 			/* get the door edict */
709 			actionEnt = G_EdictsGetByNum(i);
710 
711 			/* maybe the door is no longer 'alive' because it was destroyed */
712 			if (actionEnt && ent->clientAction == actionEnt) {
713 				if (G_IsDoor(actionEnt)) {
714 					G_ActorUseDoor(ent, actionEnt);
715 				}
716 			}
717 		}
718 		break;
719 
720 	case PA_REACT_SELECT:
721 		gi.ReadFormat(format, &hand, &fmIdx, &objIdx);
722 		G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
723 		break;
724 
725 	case PA_RESERVE_STATE:
726 		gi.ReadFormat(format, &resShot, &resCrouch);
727 
728 		G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch);
729 		break;
730 
731 	default:
732 		gi.Error("G_ClientAction: Unknown action!\n");
733 	}
734 	return action;
735 }
736 
737 /**
738  * @brief Sets the teamnum var for this match
739  * @param[in] player Pointer to connected player
740  * @todo Check whether there are enough free spawnpoints in all cases
741  */
G_GetTeam(Player & player)742 static void G_GetTeam (Player &player)
743 {
744 	/* player has already a team */
745 	if (player.getTeam() > 0) {
746 		Com_DPrintf(DEBUG_GAME, "Player %s is already on team %i\n", player.pers.netname, player.getTeam());
747 		return;
748 	}
749 
750 	/* number of currently connected players (no ai players) */
751 	int playersInGame = 0;
752 	Player *p = nullptr;
753 	while ((p = G_PlayerGetNextActiveHuman(p)))
754 		playersInGame++;
755 
756 	/* randomly assign a teamnumber in deathmatch games */
757 	if (playersInGame <= 1 && G_IsMultiPlayer() && !sv_teamplay->integer) {
758 		int spawnCheck[MAX_TEAMS];
759 		int spawnSpots = 0;
760 		int randomSpot;
761 		int i;
762 		/* skip civilian teams */
763 		for (i = TEAM_PHALANX; i < MAX_TEAMS; i++) {
764 			spawnCheck[i] = 0;
765 			/* check whether there are spawnpoints for this team */
766 			if (level.num_spawnpoints[i])
767 				spawnCheck[spawnSpots++] = i;
768 		}
769 		/* we need at least 2 different team spawnpoints for multiplayer in a death match game */
770 		if (spawnSpots < 2)
771 			gi.Error("G_GetTeam: Not enough spawn spots in map!");
772 
773 		/* assign random valid team number */
774 		i = spawnSpots;
775 		randomSpot = rand() % spawnSpots;
776 		for (;;) {
777 			const int team = spawnCheck[randomSpot];
778 			if (i == 0)
779 				gi.Error("G_GetTeam: Could not assign a team!");
780 			if (G_SetTeamForPlayer(player, team)) {
781 				gi.DPrintf("%s has been randomly assigned to team %i\n",
782 						player.pers.netname, G_ClientGetTeamNum(player));
783 				break;
784 			}
785 			i--;
786 			randomSpot = (randomSpot + 1) % spawnSpots;
787 		}
788 		return;
789 	}
790 
791 	/* find a team */
792 	if (G_IsSinglePlayer()) {
793 		G_SetTeamForPlayer(player, TEAM_PHALANX);
794 	} else if (sv_teamplay->integer) {
795 		/* set the team specified in the userinfo */
796 		const int i = G_ClientGetTeamNumPref(player);
797 		gi.DPrintf("Get a team for teamplay for %s\n", player.pers.netname);
798 		/* civilians are at team zero */
799 		if (i > TEAM_CIVILIAN && sv_maxteams->integer >= i) {
800 			G_SetTeamForPlayer(player, i);
801 			gi.BroadcastPrintf(PRINT_CONSOLE, "serverconsole: %s has chosen team %i\n", player.pers.netname, i);
802 		} else {
803 			gi.DPrintf("Team %i is not valid - choose a team between 1 and %i\n", i, sv_maxteams->integer);
804 			G_SetTeamForPlayer(player, TEAM_DEFAULT);
805 		}
806 	} else {
807 		int i;
808 		/* search team */
809 		gi.DPrintf("Getting a multiplayer team for %s\n", player.pers.netname);
810 		for (i = TEAM_CIVILIAN + 1; i < MAX_TEAMS; i++) {
811 			if (level.num_spawnpoints[i]) {
812 				bool teamAvailable = true;
813 
814 				p = nullptr;
815 				/* check if team is in use (only human controlled players) */
816 				while ((p = G_PlayerGetNextActiveAI(p))) {
817 					if (p->getTeam() == i) {
818 						Com_DPrintf(DEBUG_GAME, "Team %i is already in use\n", i);
819 						/* team already in use */
820 						teamAvailable = false;
821 						break;
822 					}
823 				}
824 				if (teamAvailable)
825 					break;
826 			}
827 		}
828 
829 		/* set the team */
830 		if (i < MAX_TEAMS) {
831 			/* remove ai player */
832 			p = nullptr;
833 			while ((p = G_PlayerGetNextActiveHuman(p))) {
834 				if (p->getTeam() == i) {
835 					gi.BroadcastPrintf(PRINT_CONSOLE, "Removing ai player...");
836 					p->setInUse(false);
837 					break;
838 				}
839 			}
840 			Com_DPrintf(DEBUG_GAME, "Assigning %s to team %i\n", player.pers.netname, i);
841 			G_SetTeamForPlayer(player, i);
842 		} else {
843 			gi.DPrintf("No free team - disconnecting '%s'\n", player.pers.netname);
844 			G_ClientDisconnect(player);
845 		}
846 	}
847 }
848 
849 /**
850  * @brief Set the used team for the given player
851  * @param[out] player The player the team should be set for
852  * @param[in] team The team to set for the given player
853  * @return <code>true</code> if the team was set successfully, <code>false</code> otherwise.
854  */
G_SetTeamForPlayer(Player & player,const int team)855 bool G_SetTeamForPlayer (Player &player, const int team)
856 {
857 	assert(team >= TEAM_NO_ACTIVE && team < MAX_TEAMS);
858 
859 	if (G_IsAIPlayer(&player)) {
860 		if (team != TEAM_ALIEN && team != TEAM_CIVILIAN)
861 			return false;
862 	} else {
863 		if (!sv_teamplay->integer) {
864 			Player *p = nullptr;
865 			while ((p = G_PlayerGetNextHuman(p)) != nullptr) {
866 				if (p->getTeam() == team)
867 					return false;
868 			}
869 		}
870 	}
871 
872 	player.setTeam(team);
873 
874 	/* if we started in dev mode, we maybe don't have a
875 	 * starting position in this map */
876 	if (!g_nospawn->integer) {
877 		if (team >= 0 && team < MAX_TEAMS) {
878 			if (!level.num_spawnpoints[team])
879 				gi.Error("No spawnpoints for team %i", team);
880 		}
881 	}
882 
883 	if (!G_IsAIPlayer(&player))
884 		Info_SetValueForKeyAsInteger(player.pers.userinfo, sizeof(player.pers.userinfo), "cl_team", team);
885 
886 	return true;
887 }
888 
889 /**
890  * @brief Returns the assigned team number of the player
891  */
G_ClientGetTeamNum(const Player & player)892 int G_ClientGetTeamNum (const Player &player)
893 {
894 	return player.getTeam();
895 }
896 
897 /**
898  * @brief Returns the preferred team number for the player
899  */
G_ClientGetTeamNumPref(const Player & player)900 int G_ClientGetTeamNumPref (const Player &player)
901 {
902 	return Info_IntegerForKey(player.pers.userinfo, "cl_teamnum");
903 }
904 
905 /**
906  * @return @c true if the player is for starting the multiplayer match
907  */
G_ClientIsReady(const Player * player)908 bool G_ClientIsReady (const Player *player)
909 {
910 	assert(player);
911 	return player->isReady();
912 }
913 
914 /**
915  * @brief Chose a team that should start the match
916  * @param[in] player In singleplayer mode the team of this player will get the first turn
917  * @sa SVCmd_StartGame_f
918  */
G_GetStartingTeam(const Player & player)919 static void G_GetStartingTeam (const Player &player)
920 {
921 	int teamCount;
922 	int playerCount;
923 	int knownTeams[MAX_TEAMS];
924 	Player *p;
925 
926 	/* return with no action if activeTeam already assigned or if are in single-player mode */
927 	if (G_MatchIsRunning())
928 		return;
929 
930 	if (G_IsSinglePlayer()) {
931 		level.activeTeam = player.getTeam();
932 		level.teamOfs = MAX_TEAMS - level.activeTeam;
933 		return;
934 	}
935 
936 	/* count number of currently connected unique teams and players (human controlled players only) */
937 	p = nullptr;
938 	teamCount = 0;
939 	playerCount = 0;
940 	while ((p = G_PlayerGetNextActiveHuman(p))) {
941 		int j;
942 		playerCount++;
943 		for (j = 0; j < teamCount; j++) {
944 			if (p->getTeam() == knownTeams[j])
945 				break;
946 		}
947 		if (j == teamCount)
948 			knownTeams[teamCount++] = p->getTeam();
949 	}
950 
951 	if (teamCount) {
952 		const int teamIndex = (int) (frand() * (teamCount - 1) + 0.5);
953 		G_PrintStats("Starting new game: %s with %i teams", level.mapname, teamCount);
954 		level.activeTeam = knownTeams[teamIndex];
955 		level.teamOfs = MAX_TEAMS - level.activeTeam;
956 		p = nullptr;
957 		while ((p = G_PlayerGetNextActiveHuman(p)))
958 			if (p->getTeam() != level.activeTeam)
959 				p->roundDone = true;
960 	}
961 }
962 
963 /**
964  * @brief Find valid actor spawn fields for this player.
965  * @note Already used spawn-point are not found because ent->type is changed in G_ClientTeamInfo.
966  * @param[in] player The player to spawn the actors for.
967  * @param[in] spawnType The type of spawn-point so search for (ET_ACTORSPAWN or ET_ACTOR2x2SPAWN)
968  * @return A pointer to a found spawn point or nullptr if nothing was found or on error.
969  */
G_ClientGetFreeSpawnPoint(const Player & player,int spawnType)970 static Edict* G_ClientGetFreeSpawnPoint (const Player &player, int spawnType)
971 {
972 	Edict* ent = nullptr;
973 
974 	/* Abort for non-spawnpoints */
975 	assert(spawnType == ET_ACTORSPAWN || spawnType == ET_ACTOR2x2SPAWN);
976 
977 	if (level.noRandomSpawn) {
978 		while ((ent = G_EdictsGetNextInUse(ent)))
979 			if (ent->type == spawnType && player.getTeam() == ent->team) {
980 				if (G_EdictsGetLivingActorFromPos(ent->pos))
981 					continue;
982 				return ent;
983 			}
984 	} else {
985 		Edict* list[MAX_EDICTS];
986 		int count = 0;
987 		while ((ent = G_EdictsGetNextInUse(ent)))
988 			if (ent->type == spawnType && player.getTeam() == ent->team) {
989 				if (G_EdictsGetLivingActorFromPos(ent->pos))
990 					continue;
991 				list[count++] = ent;
992 			}
993 
994 		if (count)
995 			return list[rand() % count];
996 	}
997 
998 	return nullptr;
999 }
1000 
1001 /**
1002  * @brief Checks whether the spawn of an actor is allowed for the current running match.
1003  * @note Don't allow spawning of soldiers for multiplayer if:
1004  * + the sv_maxsoldiersperplayer limit is hit (e.g. the assembled team is bigger than the allowed number of soldiers)
1005  * + the team already hit the max allowed amount of soldiers
1006  * @param num The nth actor the player want to spawn in the game.
1007  * @param team The team the player is part of.
1008  * @return @c true if spawn is allowed, @c false otherwise.
1009  */
G_ActorSpawnIsAllowed(const int num,const int team)1010 static inline bool G_ActorSpawnIsAllowed (const int num, const int team)
1011 {
1012 	if (G_IsSinglePlayer())
1013 		return true;
1014 
1015 	return num < sv_maxsoldiersperplayer->integer && level.num_spawned[team] < sv_maxsoldiersperteam->integer;
1016 }
1017 
1018 /**
1019  * @brief Think function for actors that spawn dead
1020  * @param ent The actor
1021  */
G_ThinkActorDieAfterSpawn(Edict * ent)1022 static void G_ThinkActorDieAfterSpawn (Edict* ent)
1023 {
1024 	G_ActorDieOrStun(ent, nullptr);
1025 	ent->think = nullptr;
1026 }
1027 
1028 /**
1029  * @brief Think function for actors that spawn crouched
1030  * @param ent The actor
1031  */
G_ThinkActorGoCrouch(Edict * ent)1032 static void G_ThinkActorGoCrouch (Edict* ent)
1033 {
1034 	G_ClientStateChange(ent->getPlayer(), ent, STATE_CROUCHED, true);
1035 	ent->think = nullptr;
1036 }
1037 
1038 /**
1039  * @brief Searches a free spawning point for a given actor size and turns it into an actor
1040  * @param[in] player The player to get the free spawn points for
1041  * @param[in] actorSize The actor size to get a spawning point for
1042  * @return An actor edict or @c nullptr if no free spawning point was found
1043  */
G_ClientGetFreeSpawnPointForActorSize(const Player & player,const actorSizeEnum_t actorSize)1044 Edict* G_ClientGetFreeSpawnPointForActorSize (const Player &player, const actorSizeEnum_t actorSize)
1045 {
1046 	Edict* ent;
1047 
1048 	if (actorSize == ACTOR_SIZE_NORMAL) {
1049 		/* Find valid actor spawn fields for this player. */
1050 		ent = G_ClientGetFreeSpawnPoint(player, ET_ACTORSPAWN);
1051 		if (ent) {
1052 			Edict* copy = G_EdictDuplicate(ent);
1053 			if (copy != nullptr)
1054 				copy->type = ET_ACTOR;
1055 			ent = copy;
1056 		}
1057 	} else if (actorSize == ACTOR_SIZE_2x2) {
1058 		/* Find valid actor spawn fields for this player. */
1059 		ent = G_ClientGetFreeSpawnPoint(player, ET_ACTOR2x2SPAWN);
1060 		if (ent) {
1061 			Edict* copy = G_EdictDuplicate(ent);
1062 			if (copy != nullptr) {
1063 				copy->type = ET_ACTOR2x2;
1064 				copy->morale = 100;
1065 			}
1066 			ent = copy;
1067 		}
1068 	} else {
1069 		gi.Error("G_ClientGetFreeSpawnPointForActorSize: unknown fieldSize for actor edict (actorSize: %i)\n",
1070 				actorSize);
1071 	}
1072 
1073 	if (!ent)
1074 		return nullptr;
1075 
1076 	level.num_spawned[ent->team]++;
1077 	ent->pnum = player.getNum();
1078 	ent->chr.fieldSize = actorSize;
1079 	ent->fieldSize = ent->chr.fieldSize;
1080 	ent->flags |= FL_DESTROYABLE;
1081 	G_VisFlagsReset(*ent);
1082 	gi.LinkEdict(ent);
1083 
1084 	if (ent->spawnflags & STATE_CROUCHED) {
1085 		ent->think = G_ThinkActorGoCrouch;
1086 		ent->nextthink = 1;
1087 	}
1088 
1089 	if (ent->spawnflags & STATE_STUN) {
1090 		if (ent->spawnflags & STATE_DEAD)
1091 			ent->HP = 0;
1092 		ent->think = G_ThinkActorDieAfterSpawn;
1093 		ent->nextthink = 1;
1094 	}
1095 
1096 	G_ActorModifyCounters(nullptr, ent, 1, 0, 0);
1097 
1098 	G_ReactionFireTargetsCreate(ent);
1099 
1100 	return ent;
1101 }
1102 
1103 /**
1104  * @brief Read the inventory from the clients team data
1105  * @param ent The actor's entity that should receive the items.
1106  */
G_ClientReadInventory(Edict * ent)1107 static void G_ClientReadInventory (Edict* ent)
1108 {
1109 	/* inventory */
1110 	int nr = gi.ReadShort();
1111 
1112 	for (; nr-- > 0;) {
1113 		const invDef_t* container;
1114 		Item item;
1115 		int x, y;
1116 		G_ReadItem(&item, &container, &x, &y);
1117 		if (container->temp)
1118 			gi.Error("G_ClientReadInventory failed, tried to add '%s' to a temp container %i", item.def()->id, container->id);
1119 		/* ignore the overload for now */
1120 		if (!ent->chr.inv.canHoldItemWeight(CID_EQUIP, container->id, item, ent->chr.score.skills[ABILITY_POWER]))
1121 			Com_Printf("G_ClientReadInventory: Item %s exceeds ent %i weight capacity\n", item.def()->id, ent->number);
1122 		if (!level.noEquipment && game.i.addToInventory(&ent->chr.inv, &item, container, x, y, 1) == nullptr)
1123 			gi.Error("G_ClientReadInventory failed, could not add item '%s' to container %i (x:%i,y:%i)",
1124 					item.def()->id, container->id, x, y);
1125 	}
1126 }
1127 
1128 /**
1129  * @brief Reads the character data from the netchannel that is needed to spawn an actor.
1130  * @param ent The actor entity to save the read data in.
1131  */
G_ClientReadCharacter(Edict * ent)1132 static void G_ClientReadCharacter (Edict* ent)
1133 {
1134 	int k;
1135 	int teamDefIdx;
1136 
1137 	ent->chr.init();
1138 	/* model */
1139 	ent->chr.ucn = gi.ReadShort();
1140 	gi.ReadString(ent->chr.name, sizeof(ent->chr.name));
1141 	gi.ReadString(ent->chr.path, sizeof(ent->chr.path));
1142 	gi.ReadString(ent->chr.body, sizeof(ent->chr.body));
1143 	gi.ReadString(ent->chr.head, sizeof(ent->chr.head));
1144 	ent->chr.bodySkin = gi.ReadByte();
1145 	ent->chr.headSkin = gi.ReadByte();
1146 
1147 	ent->chr.HP = gi.ReadShort();
1148 	ent->chr.minHP = ent->chr.HP;
1149 	ent->chr.maxHP = gi.ReadShort();
1150 	teamDefIdx = gi.ReadByte();
1151 	if (teamDefIdx < 0 || teamDefIdx >= MAX_TEAMDEFS)
1152 		gi.Error("Invalid team definition index given: %i", teamDefIdx);
1153 	ent->chr.teamDef = &gi.csi->teamDef[teamDefIdx];
1154 
1155 	ent->chr.gender = gi.ReadByte();
1156 	ent->chr.STUN = gi.ReadByte();
1157 	ent->chr.morale = gi.ReadByte();
1158 
1159 	for (k = 0; k < ent->chr.teamDef->bodyTemplate->numBodyParts(); ++k)
1160 		ent->chr.wounds.treatmentLevel[k] = gi.ReadByte();
1161 
1162 	for (k = 0; k < SKILL_NUM_TYPES + 1; k++) /* new attributes */
1163 		ent->chr.score.experience[k] = gi.ReadLong();
1164 	for (k = 0; k < SKILL_NUM_TYPES; k++) /* new attributes */
1165 		ent->chr.score.skills[k] = gi.ReadByte();
1166 	for (k = 0; k < KILLED_NUM_TYPES; k++)
1167 		ent->chr.score.kills[k] = gi.ReadShort();
1168 	for (k = 0; k < KILLED_NUM_TYPES; k++)
1169 		ent->chr.score.stuns[k] = gi.ReadShort();
1170 	ent->chr.score.assignedMissions = gi.ReadShort();
1171 }
1172 
1173 /**
1174  * @brief Call this if you want to skip some actor netchannel data
1175  * @note The fieldsize is not skipped
1176  * @sa G_ClientTeamInfo
1177  */
G_ClientSkipActorInfo(void)1178 static void G_ClientSkipActorInfo (void)
1179 {
1180 	int i, n;
1181 	Edict ent;
1182 	invDef_t container;
1183 	Item item;
1184 	int x, y;
1185 	const invDef_t* c = &container;
1186 
1187 	G_ClientReadCharacter(&ent);
1188 
1189 	/* skip inventory */
1190 	n = gi.ReadShort();
1191 	for (i = 0; i < n; i++) {
1192 		G_ReadItem(&item, &c, &x, &y);
1193 	}
1194 }
1195 
1196 /**
1197  * @brief Used after spawning an actor to set some default values that are not read from the
1198  * network event.
1199  * @param ent The actor edict to set the values for.
1200  */
G_ClientAssignDefaultActorValues(Edict * ent)1201 static void G_ClientAssignDefaultActorValues (Edict* ent)
1202 {
1203 	/* Mission Scores */
1204 	scoreMission[scoreMissionNum].init();
1205 	ent->chr.scoreMission = &scoreMission[scoreMissionNum];
1206 	scoreMissionNum++;
1207 
1208 	/* set initial vital statistics */
1209 	ent->HP = ent->chr.HP;
1210 	ent->morale = ent->chr.morale;
1211 
1212 	/** @todo for now, heal fully upon entering mission */
1213 	ent->morale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]);
1214 
1215 	/* set models */
1216 	ent->body = gi.ModelIndex(CHRSH_CharGetBody(&ent->chr));
1217 	ent->head = gi.ModelIndex(CHRSH_CharGetHead(&ent->chr));
1218 
1219 	ent->chr.scoreMission->carriedWeight = ent->chr.inv.getWeight();
1220 }
1221 
1222 /**
1223  * @brief This is called after the actors are spawned and will set actor states without consuming TUs
1224  * @param player The player to perform the action for
1225  */
G_ClientInitActorStates(const Player & player)1226 void G_ClientInitActorStates (const Player &player)
1227 {
1228 	const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
1229 	int i;
1230 
1231 	for (i = 0; i < length; i++) {
1232 		const int ucn = gi.ReadShort();
1233 		int saveTU;
1234 		actorHands_t hand;
1235 		int fmIdx, objIdx;
1236 		Edict* ent = G_EdictsGetActorByUCN(ucn, player.getTeam());
1237 		if (!ent)
1238 			gi.Error("Could not find character on team %i with unique character number %i", player.getTeam(), ucn);
1239 
1240 		/* these state changes are not consuming any TUs */
1241 		saveTU = ent->TU;
1242 		G_ClientStateChange(player, ent, gi.ReadShort(), false);
1243 		hand = (actorHands_t)gi.ReadShort();
1244 		fmIdx = gi.ReadShort();
1245 		objIdx = gi.ReadShort();
1246 		G_ActorSetTU(ent, saveTU);
1247 		if (objIdx != NONE) {
1248 			G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
1249 		}
1250 		G_ClientStateChangeUpdate(*ent);
1251 	}
1252 }
1253 
1254 /**
1255  * @brief The client lets the server spawn the actors for a given player by sending their information (models, inventory, etc..) over the network.
1256  * @param[in] player The player to spawn the actors for.
1257  * @sa GAME_SendCurrentTeamSpawningInfo
1258  * @sa clc_teaminfo
1259  */
G_ClientTeamInfo(const Player & player)1260 void G_ClientTeamInfo (const Player &player)
1261 {
1262 	const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
1263 	int i;
1264 
1265 	for (i = 0; i < length; i++) {
1266 		const actorSizeEnum_t actorFieldSize = gi.ReadByte();
1267 		/* Search for a spawn point for each entry the client sent */
1268 		if (player.getTeam() == TEAM_NO_ACTIVE || !G_ActorSpawnIsAllowed(i, player.getTeam()))
1269 			G_ClientSkipActorInfo();
1270 		else {
1271 			Edict* ent = G_ClientGetFreeSpawnPointForActorSize(player, actorFieldSize);
1272 			if (ent) {
1273 				Com_DPrintf(DEBUG_GAME, "Player: %i - team %i - size: %i\n", player.getNum(), ent->team, ent->fieldSize);
1274 
1275 				G_ClientReadCharacter(ent);
1276 				G_ClientReadInventory(ent);
1277 				G_ClientAssignDefaultActorValues(ent);
1278 				G_ActorGiveTimeUnits(ent);
1279 				G_TouchTriggers(ent);
1280 				ent->contentFlags = G_ActorGetContentFlags(ent->origin);
1281 			} else {
1282 				gi.DPrintf("Not enough spawn points for team %i (actorsize: %i)\n", player.getTeam(), actorFieldSize);
1283 
1284 				G_ClientSkipActorInfo();
1285 			}
1286 		}
1287 	}
1288 
1289 	Com_Printf("Used inventory slots client %s spawn: %i\n", player.pers.netname, game.i.GetUsedSlots());
1290 }
1291 
1292 /**
1293  * @brief Send brush models for entities like func_breakable and func_door and triggers
1294  * with their bounding boxes to the client and let him know about them.
1295  * There are also entities that are announced here, but fully handled clientside - like
1296  * func_rotating.
1297  * @sa CL_AddBrushModel
1298  * @sa EV_ADD_BRUSH_MODEL
1299  * @param[in] player The player the edicts are send to
1300  */
G_ClientSendEdictsAndBrushModels(const Player & player)1301 static void G_ClientSendEdictsAndBrushModels (const Player &player)
1302 {
1303 	const int mask = G_PlayerToPM(player);
1304 	/* skip the world */
1305 	Edict* ent = G_EdictsGetFirst();
1306 
1307 	/* make SOLID_BSP edicts visible to the client */
1308 	while ((ent = G_EdictsGetNextInUse(ent))) {
1309 		/* brush models that have a type - not the world - keep in
1310 		 * mind that there are several world edicts in the list in case of
1311 		 * a map assembly */
1312 		if (ent->solid != SOLID_BSP)
1313 			continue;
1314 
1315 		/* skip the world(s) in case of map assembly */
1316 		if (ent->type > ET_NULL) {
1317 			G_EventAddBrushModel(mask, *ent);
1318 			G_VisFlagsAdd(*ent, ~ent->visflags);
1319 		}
1320 	}
1321 }
1322 
1323 /**
1324  * @brief This functions starts the client
1325  * @sa G_ClientStartMatch
1326  * @sa CL_StartGame
1327  */
G_ClientBegin(Player & player)1328 bool G_ClientBegin (Player &player)
1329 {
1330 	player.began = true;
1331 	level.numplayers++;
1332 
1333 	/* find a team */
1334 	G_GetTeam(player);
1335 	if (!player.began)
1336 		return false;
1337 
1338 	gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
1339 
1340 	/* spawn camera (starts client rendering) */
1341 	G_EventStart(player, sv_teamplay->integer);
1342 
1343 	/* send things like doors and breakables */
1344 	G_ClientSendEdictsAndBrushModels(player);
1345 
1346 	/* ensure that the start event is send */
1347 	G_EventEnd();
1348 
1349 	/* set the net name */
1350 	gi.ConfigString(CS_PLAYERNAMES + player.getNum(), "%s", player.pers.netname);
1351 
1352 	/* inform all clients */
1353 	gi.BroadcastPrintf(PRINT_CONSOLE, "%s has joined team %i\n", player.pers.netname, player.getTeam());
1354 
1355 	return true;
1356 }
1357 
1358 /**
1359  * @brief Sets the team, init the TU and sends the player stats.
1360  * @sa G_SendPlayerStats
1361  * @sa G_GetTeam
1362  * @sa G_GiveTimeUnits
1363  * @sa G_ClientBegin
1364  * @sa CL_Reset
1365  */
G_ClientStartMatch(Player & player)1366 void G_ClientStartMatch (Player &player)
1367 {
1368 	G_GetStartingTeam(player);
1369 
1370 	/* do all the init events here... */
1371 	/* reset the data */
1372 	G_EventReset(player, level.activeTeam);
1373 
1374 	/* show visible actors and add invisible actor */
1375 	G_VisFlagsClear(player.getTeam());
1376 	G_CheckVisPlayer(player, false);
1377 	G_SendInvisible(player);
1378 
1379 	/* submit stats */
1380 	G_SendPlayerStats(player);
1381 
1382 	/* ensure that the last event is send, too */
1383 	G_EventEnd();
1384 
1385 	if (G_IsMultiPlayer()) {
1386 		/* ensure that we restart the round time limit */
1387 		sv_roundtimelimit->modified = true;
1388 	}
1389 
1390 	/* inform all clients */
1391 	gi.BroadcastPrintf(PRINT_CONSOLE, "%s has taken control over team %i.\n", player.pers.netname, player.getTeam());
1392 }
1393 
1394 /**
1395  * @brief called whenever the player updates a userinfo variable.
1396  * @note The game can override any of the settings in place (forcing skins or names, etc) before copying it off.
1397  */
G_ClientUserinfoChanged(Player & player,const char * userinfo)1398 void G_ClientUserinfoChanged (Player &player, const char* userinfo)
1399 {
1400 	const bool alreadyReady = player.isReady();
1401 	const int oldTeamnum = Info_IntegerForKey(player.pers.userinfo, "cl_teamnum");
1402 
1403 	/* check for malformed or illegal info strings */
1404 	if (!Info_Validate(userinfo))
1405 		userinfo = "\\cl_name\\badinfo";
1406 
1407 	/* set name */
1408 	Q_strncpyz(player.pers.netname, Info_ValueForKey(userinfo, "cl_name"), sizeof(player.pers.netname));
1409 	Q_strncpyz(player.pers.userinfo, userinfo, sizeof(player.pers.userinfo));
1410 	player.autostand = Info_IntegerForKey(userinfo, "cl_autostand");
1411 	player.setReady(Info_IntegerForKey(userinfo, "cl_ready"));
1412 
1413 	/* send the updated config string */
1414 	gi.ConfigString(CS_PLAYERNAMES + player.getNum(), "%s", player.pers.netname);
1415 
1416 	/* try to update to the preferred team */
1417 	if (!G_MatchIsRunning() && oldTeamnum != Info_IntegerForKey(userinfo, "cl_teamnum")) {
1418 		/* if the player is marked as ready he can't change his team */
1419 		if (!alreadyReady || !player.isReady()) {
1420 			player.setTeam(TEAM_NO_ACTIVE);
1421 			G_GetTeam(player);
1422 		} else {
1423 			Com_DPrintf(DEBUG_GAME, "G_ClientUserinfoChanged: player %s is already marked as being ready\n",
1424 					player.pers.netname);
1425 		}
1426 	}
1427 }
1428 
1429 /**
1430  * @brief Checks whether the connection is valid or invalid and set some
1431  * user info keys.
1432  * @param[in,out] player The player that is trying to connect to the game
1433  * @param[in,out] userinfo The userinfo data that is checked and changed
1434  * @param[in] userinfoSize The size of the userinfo buffer
1435  * @sa G_ClientDisconnect
1436  * @sa CL_ConnectionlessPacket
1437  * @todo Check if the teamnum preference has already reached maxsoldiers
1438  * and reject connection if so
1439  * @return @c false if the connection is refused, @c true otherwise
1440  */
G_ClientConnect(Player * player,char * userinfo,size_t userinfoSize)1441 bool G_ClientConnect (Player *player, char* userinfo, size_t userinfoSize)
1442 {
1443 	const char* value;
1444 
1445 	value = Info_ValueForKey(userinfo, "ip");
1446 
1447 	Com_Printf("connection attempt from %s\n", value);
1448 
1449 	/* check to see if they are on the banned IP list */
1450 	if (SV_FilterPacket(value)) {
1451 		Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_BANNED);
1452 		return false;
1453 	}
1454 
1455 	if (!G_PlayerToPM(*player)) {
1456 		Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_SERVER_FULL);
1457 		return false;
1458 	}
1459 
1460 	value = Info_ValueForKey(userinfo, "password");
1461 	if (password->string[0] != '\0' && !Q_streq(password->string, "none") && !Q_streq(password->string, value)) {
1462 		Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_PASSWORD_REQUIRED_OR_INCORRECT);
1463 		return false;
1464 	}
1465 
1466 	/* fix for fast reconnects after a disconnect */
1467 	if (player->isInUse()) {
1468 		gi.BroadcastPrintf(PRINT_CONSOLE, "%s already in use.\n", player->pers.netname);
1469 		G_ClientDisconnect(*player);
1470 	}
1471 
1472 	/* reset persistent data */
1473 	OBJZERO(player->pers);
1474 	G_ClientUserinfoChanged(*player, userinfo);
1475 
1476 	gi.BroadcastPrintf(PRINT_CONSOLE, "%s is connecting...\n", player->pers.netname);
1477 	return true;
1478 }
1479 
1480 /**
1481  * @sa G_ClientConnect
1482  */
G_ClientDisconnect(Player & player)1483 void G_ClientDisconnect (Player &player)
1484 {
1485 #if 0
1486 	Edict* ent = nullptr;
1487 #endif
1488 
1489 	/* only if the player already sent his began */
1490 	if (player.began) {
1491 		level.numplayers--;
1492 		gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
1493 
1494 		if (level.activeTeam == player.getTeam())
1495 			G_ClientEndRound(player);
1496 
1497 		/* if no more players are connected - stop the server */
1498 		G_MatchEndCheck();
1499 	}
1500 
1501 #if 0
1502 	/* now let's remove all the edicts that belongs to this player */
1503 	while ((ent = G_EdictsGetNextLivingActor(ent))) {
1504 		if (ent->pnum == player->num)
1505 			G_ActorDie(ent, STATE_DEAD, nullptr);
1506 	}
1507 	G_MatchEndCheck();
1508 #endif
1509 
1510 	player.began = false;
1511 	player.roundDone = false;
1512 	player.setReady(false);
1513 
1514 	gi.BroadcastPrintf(PRINT_CONSOLE, "%s disconnected.\n", player.pers.netname);
1515 }
1516 
1517 /**
1518  * @brief Called after every player has joined
1519  */
G_ResetClientData(void)1520 void G_ResetClientData (void)
1521 {
1522 	scoreMissionNum = 0;
1523 	OBJZERO(scoreMission);
1524 }
1525