/* =============== HTTP_StartDownload Actually starts a download by adding it to the curl multi handle. =============== */ void HTTP_StartDownload (dlhandle_t *dl) { cvar_t *hostname; char escapedFilePath[2048]; hostname = gi.cvar ("hostname", NULL, 0); if (!hostname) TDM_Error ("HTTP_StartDownload: Couldn't get hostname cvar"); dl->tempBuffer = NULL; dl->speed = 0; dl->fileSize = 0; dl->position = 0; if (!dl->curl) dl->curl = curl_easy_init (); HTTP_EscapePath (dl->filePath, escapedFilePath); Com_sprintf (dl->URL, sizeof(dl->URL), "http://%s%s%s", otdm_api_ip, g_http_path->string, escapedFilePath); curl_easy_setopt (dl->curl, CURLOPT_HTTPHEADER, http_header_slist); curl_easy_setopt (dl->curl, CURLOPT_ENCODING, ""); curl_easy_setopt (dl->curl, CURLOPT_DEBUGFUNCTION, CURL_Debug); curl_easy_setopt (dl->curl, CURLOPT_VERBOSE, 1); curl_easy_setopt (dl->curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt (dl->curl, CURLOPT_WRITEDATA, dl); if (g_http_bind->string[0]) curl_easy_setopt (dl->curl, CURLOPT_INTERFACE, g_http_bind->string); else curl_easy_setopt (dl->curl, CURLOPT_INTERFACE, NULL); curl_easy_setopt (dl->curl, CURLOPT_WRITEFUNCTION, HTTP_Recv); if (g_http_proxy->string[0]) curl_easy_setopt (dl->curl, CURLOPT_PROXY, g_http_proxy->string); else curl_easy_setopt (dl->curl, CURLOPT_PROXY, NULL); curl_easy_setopt (dl->curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt (dl->curl, CURLOPT_MAXREDIRS, 5); curl_easy_setopt (dl->curl, CURLOPT_WRITEHEADER, dl); curl_easy_setopt (dl->curl, CURLOPT_HEADERFUNCTION, HTTP_Header); curl_easy_setopt (dl->curl, CURLOPT_PROGRESSDATA, dl); curl_easy_setopt (dl->curl, CURLOPT_USERAGENT, "OpenTDM (" OPENTDM_VERSION ")"); curl_easy_setopt (dl->curl, CURLOPT_REFERER, hostname->string); curl_easy_setopt (dl->curl, CURLOPT_URL, dl->URL); if (curl_multi_add_handle (multi, dl->curl) != CURLM_OK) { gi.dprintf ("HTTP_StartDownload: curl_multi_add_handle: error\n"); return; } handleCount++; }
/* ============== TDM_AddPlayerToMatchinfo ============== A new player joined mid-match (invite / etc), update teamplayerinfo to reflect this. */ void TDM_AddPlayerToMatchinfo (edict_t *ent) { int i; teamplayer_t *new_teamplayers; if (!current_matchinfo.teamplayers) TDM_Error ("TDM_AddPlayerToMatchinfo: no teamplayers"); //allocate a new teamplayers array new_teamplayers = gi.TagMalloc (sizeof(teamplayer_t) * (current_matchinfo.num_teamplayers + 1), TAG_GAME); memcpy (new_teamplayers, current_matchinfo.teamplayers, sizeof(teamplayer_t) * current_matchinfo.num_teamplayers); //setup this client TDM_SetupTeamInfoForPlayer (ent, new_teamplayers + current_matchinfo.num_teamplayers); //fix up pointers on existing clients and other things that reference teamplayers. NASTY! for (i = 0; i < current_matchinfo.num_teamplayers; i++) { if (current_matchinfo.captains[TEAM_A] == current_matchinfo.teamplayers + i) current_matchinfo.captains[TEAM_A] = new_teamplayers + i; else if (current_matchinfo.captains[TEAM_B] == current_matchinfo.teamplayers + i) current_matchinfo.captains[TEAM_B] = new_teamplayers + i; if (level.tdm_timeout_caller == current_matchinfo.teamplayers + i) level.tdm_timeout_caller = new_teamplayers + i; //might not have a current client (disconnected) if (current_matchinfo.teamplayers[i].client) current_matchinfo.teamplayers[i].client->client->resp.teamplayerinfo = new_teamplayers + i; } //remove old memory gi.TagFree (current_matchinfo.teamplayers); //move everything to new current_matchinfo.teamplayers = new_teamplayers; current_matchinfo.num_teamplayers++; }
void ParseEntityString (qboolean respawn) { edict_t *ent; int inhibit; const char *com_token; const char *entities; int i; entities = level.entity_string; ent = NULL; inhibit = 0; i = 0; //mark all the spawned_entities as inuse so G_Spawn doesn't try to pick them during //trigger spawning etc for (i = 0; i < num_spawned_entities; i++) spawned_entities[i]->inuse = true; i = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (respawn) { ent = spawned_entities[i]; if (ent != world) { //double check we aren't overwriting stuff that we shouldn't be if (ent->enttype == ENT_DOOR_TRIGGER || ent->enttype == ENT_PLAT_TRIGGER) TDM_Error ("ParseEntityString: Trying to reuse an already spawned ent!"); G_FreeEdict (ent); G_InitEdict (ent); } i++; } else { if (!ent) ent = g_edicts; else ent = G_Spawn (); } entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; //have to keep num_spawned in sync for respawn if (!respawn) spawned_entities[num_spawned_entities++] = ent; continue; } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } else if (respawn) continue; ED_CallSpawn (ent); if (!respawn) { if (num_spawned_entities == MAX_EDICTS * 4) TDM_Error ("Exceeded MAX_EDICTS * 4 entities during entity string parse"); spawned_entities[num_spawned_entities++] = ent; } else { //an elevator or something respawned on top of a player? don't do it for match start, since //players will be respawning anyway. if (tdm_match_status < MM_PLAYING && (ent->solid == SOLID_BBOX || ent->solid == SOLID_BSP)) { edict_t *touch[MAX_EDICTS]; int count; int j; //r1: fix 00000196, can't killbox during map spawn due to risk of killing half-spawned //map entities. do a pseudo-killbox that only whacks players instead. count = gi.BoxEdicts (ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID); for (j = 0; j < count; j++) { if (touch[j] == ent) continue; //no point killing anything that won't clip (eg corpses) if (touch[j]->solid == SOLID_NOT || touch[j]->enttype == ENT_BODYQUE) continue; if (!touch[j]->client) continue; if (touch[j]->inuse) T_Damage (touch[j], ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } } } } //gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); }
/* ============== TDM_GetPlayerIdView ============== Find the best player for the id view and return a configstring index that contains their info. */ int TDM_GetPlayerIdView (edict_t *ent) { edict_t *ignore; edict_t *target; vec3_t forward; trace_t tr; vec3_t start; vec3_t mins = {-4,-4,-4}; vec3_t maxs = {4,4,4}; qboolean ignoreConfigStringUpdate; int i; int powerarmor; qboolean show_health_info; ignoreConfigStringUpdate = false; if (ent->client->chase_target) { target = ent->client->chase_target->client->resp.last_id_client; } else { int tracemask; VectorCopy (ent->s.origin, start); start[2] += ent->viewheight; AngleVectors (ent->client->v_angle, forward, NULL, NULL); VectorScale (forward, 4096, forward); VectorAdd (ent->s.origin, forward, forward); ignore = ent; target = NULL; tracemask = CONTENTS_SOLID|CONTENTS_MONSTER|MASK_WATER; //find best player through tracing for (i = 0; i < 10; i++) { tr = gi.trace (start, mins, maxs, forward, ignore, tracemask); //hit transparent water if (tr.ent == world && tr.surface && (tr.surface->flags & (SURF_TRANS33|SURF_TRANS66))) { tracemask &= ~MASK_WATER; VectorCopy (tr.endpos, start); continue; } if (tr.ent == world || tr.fraction == 1.0f) break; //we hit something that's a player and it's alive and on our team! //note, we trace twice so we hit water planes if (tr.ent && tr.ent->client && tr.ent->health > 0 && visible (tr.ent, ent, CONTENTS_SOLID | MASK_WATER)) { target = tr.ent; break; } else { VectorCopy (tr.endpos, start); ignore = tr.ent; } } //if trace was unsuccessful, try guessing based on angles if (!target) { edict_t *who, *best; vec3_t dir; float distance, bdistance = 0.0f; float bd = 0.0f, d; AngleVectors (ent->client->v_angle, forward, NULL, NULL); best = NULL; for (who = g_edicts + 1; who <= g_edicts + game.maxclients; who++) { if (!who->inuse) continue; if (who->health <= 0) continue; if (who == ent) continue; VectorSubtract (who->s.origin, ent->s.origin, dir); distance = VectorLength (dir); VectorNormalize (dir); d = DotProduct (forward, dir); //note, we trace twice so we hit water planes if (d > bd && visible (ent, who, CONTENTS_SOLID | MASK_WATER) && visible (who, ent, CONTENTS_SOLID | MASK_WATER)) { bdistance = distance; bd = d; best = who; } } if (best) { if (!best->client->pers.team) TDM_Error ("TDM_GetPlayerIdView: Got a spectator via DotProduct with %d health (%s)", best->health, best->client->pers.netname); //allow variable slop based on proximity if ((bdistance < 150 && bd > 0.50f) || (bdistance < 250 && bd > 0.90f) || (bdistance < 600 && bd > 0.96f) || bd > 0.98f) target = best; } } else if (!target->client->pers.team) TDM_Error ("TDM_GetPlayerIdView: Got a spectator via trace with %d health (%s)", target->health, target->client->pers.netname); } if (!target) return 0; show_health_info = false; if (ent->client->pers.team == target->client->pers.team) { //same team, show health info show_health_info = true; } else if (ent->client->pers.team == TEAM_SPEC) { /*if (!ent->client->chase_target) { //free floating camera, DON'T show health info show_health_info = false; } else */ if (ent->client->chase_target && ent->client->chase_target->client->pers.team == target->client->pers.team) { //viewing someone on the same team as our chase target, show health info show_health_info = true; } } //check for power armor if (target->client->inventory[ITEM_ITEM_POWER_SCREEN] > 0 || target->client->inventory[ITEM_ITEM_POWER_SHIELD] > 0) powerarmor = target->client->inventory[ITEM_AMMO_CELLS]; else powerarmor = -1; //don't spam configstring if they haven't changed since last time if (ent->client->resp.last_id_client == target && ent->client->resp.last_id_health == target->health && ent->client->resp.last_id_armor == TDM_GetArmorValue (target) && ent->client->resp.last_id_powerarmor == powerarmor) ignoreConfigStringUpdate = true; ent->client->resp.last_id_client = target; ent->client->resp.last_id_health = target->health; ent->client->resp.last_id_armor = TDM_GetArmorValue (target); ent->client->resp.last_id_powerarmor = powerarmor; if (!ignoreConfigStringUpdate) { char *string; if (show_health_info) { char buff[16]; //show power armor if they have it if (powerarmor != -1) sprintf (buff, " P:%d", powerarmor); else buff[0] = '\0'; if (ent->client->pers.config.id_highlight) string = TDM_SetColorText (va ("%16s H:%d A:%d%s", target->client->pers.netname, target->health, TDM_GetArmorValue (target), buff)); else string = va ("%16s H:%d A:%d%s", target->client->pers.netname, target->health, TDM_GetArmorValue (target), buff); } else { if (ent->client->pers.config.id_highlight) string = TDM_SetColorText (va ("%16s", target->client->pers.netname)); else string = va ("%16s", target->client->pers.netname); } gi.WriteByte (SVC_CONFIGSTRING); gi.WriteShort (CS_TDM_ID_VIEW); gi.WriteString (string); gi.unicast (ent, false); } return target - g_edicts; }
/* =============== CL_FinishHTTPDownload A download finished, find out what it was, whether there were any errors and if so, how severe. If none, rename file and other such stuff. =============== */ static void HTTP_FinishDownload (void) { int msgs_in_queue; CURLMsg *msg; CURLcode result; dlhandle_t *dl; CURL *curl; long responseCode; double timeTaken; double fileSize; unsigned i; do { msg = curl_multi_info_read (multi, &msgs_in_queue); if (!msg) { gi.dprintf ("HTTP_FinishDownload: Odd, no message for us...\n"); return; } if (msg->msg != CURLMSG_DONE) { gi.dprintf ("HTTP_FinishDownload: Got some weird message...\n"); continue; } curl = msg->easy_handle; for (i = 0; i < MAX_DOWNLOADS; i++) { if (downloads[i].curl == curl) break; } if (i == MAX_DOWNLOADS) TDM_Error ("HTTP_FinishDownload: Handle not found!"); dl = &downloads[i]; result = msg->data.result; switch (result) { //for some reason curl returns CURLE_OK for a 404... case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { TDM_HandleDownload (dl->tdm_handle, NULL, 0, responseCode); gi.dprintf ("HTTP: %s: 404 File Not Found\n", dl->URL); curl_multi_remove_handle (multi, dl->curl); dl->inuse = false; continue; } else if (responseCode == 200) { TDM_HandleDownload (dl->tdm_handle, dl->tempBuffer, dl->position, responseCode); gi.TagFree (dl->tempBuffer); } else { TDM_HandleDownload (dl->tdm_handle, NULL, 0, responseCode); if (dl->tempBuffer) gi.TagFree (dl->tempBuffer); } break; //fatal error default: TDM_HandleDownload (dl->tdm_handle, NULL, 0, 0); gi.dprintf ("HTTP Error: %s: %s\n", dl->URL, curl_easy_strerror (result)); curl_multi_remove_handle (multi, dl->curl); dl->inuse = false; continue; } //show some stats curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &timeTaken); curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize); //FIXME: //technically i shouldn't need to do this as curl will auto reuse the //existing handle when you change the URL. however, the handleCount goes //all weird when reusing a download slot in this way. if you can figure //out why, please let me know. curl_multi_remove_handle (multi, dl->curl); dl->inuse = false; gi.dprintf ("HTTP: Finished %s: %.f bytes, %.2fkB/sec\n", dl->URL, fileSize, (fileSize / 1024.0) / timeTaken); } while (msgs_in_queue > 0); }