UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_game_multiplayer.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "../../cl_shared.h"
27 #include "../cl_game.h"
28 #include "cl_game_multiplayer.h"
29 #include "mp_callbacks.h"
30 #include "mp_serverlist.h"
31 #include "../../ui/ui_data.h"
32 
33 static const cgame_import_t* cgi;
34 
36 
37 static void GAME_MP_StartBattlescape (bool isTeamPlay)
38 {
39  cgi->UI_ExecuteConfunc("multiplayer_setTeamplay %i", isTeamPlay);
40  cgi->UI_InitStack("multiplayer_wait", nullptr);
42 }
43 
44 static void GAME_MP_NotifyEvent (event_t eventType)
45 {
46  if (eventType != EV_RESET)
47  return;
48 
49  cgi->HUD_InitUI("missionoptions");
50 }
51 
52 static void GAME_MP_EndRoundAnnounce (int playerNum, int team)
53 {
54  char buf[128];
55 
56  /* it was our own turn */
57  if (cgi->CL_GetPlayerNum() == playerNum) {
58  Com_sprintf(buf, sizeof(buf), _("You've ended your turn.\n"));
59  } else {
60  const char* playerName = cgi->CL_PlayerGetName(playerNum);
61  Com_sprintf(buf, sizeof(buf), _("%s ended his turn (team %i).\n"), playerName, team);
62  }
63  /* add translated message to chat buffer */
64  cgi->HUD_DisplayMessage(buf);
65 }
66 
71 static void GAME_MP_StartServer_f (void)
72 {
73  if (!cgi->Cvar_GetInteger("sv_dedicated") && cgi->GAME_IsTeamEmpty())
74  cgi->GAME_AutoTeam("multiplayer_initial", cgi->GAME_GetCharacterArraySize());
75 
76  if (cgi->Cvar_GetInteger("sv_teamplay")
78  cgi->UI_Popup(_("Settings doesn't make sense"), _("Set soldiers per player lower than soldiers per team"));
79  return;
80  }
81 
82  const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
83  if (!md || !md->multiplayer)
84  return;
85  assert(md->mapTheme);
86 
88  cgi->Cvar_Set("rm_drop", "%s", "");
89  cgi->Cvar_Set("rm_ufo", "%s", "");
90  cgi->Cvar_Set("sv_hurtaliens", "0");
91 
92  if (md->mapTheme[0] == '+') {
93  const linkedList_t* const ufos = md->ufos;
94  const linkedList_t* const crafts = md->aircraft;
95  if (ufos)
96  cgi->Cvar_Set("rm_ufo", "%s",
97  cgi->Com_GetRandomMapAssemblyNameForCraft((const char*)ufos->data));
98  if (crafts)
99  cgi->Cvar_Set("rm_drop", "%s",
100  cgi->Com_GetRandomMapAssemblyNameForCraft((const char*)crafts->data));
101  }
102 
103  if (md->teams)
104  cgi->Cvar_SetValue("sv_maxteams", md->teams);
105  else
106  cgi->Cvar_SetValue("sv_maxteams", 2);
107 
108  cgi->Cmd_ExecuteString("map %s %s %s", cgi->Cvar_GetInteger("mn_serverday") ? "day" : "night", md->mapTheme, md->params ? (const char*)cgi->LIST_GetRandom(md->params) : "");
109 
110  cgi->UI_InitStack("multiplayer_wait", "missionoptions");
111 }
112 
125 static void GAME_MP_Results (dbuffer* msg, int winner, int* numSpawned, int* numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
126 {
127  /* HACK: Change to the main menu now so cgame shutdown won't kill the results screen by doing it later */
128  cgi->UI_InitStack("main", "");
129 
130  linkedList_t* list = nullptr;
131  int enemiesKilled = 0, enemiesStunned = 0;
132  const int team = cgi->GAME_GetCurrentTeam();
133 
134  for (int i = 0; i < MAX_TEAMS; i++) {
135  if (i == team)
136  continue;
137  enemiesKilled += numKilled[team][i];
138  enemiesStunned += numStunned[team][i];
139  }
140 
141  cgi->LIST_AddString(&list, va(_("Enemies killed:\t%i"), enemiesKilled + enemiesStunned));
142  cgi->LIST_AddString(&list, va(_("Team survivors:\t%i"), numAlive[team]));
143  cgi->UI_RegisterLinkedListText(TEXT_LIST2, list);
144  if (winner == team) {
145  cgi->UI_PushWindow("won");
146  } else {
147  cgi->UI_PushWindow("lost");
148  }
149 }
150 
151 static const mapDef_t* GAME_MP_MapInfo (int step)
152 {
153  int i = 0;
154  const char* gameType = cgi->Cvar_GetString("sv_gametype");
155  for (;;) {
156  i++;
157  if (i > 100000)
158  break;
159 
160  const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
161  if (md == nullptr)
162  break;
163  if (!md->multiplayer || !cgi->LIST_ContainsString(md->gameTypes, gameType)) {
164  cgi->GAME_SwitchCurrentSelectedMap(step ? step : 1);
165  continue;
166  }
167  linkedList_t* gameNames = nullptr;
168  for (int j = 0; j < cgi->csi->numGTs; j++) {
169  const gametype_t* gt = &cgi->csi->gts[j];
170  if (cgi->LIST_ContainsString(md->gameTypes, gt->id)) {
171  cgi->LIST_AddString(&gameNames, _(gt->name));
172  }
173  }
174  cgi->UI_RegisterLinkedListText(TEXT_LIST2, gameNames);
175  cgi->Cvar_SetValue("ai_singleplayeraliens", md->maxAliens);
176 
177  return md;
178  }
179 
180  cgi->Com_Printf("no multiplayer map found for the current selected gametype: '%s'", gameType);
181  return nullptr;
182 }
183 
187 static void GAME_MP_UpdateGametype_f (void)
188 {
189  const int numGTs = cgi->csi->numGTs;
190  /* no types defined or parsed */
191  if (numGTs == 0)
192  return;
193 
194  cgi->Com_SetGameType();
195 
196  const char* gameType = cgi->Cvar_GetString("sv_gametype");
197  const mapDef_t* md = cgi->GAME_GetCurrentSelectedMap();
198  if (md != nullptr && md->multiplayer && cgi->LIST_ContainsString(md->gameTypes, gameType)) {
199  /* no change needed, gametype is supported */
200  return;
201  }
202 
203  GAME_MP_MapInfo(1);
204 }
205 
207 
211 static void GAME_MP_AddChatMessage (const char* text)
212 {
213  char message[2048];
214  Q_strncpyz(message, text, sizeof(message));
215 
216  const char* msg = Com_Trim(message);
217  cgi->LIST_AddString(&mp_chatMessageStack, msg);
218  cgi->HUD_DisplayMessage(msg);
219  cgi->UI_RegisterLinkedListText(TEXT_CHAT_WINDOW, mp_chatMessageStack);
220  cgi->UI_TextScrollEnd("hud_chat.allchats.chatscreen.chat");
221 }
222 
223 static bool GAME_MP_HandleServerCommand (const char* command, dbuffer* msg)
224 {
225  if (Q_streq(command, SV_CMD_TEAMINFO)) {
227  return true;
228  }
229 
230  return false;
231 }
232 
233 static const cmdList_t multiplayerCmds[] = {
234  {"mp_startserver", GAME_MP_StartServer_f, nullptr},
235  {"mp_updategametype", GAME_MP_UpdateGametype_f, "Update the menu values with current gametype values"},
236  {nullptr, nullptr, nullptr}
237 };
238 static void GAME_MP_InitStartup (void)
239 {
240  cgi->Cvar_ForceSet("sv_maxclients", "2");
242  cgi->Cvar_Set("cl_equip", "multiplayer_initial");
243 
244  cgi->Cmd_TableAddList(multiplayerCmds);
247 }
248 
249 static void GAME_MP_Shutdown (void)
250 {
251  cgi->Cmd_TableRemoveList(multiplayerCmds);
254 
255  cgi->SV_Shutdown("Game mode shutdown", false);
256 
257  OBJZERO(teamData);
258 }
259 
260 static void GAME_MP_RunFrame (float secondsSinceLastFrame)
261 {
262  if (!cgi->Com_ServerState() && cgi->CL_GetClientState() < ca_connected && Q_strnull(cgi->Cvar_GetString("rcon_address")))
263  return;
264 
267  if (!cgi->Com_ServerState() && Q_strnull(rcon_client_password->string)) {
268  cgi->UI_ExecuteConfunc("multiplayer_admin_panel 0");
269  } else {
270  cgi->UI_ExecuteConfunc("multiplayer_admin_panel 1");
271  }
272  }
273 
275  for (int i = 0; i < lengthof(cvars); i++) {
276  if (!cvars[i]->modified) {
277  continue;
278  }
279  cvars[i]->modified = false;
280  if (!cgi->Com_ServerState()) {
281  cgi->Cmd_ExecuteString(SV_CMD_RCON " set %s %s", cvars[i]->name, cvars[i]->string);
282  }
283  }
284 }
285 
286 #ifndef HARD_LINKED_CGAME
288 #else
289 const cgame_export_t* GetCGameMultiplayerAPI (const cgame_import_t* import)
290 #endif
291 {
292  static cgame_export_t e;
293 
294  OBJZERO(e);
295 
296  e.name = "Multiplayer mode";
297  e.menu = "multiplayer";
298  e.isMultiplayer = 1;
299  e.Init = GAME_MP_InitStartup;
300  e.Shutdown = GAME_MP_Shutdown;
302  e.Results = GAME_MP_Results;
303  e.EndRoundAnnounce = GAME_MP_EndRoundAnnounce;
304  e.StartBattlescape = GAME_MP_StartBattlescape;
305  e.NotifyEvent = GAME_MP_NotifyEvent;
306  e.AddChatMessage = GAME_MP_AddChatMessage;
307  e.HandleServerCommand = GAME_MP_HandleServerCommand;
308  e.RunFrame = GAME_MP_RunFrame;
309 
310  cgi = import;
311 
312  return &e;
313 }
int numGTs
Definition: q_shared.h:568
bool Q_strnull(const char *string)
Definition: shared.h:138
char name[MAX_VAR]
Definition: q_shared.h:343
char id[MAX_VAR]
Definition: q_shared.h:342
static void GAME_MP_InitStartup(void)
static bool GAME_MP_HandleServerCommand(const char *command, dbuffer *msg)
static void GAME_MP_Shutdown(void)
#define MAX_TEAMS
Definition: defines.h:98
static void GAME_MP_UpdateGametype_f(void)
Update the map according to the gametype.
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
#define SV_CMD_TEAMINFO
Definition: q_shared.h:591
int teams
Definition: q_shared.h:475
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
cvar_t * cl_roundtimelimit
#define _(String)
Definition: cl_shared.h:43
void * data
Definition: list.h:31
void *IMPORT * LIST_GetRandom(linkedList_t *list)
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
static const cmdList_t multiplayerCmds[]
Serverlist menu callbacks headers for multiplayer.
csi_t * csi
Definition: cgame.h:100
cvar_t * rcon_client_password
linkedList_t * gameTypes
Definition: q_shared.h:476
#define CGAME_HARD_LINKED_FUNCTIONS
Definition: cl_game.h:91
cvar_t * cl_maxsoldiersperplayer
int integer
Definition: cvar.h:81
char * Com_Trim(char *s)
Removed leading and trailing whitespaces.
Definition: shared.cpp:65
voidpf void * buf
Definition: ioapi.h:42
const char *IMPORT * Cvar_GetString(const char *varName)
const linkedList_t *IMPORT * LIST_ContainsString(const linkedList_t *list, const char *string)
const char *IMPORT * CL_PlayerGetName(unsigned int player)
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
cvar_t *IMPORT * Cvar_Set(const char *varName, const char *value,...) __attribute__((format(__printf__
static linkedList_t * mp_chatMessageStack
#define OBJZERO(obj)
Definition: shared.h:178
const mapDef_t *EXPORT * MapInfo(int step)
Serverlist management headers for multiplayer.
bool multiplayer
Definition: q_shared.h:474
gametype_t gts[MAX_GAMETYPES]
Definition: q_shared.h:567
static void GAME_MP_NotifyEvent(event_t eventType)
event_t
Possible event values.
Definition: q_shared.h:79
Multiplayer game type headers.
static void GAME_MP_AddChatMessage(const char *text)
Displays a chat on the hud and add it to the chat buffer.
static const cgame_import_t * cgi
linkedList_t * aircraft
Definition: q_shared.h:492
void GAME_MP_CallbacksInit(const cgame_import_t *import)
Definition: cmd.h:86
const mapDef_t *IMPORT * GAME_GetCurrentSelectedMap(void)
static void GAME_MP_EndRoundAnnounce(int playerNum, int team)
void GAME_MP_ParseTeamInfoMessage(dbuffer *msg)
Team selection text.
char * mapTheme
Definition: q_shared.h:464
static const mapDef_t * GAME_MP_MapInfo(int step)
const char *IMPORT * Com_GetRandomMapAssemblyNameForCraft(const char *craftID)
void GAME_MP_ServerListShutdown(void)
QGL_EXTERN GLint i
Definition: r_gl.h:113
char * string
Definition: cvar.h:73
static void GAME_MP_RunFrame(float secondsSinceLastFrame)
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
teamData_t teamData
static CGAME_HARD_LINKED_FUNCTIONS void GAME_MP_StartBattlescape(bool isTeamPlay)
static void GAME_MP_StartServer_f(void)
Starts a server and checks if the server loads a team unless he is a dedicated server admin...
void GAME_MP_CallbacksShutdown(void)
linkedList_t * ufos
Definition: q_shared.h:491
int isMultiplayer
Definition: cgame.h:40
void GAME_MP_ServerListInit(const cgame_import_t *import)
#define lengthof(x)
Definition: shared.h:105
const char * menu
Definition: cgame.h:39
#define Q_streq(a, b)
Definition: shared.h:136
#define SV_CMD_RCON
Definition: q_shared.h:589
bool modified
Definition: cvar.h:79
const char * name
Definition: cgame.h:38
cvar_t *IMPORT * Cvar_ForceSet(const char *varName, const char *value)
cvar_t * cl_maxsoldiersperteam
static void GAME_MP_Results(dbuffer *msg, int winner, int *numSpawned, int *numAlive, int numKilled[][MAX_TEAMS], int numStunned[][MAX_TEAMS], bool nextmap)
After a mission was finished this function is called.
const cgame_export_t * GetCGameAPI(const cgame_import_t *import)
int maxAliens
Definition: q_shared.h:483
linkedList_t * params
Definition: q_shared.h:465