00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "client.h"
00030 #include "battlescape/cl_localentity.h"
00031 #include "battlescape/events/e_server.h"
00032 #include "battlescape/cl_particle.h"
00033 #include "battlescape/cl_actor.h"
00034 #include "battlescape/cl_hud.h"
00035 #include "battlescape/cl_parse.h"
00036 #include "battlescape/events/e_parse.h"
00037 #include "battlescape/cl_view.h"
00038 #include "cl_console.h"
00039 #include "cl_screen.h"
00040 #include "cl_game.h"
00041 #include "cl_tutorials.h"
00042 #include "cl_tip.h"
00043 #include "cl_team.h"
00044 #include "cl_language.h"
00045 #include "cl_irc.h"
00046 #include "cl_sequence.h"
00047 #include "cl_inventory.h"
00048 #include "cl_menu.h"
00049 #include "cl_http.h"
00050 #include "input/cl_joystick.h"
00051 #include "cinematic/cl_cinematic.h"
00052 #include "sound/s_music.h"
00053 #include "sound/s_sample.h"
00054 #include "renderer/r_main.h"
00055 #include "renderer/r_particle.h"
00056 #include "ui/ui_main.h"
00057 #include "ui/ui_popup.h"
00058 #include "ui/ui_main.h"
00059 #include "ui/ui_font.h"
00060 #include "ui/ui_nodes.h"
00061 #include "ui/ui_parse.h"
00062 #include "multiplayer/mp_callbacks.h"
00063 #include "multiplayer/mp_serverlist.h"
00064 #include "multiplayer/mp_team.h"
00065 #include "../shared/infostring.h"
00066 #include "../shared/parse.h"
00067 #include "../ports/system.h"
00068
00069 cvar_t *cl_fps;
00070 cvar_t *cl_leshowinvis;
00071 cvar_t *cl_selected;
00072
00073 cvar_t *cl_lastsave;
00074
00075 static cvar_t *cl_connecttimeout;
00076
00077 static cvar_t *cl_introshown;
00078
00079
00080 static cvar_t *cl_name;
00081 static cvar_t *cl_msg;
00082 static cvar_t *cl_ready;
00083 cvar_t *cl_teamnum;
00084 cvar_t *cl_team;
00085
00086 client_static_t cls;
00087
00088 struct memPool_s *cl_genericPool;
00089 struct memPool_s *cl_ircSysPool;
00090 struct memPool_s *cl_soundSysPool;
00091 struct memPool_s *vid_genericPool;
00092 struct memPool_s *vid_imagePool;
00093 struct memPool_s *vid_lightPool;
00094 struct memPool_s *vid_modelPool;
00095
00096
00102 void Cmd_ForwardToServer (void)
00103 {
00104 const char *cmd = Cmd_Argv(0);
00105 struct dbuffer *msg;
00106
00107 if (cls.state <= ca_connected || cmd[0] == '-' || cmd[0] == '+') {
00108 Com_Printf("Unknown command \"%s\" - wasn't sent to server\n", cmd);
00109 return;
00110 }
00111
00112 msg = new_dbuffer();
00113 NET_WriteByte(msg, clc_stringcmd);
00114 dbuffer_add(msg, cmd, strlen(cmd));
00115 if (Cmd_Argc() > 1) {
00116 dbuffer_add(msg, " ", 1);
00117 dbuffer_add(msg, Cmd_Args(), strlen(Cmd_Args()));
00118 }
00119 dbuffer_add(msg, "", 1);
00120 NET_WriteMsg(cls.netStream, msg);
00121 }
00122
00127 static void CL_Env_f (void)
00128 {
00129 const int argc = Cmd_Argc();
00130
00131 if (argc == 3) {
00132 Sys_Setenv(Cmd_Argv(1), Cmd_Argv(2));
00133 } else if (argc == 2) {
00134 const char *env = SDL_getenv(Cmd_Argv(1));
00135 if (env)
00136 Com_Printf("%s=%s\n", Cmd_Argv(1), env);
00137 else
00138 Com_Printf("%s undefined\n", Cmd_Argv(1));
00139 }
00140 }
00141
00142
00143 static void CL_ForwardToServer_f (void)
00144 {
00145 if (cls.state != ca_connected && cls.state != ca_active) {
00146 Com_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
00147 return;
00148 }
00149
00150
00151 if (Cmd_Argc() > 1) {
00152 struct dbuffer *msg;
00153 msg = new_dbuffer();
00154 NET_WriteByte(msg, clc_stringcmd);
00155 dbuffer_add(msg, Cmd_Args(), strlen(Cmd_Args()) + 1);
00156 NET_WriteMsg(cls.netStream, msg);
00157 }
00158 }
00159
00160 static void CL_Quit_f (void)
00161 {
00162 CL_Disconnect();
00163 Com_Quit();
00164 }
00165
00172 void CL_Drop (void)
00173 {
00174 CL_Disconnect();
00175
00176
00177 SCR_EndLoadingPlaque();
00178
00179 GAME_Drop();
00180 }
00181
00187 static void CL_Connect (void)
00188 {
00189 Com_SetUserinfoModified(qfalse);
00190
00191 NET_DatagramSocketClose(cls.netDatagramSocket);
00192 cls.netDatagramSocket = NULL;
00193
00194 assert(!cls.netStream);
00195
00196 if (cls.servername[0] != '\0') {
00197 assert(cls.serverport[0] != '\0');
00198 Com_Printf("Connecting to %s %s...\n", cls.servername, cls.serverport);
00199 cls.netStream = NET_Connect(cls.servername, cls.serverport);
00200 } else {
00201 Com_Printf("Connecting to localhost...\n");
00202 cls.netStream = NET_ConnectToLoopBack();
00203 }
00204
00205 if (cls.netStream) {
00206 NET_OOB_Printf(cls.netStream, "connect %i \"%s\"\n", PROTOCOL_VERSION, Cvar_Userinfo());
00207 cls.connectTime = CL_Milliseconds();
00208 } else {
00209 if (cls.servername[0]) {
00210 assert(cls.serverport[0]);
00211 Com_Printf("Could not connect to %s %s\n", cls.servername, cls.serverport);
00212 } else {
00213 Com_Printf("Could not connect to localhost\n");
00214 }
00215 }
00216 }
00217
00224 static void CL_ClearState (void)
00225 {
00226 LE_Cleanup();
00227
00228
00229 memset(&cl, 0, sizeof(cl));
00230 cl.cam.zoom = 1.0;
00231 CL_ViewCalcFieldOfViewX();
00232
00233
00234 r_numParticles = 0;
00235
00236 refdef.rendererFlags &= ~RDF_IRGOGGLES;
00237 }
00238
00247 void CL_Disconnect (void)
00248 {
00249 struct dbuffer *msg;
00250
00251 if (cls.state < ca_connecting)
00252 return;
00253
00254
00255 if (!Com_ServerState()) {
00256 msg = new_dbuffer();
00257 NET_WriteByte(msg, clc_stringcmd);
00258 NET_WriteString(msg, "disconnect\n");
00259 NET_WriteMsg(cls.netStream, msg);
00260
00261 NET_Wait(0);
00262 }
00263
00264 NET_StreamFinished(cls.netStream);
00265 cls.netStream = NULL;
00266
00267 CL_ClearState();
00268
00269 S_Stop();
00270
00271 R_ShutdownModels(qfalse);
00272
00273 CL_SetClientState(ca_disconnected);
00274 CL_ClearBattlescapeEvents();
00275 GAME_EndBattlescape();
00276 }
00277
00278
00279
00280 #ifdef ACTIVATE_PACKET_COMMAND
00281
00288 static void CL_Packet_f (void)
00289 {
00290 int i, l;
00291 const char *in;
00292 char *out;
00293 struct net_stream *s;
00294
00295 if (Cmd_Argc() != 4) {
00296 Com_Printf("Usage: %s <destination> <port> <contents>\n", Cmd_Argv(0));
00297 return;
00298 }
00299
00300 s = NET_Connect(Cmd_Argv(1), Cmd_Argv(2));
00301 if (!s) {
00302 Com_Printf("Could not connect to %s at port %s\n", Cmd_Argv(1), Cmd_Argv(2));
00303 return;
00304 }
00305
00306 in = Cmd_Argv(3);
00307
00308 l = strlen(in);
00309 for (i = 0; i < l; i++) {
00310 if (in[i] == '\\' && in[i + 1] == 'n') {
00311 *out++ = '\n';
00312 i++;
00313 } else
00314 *out++ = in[i];
00315 }
00316 *out = 0;
00317
00318 NET_OOB_Printf(s, "%s %i", out, PROTOCOL_VERSION);
00319 NET_StreamFinished(s);
00320 }
00321 #endif
00322
00330 static void CL_ConnectionlessPacket (struct dbuffer *msg)
00331 {
00332 char s[1024];
00333 const char *c;
00334
00335 NET_ReadStringLine(msg, s, sizeof(s));
00336
00337 Cmd_TokenizeString(s, qfalse);
00338
00339 c = Cmd_Argv(0);
00340
00341 Com_DPrintf(DEBUG_CLIENT, "server OOB: %s\n", Cmd_Args());
00342
00343
00344 if (!strncmp(c, "client_connect", 13)) {
00345 int i;
00346 for (i = 1; i < Cmd_Argc(); i++) {
00347 const char *p = Cmd_Argv(i);
00348 if (!strncmp(p, "dlserver=", 9)) {
00349 p += 9;
00350 Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "ufo://%s", cls.servername);
00351 CL_SetHTTPServer(p);
00352 if (cls.downloadServer[0])
00353 Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer);
00354 }
00355 }
00356 if (cls.state == ca_connected) {
00357 Com_Printf("Dup connect received. Ignored.\n");
00358 return;
00359 }
00360 msg = new_dbuffer();
00361 NET_WriteByte(msg, clc_stringcmd);
00362 NET_WriteString(msg, "new\n");
00363 NET_WriteMsg(cls.netStream, msg);
00364 return;
00365 }
00366
00367
00368 if (!strncmp(c, "cmd", 3)) {
00369 if (!NET_StreamIsLoopback(cls.netStream)) {
00370 Com_Printf("Command packet from remote host. Ignored.\n");
00371 return;
00372 } else {
00373 char str[1024];
00374 NET_ReadString(msg, str, sizeof(str));
00375 Cbuf_AddText(str);
00376 Cbuf_AddText("\n");
00377 }
00378 return;
00379 }
00380
00381
00382 if (!strncmp(c, "teaminfo", 8)) {
00383 CL_ParseTeamInfoMessage(msg);
00384 return;
00385 }
00386
00387
00388 if (!strncmp(c, "ping", 4)) {
00389 NET_OOB_Printf(cls.netStream, "ack");
00390 return;
00391 }
00392
00393
00394 if (!strncmp(c, "echo", 4)) {
00395 NET_OOB_Printf(cls.netStream, "%s", Cmd_Argv(1));
00396 return;
00397 }
00398
00399
00400 if (!strncmp(c, "print", 5)) {
00401 char str[1024];
00402 NET_ReadString(msg, str, sizeof(str));
00403
00404 if (strstr(s, REJ_PASSWORD_REQUIRED_OR_INCORRECT))
00405 UI_PushWindow("serverpassword", NULL);
00406 UI_Popup(_("Notice"), _(str));
00407 return;
00408 }
00409
00410 Com_Printf("Unknown command received \"%s\"\n", c);
00411 }
00412
00420 static void CL_ReadPackets (void)
00421 {
00422 struct dbuffer *msg;
00423 while ((msg = NET_ReadMsg(cls.netStream))) {
00424 const svc_ops_t cmd = NET_ReadByte(msg);
00425 if (cmd == clc_oob)
00426 CL_ConnectionlessPacket(msg);
00427 else
00428 CL_ParseServerMessage(cmd, msg);
00429 free_dbuffer(msg);
00430 }
00431 }
00432
00437 static void CL_UserInfo_f (void)
00438 {
00439 Com_Printf("User info settings:\n");
00440 Info_Print(Cvar_Userinfo());
00441 }
00442
00447 static void CL_SpawnSoldiers_f (void)
00448 {
00449 if (!CL_OnBattlescape())
00450 return;
00451
00452 if (cl.spawned)
00453 return;
00454
00455 cl.spawned = qtrue;
00456 GAME_SpawnSoldiers();
00457 }
00458
00459 static qboolean CL_DownloadUMPMap (const char *tiles)
00460 {
00461 char name[MAX_VAR];
00462 char base[MAX_QPATH];
00463 qboolean startedDownload = qfalse;
00464
00465
00466 while (tiles) {
00467
00468 const char *token = Com_Parse(&tiles);
00469 if (!tiles)
00470 return startedDownload;
00471
00472
00473 if (token[0] == '-') {
00474 Q_strncpyz(base, token + 1, sizeof(base));
00475 continue;
00476 }
00477
00478
00479 if (token[0] == '+')
00480 Com_sprintf(name, sizeof(name), "%s%s", base, token + 1);
00481 else
00482 Q_strncpyz(name, token, sizeof(name));
00483
00484 startedDownload |= !CL_CheckOrDownloadFile(va("maps/%s.bsp", name));
00485 }
00486
00487 return startedDownload;
00488 }
00489
00490 static qboolean CL_DownloadMap (const char *map)
00491 {
00492 qboolean startedDownload;
00493 if (map[0] != '+') {
00494 startedDownload = !CL_CheckOrDownloadFile(va("maps/%s.bsp", map));
00495 } else {
00496 startedDownload = !CL_CheckOrDownloadFile(va("maps/%s.ump", map + 1));
00497 if (!startedDownload) {
00498 const char *tiles = CL_GetConfigString(CS_TILES);
00499 startedDownload = CL_DownloadUMPMap(tiles);
00500 }
00501 }
00502
00503 return startedDownload;
00504 }
00505
00511 static qboolean CL_CanMultiplayerStart (void)
00512 {
00513 const int day = CL_GetConfigStringInteger(CS_LIGHTMAP);
00514 const char *serverVersion = CL_GetConfigString(CS_VERSION);
00515
00516
00517 if (strcmp(UFO_VERSION, serverVersion)) {
00518 Com_sprintf(popupText, sizeof(popupText), _("Local game version (%s) differs from the server version (%s)"), UFO_VERSION, serverVersion);
00519 UI_Popup(_("Error"), popupText);
00520 Com_Error(ERR_DISCONNECT, "Local game version (%s) differs from the server version (%s)", UFO_VERSION, serverVersion);
00521
00522 } else if (csi.numODs != CL_GetConfigStringInteger(CS_OBJECTAMOUNT)) {
00523 UI_Popup(_("Error"), _("Script files are not the same"));
00524 Com_Error(ERR_DISCONNECT, "Script files are not the same");
00525 }
00526
00527
00528 SCR_BeginLoadingPlaque();
00529
00530
00531 if (cls.downloadMaps) {
00532 if (CL_DownloadMap(CL_GetConfigString(CS_NAME)))
00533 return qfalse;
00534 cls.downloadMaps = qfalse;
00535 }
00536
00537
00538 if (CL_PendingHTTPDownloads())
00539 return qfalse;
00540
00541 if (Com_GetScriptChecksum() != CL_GetConfigStringInteger(CS_UFOCHECKSUM))
00542 Com_Printf("You are using modified ufo script files - might produce problems\n");
00543
00544 CM_LoadMap(CL_GetConfigString(CS_TILES), day, CL_GetConfigString(CS_POSITIONS), cl.mapData, cl.mapTiles);
00545
00546 if (cl.mapData->mapChecksum != CL_GetConfigStringInteger(CS_MAPCHECKSUM)) {
00547 UI_Popup(_("Error"), _("Local map version differs from server"));
00548 Com_Error(ERR_DISCONNECT, "Local map version differs from server: %u != '%i'",
00549 cl.mapData->mapChecksum, CL_GetConfigStringInteger(CS_MAPCHECKSUM));
00550 }
00551
00552 return qtrue;
00553 }
00554
00561 void CL_RequestNextDownload (void)
00562 {
00563 if (cls.state != ca_connected) {
00564 Com_Printf("CL_RequestNextDownload: Not connected (%i)\n", cls.state);
00565 return;
00566 }
00567
00568
00569 cl.mapTiles = SV_GetMapTiles();
00570 cl.mapData = SV_GetMapData();
00571
00572
00573
00574 if (!Com_ServerState() && !CL_CanMultiplayerStart())
00575 return;
00576
00577 CL_ViewLoadMedia();
00578
00579 {
00580 struct dbuffer *msg = new_dbuffer();
00581
00582
00583 NET_WriteByte(msg, clc_stringcmd);
00584
00585 NET_WriteString(msg, "begin\n");
00586 NET_WriteMsg(cls.netStream, msg);
00587 }
00588
00589 cls.waitingForStart = CL_Milliseconds();
00590 }
00591
00592
00598 static void CL_Precache_f (void)
00599 {
00600 cls.downloadMaps = qtrue;
00601
00602 CL_RequestNextDownload();
00603 }
00604
00605 static void CL_SetRatioFilter_f (void)
00606 {
00607 uiNode_t* firstOption = UI_GetOption(OPTION_VIDEO_RESOLUTIONS);
00608 uiNode_t* option = firstOption;
00609 float requestedRation = atof(Cmd_Argv(1));
00610 qboolean all = qfalse;
00611 qboolean custom = qfalse;
00612 const float delta = 0.01;
00613
00614 if (Cmd_Argc() != 2) {
00615 Com_Printf("Usage: %s <all|floatration>\n", Cmd_Argv(0));
00616 return;
00617 }
00618
00619 if (!strcmp(Cmd_Argv(1), "all"))
00620 all = qtrue;
00621 else if (!strcmp(Cmd_Argv(1), "custom"))
00622 custom = qtrue;
00623 else
00624 requestedRation = atof(Cmd_Argv(1));
00625
00626 while (option) {
00627 int width;
00628 int height;
00629 float ratio;
00630 qboolean visible = qfalse;
00631 int result = sscanf(OPTIONEXTRADATA(option).label, "%i x %i", &width, &height);
00632 if (result != 2)
00633 Com_Error(ERR_FATAL, "CL_SetRatioFilter_f: Impossible to decode resolution label.\n");
00634 ratio = (float)width / (float)height;
00635
00636 if (all)
00637 visible = qtrue;
00638 else if (custom)
00640 visible = ratio > 2 || (ratio > 1.7 && ratio < 1.76);
00641 else
00642 visible = ratio - delta < requestedRation && ratio + delta > requestedRation;
00643
00644 option->invis = !visible;
00645 option = option->next;
00646 }
00647
00648
00649 UI_RegisterOption(OPTION_VIDEO_RESOLUTIONS, firstOption);
00650 }
00651
00652 static void CL_VideoInitMenu (void)
00653 {
00654 uiNode_t* option = UI_GetOption(OPTION_VIDEO_RESOLUTIONS);
00655 if (option == NULL) {
00656 int i;
00657 for (i = 0; i < VID_GetModeNums(); i++) {
00658 vidmode_t vidmode;
00659 if (VID_GetModeInfo(i, &vidmode))
00660 UI_AddOption(&option, va("r%ix%i", vidmode.width, vidmode.height), va("%i x %i", vidmode.width, vidmode.height), va("%i", i));
00661 }
00662 UI_RegisterOption(OPTION_VIDEO_RESOLUTIONS, option);
00663 }
00664 }
00665
00666 static void CL_TeamDefInitMenu (void)
00667 {
00668 uiNode_t* option = UI_GetOption(OPTION_TEAMDEFS);
00669 if (option == NULL) {
00670 int i;
00671 for (i = 0; i < csi.numTeamDefs; i++) {
00672 teamDef_t *td = &csi.teamDef[i];
00673 if (td->race != RACE_CIVILIAN)
00674 UI_AddOption(&option, td->id, _(td->name), td->id);
00675 }
00676 UI_RegisterOption(OPTION_TEAMDEFS, option);
00677 }
00678 }
00679
00681 static const value_t customskin_vals[] = {
00682 {"name", V_STRING, offsetof(customSkin_t, name), 0},
00683 {"singleplayer", V_BOOL, offsetof(customSkin_t, singleplayer), MEMBER_SIZEOF(customSkin_t, singleplayer)},
00684 {"multiplayer", V_BOOL, offsetof(customSkin_t, multiplayer), MEMBER_SIZEOF(customSkin_t, multiplayer)},
00685
00686 {NULL, 0, 0, 0}
00687 };
00688
00689
00690 static void CL_ParseCustomSkin (const char *name, const char **text)
00691 {
00692 const char *errhead = "CL_ParseCustomSkin: unexpected end of file (customskin ";
00693 customSkin_t *skin;
00694 const value_t *vp;
00695 const char *token;
00696
00697
00698 token = Com_Parse(text);
00699
00700 if (!*text || *token != '{') {
00701 Com_Printf("CL_ParseCustomSkin: customskin \"%s\" without body ignored\n", name);
00702 return;
00703 }
00704
00705 skin = Com_AllocateCustomSkin();
00706
00707 skin->id = Mem_PoolStrDup(name, com_genericPool, 0);
00708
00709 do {
00710 token = Com_EParse(text, errhead, name);
00711 if (!*text)
00712 break;
00713 if (*token == '}')
00714 break;
00715
00716 for (vp = customskin_vals; vp->string; vp++)
00717 if (!strcmp(token, vp->string)) {
00718
00719 token = Com_EParse(text, errhead, name);
00720 if (!*text)
00721 return;
00722
00723 switch (vp->type) {
00724 default:
00725 Com_EParseValue(skin, token, vp->type, vp->ofs, vp->size);
00726 break;
00727 }
00728 break;
00729 }
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761 } while (*text);
00762
00763 }
00764
00766 static const value_t mapdef_vals[] = {
00767 {"description", V_TRANSLATION_STRING, offsetof(mapDef_t, description), 0},
00768 {"map", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, map), 0},
00769 {"param", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, param), 0},
00770 {"size", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, size), 0},
00771
00772 {"maxaliens", V_INT, offsetof(mapDef_t, maxAliens), MEMBER_SIZEOF(mapDef_t, maxAliens)},
00773 {"storyrelated", V_BOOL, offsetof(mapDef_t, storyRelated), MEMBER_SIZEOF(mapDef_t, storyRelated)},
00774 {"hurtaliens", V_BOOL, offsetof(mapDef_t, hurtAliens), MEMBER_SIZEOF(mapDef_t, hurtAliens)},
00775
00776 {"teams", V_INT, offsetof(mapDef_t, teams), MEMBER_SIZEOF(mapDef_t, teams)},
00777 {"multiplayer", V_BOOL, offsetof(mapDef_t, multiplayer), MEMBER_SIZEOF(mapDef_t, multiplayer)},
00778
00779 {"onwin", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, onwin), 0},
00780 {"onlose", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, onlose), 0},
00781
00782 {NULL, 0, 0, 0}
00783 };
00784
00785 static void CL_ParseMapDefinition (const char *name, const char **text)
00786 {
00787 const char *errhead = "Com_ParseMapDefinition: unexpected end of file (mapdef ";
00788 mapDef_t *md;
00789 const value_t *vp;
00790 const char *token;
00791
00792
00793 token = Com_Parse(text);
00794
00795 if (!*text || *token != '{') {
00796 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" without body ignored\n", name);
00797 return;
00798 }
00799
00800 md = Com_GetMapDefByIDX(cls.numMDs);
00801 cls.numMDs++;
00802 if (cls.numMDs >= lengthof(cls.mds))
00803 Sys_Error("Com_ParseMapDefinition: Max mapdef hit");
00804
00805 memset(md, 0, sizeof(*md));
00806 md->id = Mem_PoolStrDup(name, com_genericPool, 0);
00807
00808 do {
00809 token = Com_EParse(text, errhead, name);
00810 if (!*text)
00811 break;
00812 if (*token == '}')
00813 break;
00814
00815 for (vp = mapdef_vals; vp->string; vp++)
00816 if (!strcmp(token, vp->string)) {
00817
00818 token = Com_EParse(text, errhead, name);
00819 if (!*text)
00820 return;
00821
00822 switch (vp->type) {
00823 default:
00824 Com_EParseValue(md, token, vp->type, vp->ofs, vp->size);
00825 break;
00826 case V_TRANSLATION_STRING:
00827 if (*token == '_')
00828 token++;
00829
00830 case V_CLIENT_HUNK_STRING:
00831 Mem_PoolStrDupTo(token, (char**) ((char*)md + (int)vp->ofs), com_genericPool, 0);
00832 break;
00833 }
00834 break;
00835 }
00836
00837 if (!vp->string) {
00838 linkedList_t **list;
00839 if (!strcmp(token, "ufos")) {
00840 list = &md->ufos;
00841 } else if (!strcmp(token, "aircraft")) {
00842 list = &md->aircraft;
00843 } else if (!strcmp(token, "terrains")) {
00844 list = &md->terrains;
00845 } else if (!strcmp(token, "populations")) {
00846 list = &md->populations;
00847 } else if (!strcmp(token, "cultures")) {
00848 list = &md->cultures;
00849 } else if (!strcmp(token, "gametypes")) {
00850 list = &md->gameTypes;
00851 } else {
00852 Com_Printf("Com_ParseMapDefinition: unknown token \"%s\" ignored (mapdef %s)\n", token, name);
00853 continue;
00854 }
00855 token = Com_EParse(text, errhead, name);
00856 if (!*text || *token != '{') {
00857 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" has ufos, gametypes, terrains, populations or cultures block with no opening brace\n", name);
00858 break;
00859 }
00860 do {
00861 token = Com_EParse(text, errhead, name);
00862 if (!*text || *token == '}')
00863 break;
00864 LIST_AddString(list, token);
00865 } while (*text);
00866 }
00867 } while (*text);
00868
00869 if (!md->map) {
00870 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no map\n", name);
00871 cls.numMDs--;
00872 }
00873
00874 if (!md->description) {
00875 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no description\n", name);
00876 cls.numMDs--;
00877 }
00878 }
00879
00883 static int Com_MapDefSort (const void *mapDef1, const void *mapDef2)
00884 {
00885 const char *map1 = ((const mapDef_t *)mapDef1)->map;
00886 const char *map2 = ((const mapDef_t *)mapDef2)->map;
00887
00888
00889 if (map1[0] == '+' || map1[0] == '.')
00890 map1++;
00891 if (map2[0] == '+' || map2[0] == '.')
00892 map2++;
00893
00894 return Q_StringSort(map1, map2);
00895 }
00896
00901 void CL_InitAfter (void)
00902 {
00903 if (sv_dedicated->integer)
00904 return;
00905
00906
00907 S_Frame();
00908 S_LoadSamples();
00909
00910
00911 CL_ViewPrecacheModels();
00912
00913 CL_TeamDefInitMenu();
00914 CL_VideoInitMenu();
00915 IN_JoystickInitMenu();
00916
00917 CL_LanguageInit();
00918
00919
00920 qsort(cls.mds, cls.numMDs, sizeof(mapDef_t), Com_MapDefSort);
00921 }
00922
00936 void CL_ParseClientData (const char *type, const char *name, const char **text)
00937 {
00938 if (!strcmp(type, "font"))
00939 UI_ParseFont(name, text);
00940 else if (!strcmp(type, "tutorial"))
00941 TUT_ParseTutorials(name, text);
00942 else if (!strcmp(type, "menu_model"))
00943 UI_ParseUIModel(name, text);
00944 else if (!strcmp(type, "icon"))
00945 UI_ParseIcon(name, text);
00946 else if (!strcmp(type, "particle"))
00947 CL_ParseParticle(name, text);
00948 else if (!strcmp(type, "sequence"))
00949 CL_ParseSequence(name, text);
00950 else if (!strcmp(type, "music"))
00951 M_ParseMusic(name, text);
00952 else if (!strcmp(type, "tips"))
00953 CL_ParseTipsOfTheDay(name, text);
00954 else if (!strcmp(type, "language"))
00955 CL_ParseLanguages(name, text);
00956 else if (!strcmp(type, "window"))
00957 UI_ParseWindow(type, name, text);
00958 else if (!strcmp(type, "component"))
00959 UI_ParseComponent(type, text);
00960 else if (!strcmp(type, "mapdef"))
00961 CL_ParseMapDefinition(name, text);
00962 else if (!strcmp(type, "customskin"))
00963 CL_ParseCustomSkin(name, text);
00964 }
00965
00967 static cvarList_t checkcvar[] = {
00968 {"cl_name", NULL, NULL},
00969 {"s_language", NULL, NULL},
00970
00971 {NULL, NULL, NULL}
00972 };
00976 static void CL_CheckCvars_f (void)
00977 {
00978 int i = 0;
00979
00980 while (checkcvar[i].name) {
00981 if (!checkcvar[i].var)
00982 checkcvar[i].var = Cvar_Get(checkcvar[i].name, "", 0, NULL);
00983 if (checkcvar[i].var->string[0] == '\0') {
00984 Com_Printf("%s has no value\n", checkcvar[i].var->name);
00985 UI_PushWindow("checkcvars", NULL);
00986 break;
00987 }
00988 i++;
00989 }
00990 }
00991
00996 static void CL_ShowConfigstrings_f (void)
00997 {
00998 int i;
00999
01000 for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
01001 const char *configString = CL_GetConfigString(i);
01002 if (configString[0] == '\0')
01003 continue;
01004 Com_Printf("configstring[%3i]: %s\n", i, configString);
01005 }
01006 }
01007
01013 static void CL_InitLocal (void)
01014 {
01015 CL_SetClientState(ca_disconnected);
01016 cls.realtime = Sys_Milliseconds();
01017
01018
01019 cl_introshown = Cvar_Get("cl_introshown", "0", CVAR_ARCHIVE, "Only show the intro once at the initial start");
01020 cl_fps = Cvar_Get("cl_fps", "0", CVAR_ARCHIVE, "Show frames per second");
01021 cl_log_battlescape_events = Cvar_Get("cl_log_battlescape_events", "1", 0, "Log all battlescape events to events.log");
01022 cl_selected = Cvar_Get("cl_selected", "0", CVAR_NOSET, "Current selected soldier");
01023 cl_connecttimeout = Cvar_Get("cl_connecttimeout", "25000", CVAR_ARCHIVE, "Connection timeout for multiplayer connects");
01024 cl_lastsave = Cvar_Get("cl_lastsave", "", CVAR_ARCHIVE, "Last saved slot - use for the continue-campaign function");
01025
01026 cl_name = Cvar_Get("cl_name", Sys_GetCurrentUser(), CVAR_USERINFO | CVAR_ARCHIVE, "Playername");
01027 cl_team = Cvar_Get("cl_team", "1", CVAR_USERINFO, "Humans (0) or aliens (7)");
01028 cl_teamnum = Cvar_Get("cl_teamnum", "1", CVAR_USERINFO | CVAR_ARCHIVE, "Teamnum for multiplayer teamplay games");
01029 cl_ready = Cvar_Get("cl_ready", "0", CVAR_USERINFO, "Ready indicator in the userinfo for tactical missions");
01030 cl_msg = Cvar_Get("cl_msg", "2", CVAR_USERINFO | CVAR_ARCHIVE, "Sets the message level for server messages the client receives");
01031 sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "If sv_maxclients is 1 we are in singleplayer - otherwise we are multiplayer mode (see sv_teamplay)");
01032
01033 masterserver_url = Cvar_Get("masterserver_url", MASTER_SERVER, CVAR_ARCHIVE, "URL of UFO:AI masterserver");
01034
01035 cl_map_debug = Cvar_Get("debug_map", "0", 0, "Activate realtime map debugging options - bitmask. Valid values are 0, 1, 3 and 7");
01036 cl_le_debug = Cvar_Get("debug_le", "0", 0, "Activates some local entity debug rendering");
01037 cl_leshowinvis = Cvar_Get("cl_leshowinvis", "0", CVAR_ARCHIVE, "Show invisible local entities as null models");
01038
01039
01040 Cmd_AddCommand("check_cvars", CL_CheckCvars_f, "Check cvars like playername and so on");
01041 Cmd_AddCommand("targetalign", CL_ActorTargetAlign_f, _("Target your shot to the ground"));
01042
01043 Cmd_AddCommand("cl_setratiofilter", CL_SetRatioFilter_f, "Filter the resolution screen list with a ration");
01044
01045 Cmd_AddCommand("cmd", CL_ForwardToServer_f, "Forward to server");
01046 Cmd_AddCommand("cl_userinfo", CL_UserInfo_f, "Prints your userinfo string");
01047 #ifdef ACTIVATE_PACKET_COMMAND
01048
01049 Cmd_AddCommand("packet", CL_Packet_f, "Dangerous debug function for network testing");
01050 #endif
01051 Cmd_AddCommand("quit", CL_Quit_f, "Quits the game");
01052 Cmd_AddCommand("env", CL_Env_f, NULL);
01053
01054 Cmd_AddCommand("precache", CL_Precache_f, "Function that is called at mapload to precache map data");
01055 Cmd_AddCommand("spawnsoldiers", CL_SpawnSoldiers_f, "Spawns the soldiers for the selected teamnum");
01056 Cmd_AddCommand("cl_configstrings", CL_ShowConfigstrings_f, "Print client configstrings to game console");
01057
01058
01059
01060
01061
01062 Cmd_AddCommand("say", NULL, "Chat command");
01063 Cmd_AddCommand("say_team", NULL, "Team chat command");
01064 Cmd_AddCommand("players", NULL, "List of team and player name");
01065 #ifdef DEBUG
01066 Cmd_AddCommand("debug_cgrid", Grid_DumpWholeClientMap_f, "Shows the whole client side pathfinding grid of the current loaded map");
01067 Cmd_AddCommand("debug_croute", Grid_DumpClientRoutes_f, "Shows the whole client side pathfinding grid of the current loaded map");
01068 Cmd_AddCommand("debug_listle", LE_List_f, "Shows a list of current know local entities with type and status");
01069 Cmd_AddCommand("debug_listlm", LM_List_f, "Shows a list of current know local models");
01070
01071 Cmd_AddCommand("debug_edictdestroy", NULL, "Call the 'destroy' function of a given edict");
01072 Cmd_AddCommand("debug_edictuse", NULL, "Call the 'use' function of a given edict");
01073 Cmd_AddCommand("debug_edicttouch", NULL, "Call the 'touch' function of a given edict");
01074 Cmd_AddCommand("debug_killteam", NULL, "Kills a given team");
01075 Cmd_AddCommand("debug_stunteam", NULL, "Stuns a given team");
01076 Cmd_AddCommand("debug_listscore", NULL, "Shows mission-score entries of all team members");
01077 #endif
01078
01079 IN_Init();
01080 CL_ServerEventsInit();
01081 CL_CameraInit();
01082
01083 CLMN_InitStartup();
01084 TUT_InitStartup();
01085 PTL_InitStartup();
01086 GAME_InitStartup();
01087 SEQ_InitStartup();
01088 ACTOR_InitStartup();
01089 TEAM_InitStartup();
01090 TOTD_InitStartup();
01091 HUD_InitStartup();
01092 INV_InitStartup();
01093 HTTP_InitStartup();
01094 }
01095
01101 static void CL_SendChangedUserinfos (void)
01102 {
01103
01104 if (cls.state >= ca_connected) {
01105 if (Com_IsUserinfoModified()) {
01106 struct dbuffer *msg = new_dbuffer();
01107 NET_WriteByte(msg, clc_userinfo);
01108 NET_WriteString(msg, Cvar_Userinfo());
01109 NET_WriteMsg(cls.netStream, msg);
01110 Com_SetUserinfoModified(qfalse);
01111 }
01112 }
01113 }
01114
01118 static void CL_SendCommand (void)
01119 {
01120
01121 IN_SendKeyEvents();
01122
01123
01124 Cbuf_Execute();
01125
01126
01127 CL_SendChangedUserinfos();
01128
01129
01130 Cvar_FixCheatVars();
01131
01132 switch (cls.state) {
01133 case ca_disconnected:
01134
01135 if (Com_ServerState()) {
01136 cls.servername[0] = '\0';
01137 cls.serverport[0] = '\0';
01138 CL_SetClientState(ca_connecting);
01139 return;
01140 }
01141 break;
01142 case ca_connecting:
01143 if (CL_Milliseconds() - cls.connectTime > cl_connecttimeout->integer) {
01144 if (GAME_IsMultiplayer())
01145 Com_Error(ERR_DROP, "Server is not reachable");
01146 }
01147 break;
01148 case ca_connected:
01149 if (cls.waitingForStart) {
01150 if (CL_Milliseconds() - cls.waitingForStart > cl_connecttimeout->integer) {
01151 Com_Error(ERR_DROP, "Server aborted connection - the server didn't response in %is. You can try to increase the cvar cl_connecttimeout",
01152 cl_connecttimeout->integer / 1000);
01153 } else {
01154 Com_sprintf(cls.loadingMessages, sizeof(cls.loadingMessages),
01155 "%s (%i)", _("Awaiting game start"), (CL_Milliseconds() - cls.waitingForStart) / 1000);
01156 SCR_UpdateScreen();
01157 }
01158 }
01159 break;
01160 default:
01161 break;
01162 }
01163 }
01164
01168 void CL_SetClientState (int state)
01169 {
01170 Com_DPrintf(DEBUG_CLIENT, "CL_SetClientState: Set new state to %i (old was: %i)\n", state, cls.state);
01171 cls.state = state;
01172
01173 switch (cls.state) {
01174 case ca_uninitialized:
01175 Com_Error(ERR_FATAL, "CL_SetClientState: Don't set state ca_uninitialized\n");
01176 break;
01177 case ca_sequence:
01178 refdef.ready = qtrue;
01179 break;
01180 case ca_active:
01181 cls.waitingForStart = 0;
01182 break;
01183 case ca_connecting:
01184 CL_Connect();
01185 break;
01186 case ca_disconnected:
01187 cls.waitingForStart = 0;
01188 break;
01189 case ca_connected:
01190
01191 CL_ClearState();
01192 break;
01193 default:
01194 break;
01195 }
01196 }
01197
01201 void CL_Frame (int now, void *data)
01202 {
01203 static int lastFrame = 0;
01204 int delta;
01205
01206 if (sys_priority->modified || sys_affinity->modified)
01207 Sys_SetAffinityAndPriority();
01208
01209
01210 delta = now - lastFrame;
01211 if (lastFrame)
01212 cls.frametime = delta / 1000.0;
01213 else
01214 cls.frametime = 0;
01215 cls.realtime = Sys_Milliseconds();
01216 cl.time = now;
01217 lastFrame = now;
01218
01219
01220 if (delta)
01221 cls.framerate = 1000.0 / delta;
01222
01223 if (cls.state == ca_connected) {
01224
01225 CL_RunHTTPDownloads();
01226 }
01227
01228
01229 CL_ReadPackets();
01230
01231 CL_SendCommand();
01232
01233 IN_Frame();
01234
01235 GAME_Frame();
01236
01237
01238 CL_CameraMove();
01239
01240
01241 CL_RunMapParticles();
01242 CL_ParticleRun();
01243
01244
01245 SCR_UpdateScreen();
01246
01247
01248 SCR_RunConsole();
01249
01250
01251 LE_Think();
01252
01253
01254 S_Frame();
01255
01256
01257 CL_SendCommand();
01258 }
01259
01263 void CL_SlowFrame (int now, void *data)
01264 {
01265
01266 if (s_language->modified)
01267 CL_LanguageTryToSet(s_language->string);
01268
01269 Irc_Logic_Frame();
01270
01271 HUD_Update();
01272 }
01273
01274 static void CL_InitMemPools (void)
01275 {
01276 cl_genericPool = Mem_CreatePool("Client: Generic");
01277 cl_soundSysPool = Mem_CreatePool("Client: Sound system");
01278 cl_ircSysPool = Mem_CreatePool("Client: IRC system");
01279 }
01280
01285 void CL_Init (void)
01286 {
01287
01288 char languagePath[MAX_OSPATH];
01289 cvar_t *fs_i18ndir;
01290
01291 if (sv_dedicated->integer)
01292 return;
01293
01294 memset(&cls, 0, sizeof(cls));
01295
01296 fs_i18ndir = Cvar_Get("fs_i18ndir", "", 0, "System path to language files");
01297
01298 setlocale(LC_ALL, "C");
01299 setlocale(LC_MESSAGES, "");
01300
01301 if (fs_i18ndir->string[0] != '\0')
01302 Q_strncpyz(languagePath, fs_i18ndir->string, sizeof(languagePath));
01303 else
01304 Com_sprintf(languagePath, sizeof(languagePath), "%s/"BASEDIRNAME"/i18n/", FS_GetCwd());
01305 Com_DPrintf(DEBUG_CLIENT, "...using mo files from %s\n", languagePath);
01306 bindtextdomain(TEXT_DOMAIN, languagePath);
01307 bind_textdomain_codeset(TEXT_DOMAIN, "UTF-8");
01308
01309 textdomain(TEXT_DOMAIN);
01310
01311 CL_InitMemPools();
01312
01313
01314 Con_Init();
01315
01316 CIN_Init();
01317
01318 VID_Init();
01319 S_Init();
01320 SCR_Init();
01321
01322 CL_InitLocal();
01323
01324 Irc_Init();
01325 CL_ViewInit();
01326
01327 CL_InitParticles();
01328
01329 CL_ClearState();
01330
01331
01332 Mem_TouchGlobal();
01333 }
01334
01335 int CL_Milliseconds (void)
01336 {
01337 return cls.realtime;
01338 }
01339
01347 void CL_Shutdown (void)
01348 {
01349 static qboolean isdown = qfalse;
01350
01351 if (isdown) {
01352 printf("recursive shutdown\n");
01353 return;
01354 }
01355 isdown = qtrue;
01356
01357 GAME_SetMode(GAME_NONE);
01358 CL_HTTP_Cleanup();
01359 Irc_Shutdown();
01360 Con_SaveConsoleHistory();
01361 Key_WriteBindings("keys.cfg");
01362 S_Shutdown();
01363 R_Shutdown();
01364 UI_Shutdown();
01365 CIN_Shutdown();
01366 }