X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Frace.qc;h=bb7f0eccc2297ad547b06181fa225d3957ad318d;hb=165d542813201a21cb6152ea5d22454e43e117c7;hp=e6d7a43accca9535df7edb251036e6669c9e2da3;hpb=0f0e69c6625fc4db17559e2e47f2fdaed8298076;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index e6d7a43ac..bb7f0eccc 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -1,4 +1,77 @@ -#define MAX_CHECKPOINTS 255 +#include "race.qh" + +void W_Porto_Fail(float failhard); + +float race_readTime(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos)))); +} + +string race_readUID(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))); +} + +float race_readPos(string map, float t) +{ + float i; + for (i = 1; i <= RANKINGS_CNT; ++i) + if (race_readTime(map, i) == 0 || race_readTime(map, i) > t) + return i; + + return 0; // pos is zero if unranked +} + +void race_writeTime(string map, float t, string myuid) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + float newpos; + newpos = race_readPos(map, t); + + float i, prevpos = 0; + for(i = 1; i <= RANKINGS_CNT; ++i) + { + if(race_readUID(map, i) == myuid) + prevpos = i; + } + if (prevpos) + { + // player improved his existing record, only have to iterate on ranks between new and old recs + for (i = prevpos; i > newpos; --i) + { + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); + } + } + else + { + // player has no ranked record yet + for (i = RANKINGS_CNT; i > newpos; --i) + { + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1))); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1)); + } + } + + // store new time itself + db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t)); + db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid); +} + +string race_readName(string map, float pos) +{ + string rr = (g_cts) ? CTS_RECORD : RACE_RECORD; + + return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)))); +} + + +const float MAX_CHECKPOINTS = 255; void spawnfunc_target_checkpoint(); @@ -72,6 +145,9 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only if(recordholder == e.netname) recordholder = ""; + if(!IS_REAL_CLIENT(e)) + return; + if(!spec) msg_entity = e; WRITESPECTATABLE_MSG_ONE({ @@ -91,13 +167,6 @@ void race_SendNextCheckpoint(entity e, float spec) // qualifying only }); } -void race_InitSpectator() -{ - if(g_race_qualifying) - if(msg_entity.enemy.race_laptime) - race_SendNextCheckpoint(msg_entity.enemy, 1); -} - void race_send_recordtime(float msg) { // send the server best time @@ -121,6 +190,9 @@ void race_SendRankings(float pos, float prevpos, float del, float msg) void race_SendStatus(float id, entity e) { + if(!IS_REAL_CLIENT(e)) + return; + float msg; if (id == 0) msg = MSG_ONE; @@ -136,7 +208,9 @@ void race_SendStatus(float id, entity e) }); } -void race_setTime(string map, float t, string myuid, string mynetname, entity e) { // netname only used TEMPORARILY for printing +void race_setTime(string map, float t, string myuid, string mynetname, entity e) +{ + // netname only used TEMPORARILY for printing float newpos, player_prevpos; newpos = race_readPos(map, t); @@ -156,7 +230,10 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) race_SendStatus(0, e); // "fail" Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_RACE_FAIL_RANKED, mynetname, player_prevpos, t, oldrec); return; - } else if (!newpos) { // no ranking, time worse than the worst ranked + } + else if (!newpos) + { + // no ranking, time worse than the worst ranked oldrec = race_readTime(GetMapname(), RANKINGS_CNT); race_SendStatus(0, e); // "fail" Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_RACE_FAIL_UNRANKED, mynetname, RANKINGS_CNT, t, oldrec); @@ -178,7 +255,8 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) // store new ranking race_writeTime(GetMapname(), t, myuid); - if (newpos == 1) { + if (newpos == 1) + { write_recordmarker(e, time - TIME_DECODE(t), TIME_DECODE(t)); race_send_recordtime(MSG_ALL); } @@ -208,7 +286,8 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e) } } -void race_deleteTime(string map, float pos) { +void race_deleteTime(string map, float pos) +{ string rr; if(g_cts) rr = CTS_RECORD; @@ -216,12 +295,15 @@ void race_deleteTime(string map, float pos) { rr = RACE_RECORD; float i; - for (i = pos; i <= RANKINGS_CNT; ++i) { - if (i == RANKINGS_CNT) { + for (i = pos; i <= RANKINGS_CNT; ++i) + { + if (i == RANKINGS_CNT) + { db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), string_null); db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), string_null); } - else { + else + { db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(GetMapname(), i+1))); db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(GetMapname(), i+1)); } @@ -294,7 +376,8 @@ void race_SendTime(entity e, float cp, float t, float tvalid) if(recordholder == e.netname) recordholder = ""; - if(t != 0) { + if(t != 0) + { if(cp == race_timed_checkpoint) { race_setTime(GetMapname(), t, e.crypto_idfp, e.netname, e); @@ -326,18 +409,21 @@ void race_SendTime(entity e, float cp, float t, float tvalid) recordholder = ""; } - msg_entity = e; - if(g_race_qualifying) + if(IS_REAL_CLIENT(e)) { - WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING); - WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at - WriteInt24_t(MSG_ONE, t); // time to that intermediate - WriteInt24_t(MSG_ONE, recordtime); // previously best time - WriteString(MSG_ONE, recordholder); // record holder - }); + msg_entity = e; + if(g_race_qualifying) + { + WRITESPECTATABLE_MSG_ONE_VARNAME(dummy1, { + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_QUALIFYING); + WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at + WriteInt24_t(MSG_ONE, t); // time to that intermediate + WriteInt24_t(MSG_ONE, recordtime); // previously best time + WriteString(MSG_ONE, recordholder); // record holder + }); + } } } else // RACE! Not Qualifying @@ -354,49 +440,55 @@ void race_SendTime(entity e, float cp, float t, float tvalid) else lself = lother = othtime = 0; - msg_entity = e; - WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE); - WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at - if(e == oth) - { - WriteInt24_t(MSG_ONE, 0); - WriteByte(MSG_ONE, 0); - WriteString(MSG_ONE, ""); - } - else - { - WriteInt24_t(MSG_ONE, TIME_ENCODE(time - race_checkpoint_lasttimes[cp])); - WriteByte(MSG_ONE, lself - lother); - WriteString(MSG_ONE, oth.netname); // record holder - } - }); + if(IS_REAL_CLIENT(e)) + { + msg_entity = e; + WRITESPECTATABLE_MSG_ONE_VARNAME(dummy2, { + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE); + WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at + if(e == oth) + { + WriteInt24_t(MSG_ONE, 0); + WriteByte(MSG_ONE, 0); + WriteString(MSG_ONE, ""); + } + else + { + WriteInt24_t(MSG_ONE, TIME_ENCODE(time - race_checkpoint_lasttimes[cp])); + WriteByte(MSG_ONE, lself - lother); + WriteString(MSG_ONE, oth.netname); // record holder + } + }); + } race_checkpoint_lastplayers[cp] = e; race_checkpoint_lasttimes[cp] = time; race_checkpoint_lastlaps[cp] = lself; - msg_entity = oth; - WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT); - WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at - if(e == oth) - { - WriteInt24_t(MSG_ONE, 0); - WriteByte(MSG_ONE, 0); - WriteString(MSG_ONE, ""); - } - else - { - WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime)); - WriteByte(MSG_ONE, lother - lself); - WriteString(MSG_ONE, e.netname); // record holder - } - }); + if(IS_REAL_CLIENT(oth)) + { + msg_entity = oth; + WRITESPECTATABLE_MSG_ONE_VARNAME(dummy3, { + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT); + WriteByte(MSG_ONE, race_CheckpointNetworkID(cp)); // checkpoint the player now is at + if(e == oth) + { + WriteInt24_t(MSG_ONE, 0); + WriteByte(MSG_ONE, 0); + WriteString(MSG_ONE, ""); + } + else + { + WriteInt24_t(MSG_ONE, TIME_ENCODE(time - othtime)); + WriteByte(MSG_ONE, lother - lself); + WriteString(MSG_ONE, e.netname); // record holder + } + }); + } } } @@ -408,6 +500,9 @@ void race_ClearTime(entity e) e.race_penalty_accumulator = 0; e.race_lastpenalty = world; + if(!IS_REAL_CLIENT(e)) + return; + msg_entity = e; WRITESPECTATABLE_MSG_ONE({ WriteByte(MSG_ONE, SVC_TEMPENTITY); @@ -476,7 +571,8 @@ void checkpoint_passed() other.porto_forbidden = 2; // decreased by 1 each StartFrame - if(defrag_ents) { + if(defrag_ents) + { if(self.race_checkpoint == -2) { self.race_checkpoint = other.race_checkpoint; @@ -484,7 +580,8 @@ void checkpoint_passed() float largest_cp_id = 0; float cp_amount = 0; - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { cp_amount += 1; if(cp.race_checkpoint > largest_cp_id) // update the finish id if someone hit a new checkpoint { @@ -494,13 +591,15 @@ void checkpoint_passed() race_highest_checkpoint = largest_cp_id + 1; race_timed_checkpoint = largest_cp_id + 1; - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { if(cp.race_checkpoint == -2) // set defragcpexists to -1 so that the cp id file will be rewritten when someone finishes defragcpexists = -1; } } } - if(cp_amount == 0) { + if(cp_amount == 0) + { for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) cp.race_checkpoint = 1; race_highest_checkpoint = 1; @@ -594,11 +693,11 @@ void checkpoint_use() float race_waypointsprite_visible_for_player(entity e) { if(e.race_checkpoint == -1 || self.owner.race_checkpoint == -2) - return TRUE; + return true; else if(e.race_checkpoint == self.owner.race_checkpoint) - return TRUE; + return true; else - return FALSE; + return false; } float have_verified; @@ -627,7 +726,7 @@ void trigger_race_checkpoint_verify() // race only (middle of the race) g_race_qualifying = 0; self.race_place = 0; - if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE)) + if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, false)) error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for respawning in race) - bailing out")); if(i == 0) @@ -635,7 +734,7 @@ void trigger_race_checkpoint_verify() // qualifying only g_race_qualifying = 1; self.race_place = race_lowest_place_spawn; - if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE)) + if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, false)) error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out")); // race only (initial spawn) @@ -643,7 +742,7 @@ void trigger_race_checkpoint_verify() for(p = 1; p <= race_highest_place_spawn; ++p) { self.race_place = p; - if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE)) + if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, false)) error(strcat("Checkpoint ", ftos(i), " misses a spawnpoint with race_place==", ftos(self.race_place), " (used for initially spawning in race) - bailing out")); } } @@ -655,7 +754,7 @@ void trigger_race_checkpoint_verify() self.race_checkpoint = race_NextCheckpoint(0); g_race_qualifying = 1; self.race_place = race_lowest_place_spawn; - if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, FALSE)) + if(!Spawn_FilterOutBadSpots(findchain(classname, "info_player_deathmatch"), 0, false)) error(strcat("Checkpoint 0 misses a spawnpoint with race_place==", ftos(self.race_place), " (used for qualifying) - bailing out")); } else @@ -675,7 +774,8 @@ void trigger_race_checkpoint_verify() while((l = fgets(fh))) { len = tokenize_console(l); - if(len != 2) { + if(len != 2) + { defragcpexists = -1; // something's wrong in the defrag cp file, set defragcpexists to -1 so that it will be rewritten when someone finishes continue; } @@ -689,19 +789,23 @@ void trigger_race_checkpoint_verify() g_race_qualifying = qual; - if(race_timed_checkpoint) { - if(defrag_ents) { + if(race_timed_checkpoint) + { + if(defrag_ents) + { for(cp = world; (cp = find(cp, classname, "target_startTimer"));) WaypointSprite_UpdateSprites(cp.sprite, "race-start", "", ""); for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) WaypointSprite_UpdateSprites(cp.sprite, "race-finish", "", ""); - for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) { + for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) + { if(cp.race_checkpoint == -2) // something's wrong with the defrag cp file or it has not been written yet, set defragcpexists to -1 so that it will be rewritten when someone finishes defragcpexists = -1; } - if(defragcpexists != -1){ + if(defragcpexists != -1) + { float largest_cp_id = 0; for(cp = world; (cp = find(cp, classname, "target_checkpoint"));) if(cp.race_checkpoint > largest_cp_id) @@ -710,14 +814,17 @@ void trigger_race_checkpoint_verify() cp.race_checkpoint = largest_cp_id + 1; // finish line race_highest_checkpoint = largest_cp_id + 1; race_timed_checkpoint = largest_cp_id + 1; - } else { + } + else + { for(cp = world; (cp = find(cp, classname, "target_stopTimer"));) cp.race_checkpoint = 255; // finish line race_highest_checkpoint = 255; race_timed_checkpoint = 255; } } - else { + else + { for(cp = world; (cp = find(cp, classname, "trigger_race_checkpoint")); ) if(cp.sprite) { @@ -729,11 +836,13 @@ void trigger_race_checkpoint_verify() } } - if(defrag_ents) { + if(defrag_ents) + { entity trigger, targ; for(trigger = world; (trigger = find(trigger, classname, "trigger_multiple")); ) for(targ = world; (targ = find(targ, targetname, trigger.target)); ) - if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") { + if (targ.classname == "target_checkpoint" || targ.classname == "target_startTimer" || targ.classname == "target_stopTimer") + { trigger.wait = 0; trigger.delay = 0; targ.wait = 0; @@ -772,7 +881,7 @@ vector trigger_race_checkpoint_spawn_evalfunc(entity player, entity spot, vector return '-1 0 0'; // try reusing the previous spawn if(self == player.race_respawn_spotref || spot == player.race_respawn_spotref) - current_x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN; + current.x += SPAWN_PRIO_RACE_PREVIOUS_SPAWN; if(self.race_checkpoint == 0) { float pl; @@ -791,11 +900,7 @@ vector trigger_race_checkpoint_spawn_evalfunc(entity player, entity spot, vector void spawnfunc_trigger_race_checkpoint() { vector o; - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } EXACTTRIGGER_INIT; @@ -804,7 +909,7 @@ void spawnfunc_trigger_race_checkpoint() self.touch = checkpoint_touch; o = (self.absmin + self.absmax) * 0.5; - tracebox(o, PL_MIN, PL_MAX, o - '0 0 1' * (o_z - self.absmin_z), MOVE_NORMAL, self); + tracebox(o, PL_MIN, PL_MAX, o - '0 0 1' * (o.z - self.absmin.z), MOVE_NORMAL, self); waypoint_spawnforitem_force(self, trace_endpos); self.nearestwaypointtimeout = time + 1000000000; @@ -843,11 +948,7 @@ void spawnfunc_trigger_race_checkpoint() void spawnfunc_target_checkpoint() // defrag entity { vector o; - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } defrag_ents = 1; EXACTTRIGGER_INIT; @@ -857,7 +958,7 @@ void spawnfunc_target_checkpoint() // defrag entity self.touch = checkpoint_touch; o = (self.absmin + self.absmax) * 0.5; - tracebox(o, PL_MIN, PL_MAX, o - '0 0 1' * (o_z - self.absmin_z), MOVE_NORMAL, self); + tracebox(o, PL_MIN, PL_MAX, o - '0 0 1' * (o.z - self.absmin.z), MOVE_NORMAL, self); waypoint_spawnforitem_force(self, trace_endpos); self.nearestwaypointtimeout = time + 1000000000; @@ -926,57 +1027,9 @@ void race_RetractPlayer() self.race_checkpoint = self.race_respawn_checkpoint; } -void race_PreDie() -{ - if(!g_race && !g_cts) - return; - - race_AbandonRaceCheck(self); -} - -void race_PreSpawn() -{ - if(!g_race && !g_cts) - return; - if(self.killcount == -666 /* initial spawn */ || g_race_qualifying) // spawn - race_PreparePlayer(); - else // respawn - race_RetractPlayer(); - - race_AbandonRaceCheck(self); -} - -void race_PostSpawn(entity spot) -{ - if(!g_race && !g_cts) - return; - - if(spot.target == "") - // Emergency: this wasn't a real spawnpoint. Can this ever happen? - race_PreparePlayer(); - - // if we need to respawn, do it right - self.race_respawn_checkpoint = self.race_checkpoint; - self.race_respawn_spotref = spot; - - self.race_place = 0; -} - -void race_PreSpawnObserver() -{ - if(!g_race && !g_cts) - return; - race_PreparePlayer(); - self.race_checkpoint = -1; -} - void spawnfunc_info_player_race (void) { - if(!g_race && !g_cts) - { - remove(self); - return; - } + if(!g_race && !g_cts) { remove(self); return; } ++race_spawns; spawnfunc_info_player_deathmatch(); @@ -1010,63 +1063,37 @@ void race_ClearRecords() self = e; } -void race_ReadyRestart() -{ - float s; - - Score_NicePrint(world); - - race_ClearRecords(); - PlayerScore_Sort(race_place, 0, 1, 0); - - entity e; - FOR_EACH_CLIENT(e) - { - if(e.race_place) - { - s = PlayerScore_Add(e, SP_RACE_FASTEST, 0); - if(!s) - e.race_place = 0; - } - print(e.netname, " = ", ftos(e.race_place), "\n"); - } - - if(g_race_qualifying == 2) - { - g_race_qualifying = 0; - independent_players = 0; - cvar_set("fraglimit", ftos(race_fraglimit)); - cvar_set("leadlimit", ftos(race_leadlimit)); - cvar_set("timelimit", ftos(race_timelimit)); - ScoreRules_race(); - } -} - void race_ImposePenaltyTime(entity pl, float penalty, string reason) { if(g_race_qualifying) { pl.race_penalty_accumulator += penalty; - msg_entity = pl; - WRITESPECTATABLE_MSG_ONE({ - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING); - WriteShort(MSG_ONE, TIME_ENCODE(penalty)); - WriteString(MSG_ONE, reason); - }); + if(IS_REAL_CLIENT(pl)) + { + msg_entity = pl; + WRITESPECTATABLE_MSG_ONE({ + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_PENALTY_QUALIFYING); + WriteShort(MSG_ONE, TIME_ENCODE(penalty)); + WriteString(MSG_ONE, reason); + }); + } } else { pl.race_penalty = time + penalty; - msg_entity = pl; - WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, { - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_RACE); - WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE); - WriteShort(MSG_ONE, TIME_ENCODE(penalty)); - WriteString(MSG_ONE, reason); - }); + if(IS_REAL_CLIENT(pl)) + { + msg_entity = pl; + WRITESPECTATABLE_MSG_ONE_VARNAME(dummy, { + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_RACE); + WriteByte(MSG_ONE, RACE_NET_PENALTY_RACE); + WriteShort(MSG_ONE, TIME_ENCODE(penalty)); + WriteString(MSG_ONE, reason); + }); + } } } @@ -1155,7 +1182,7 @@ float race_GetFractionalLapCount(entity e) // race_timed_checkpoint == 0: then nextcp==0 means 0.9999x float c, nc; nc = race_highest_checkpoint + 1; - c = (mod(nextcpindex - race_timed_checkpoint + nc + nc - 1, nc) + 1) - bestfraction; + c = ((nextcpindex - race_timed_checkpoint + nc + nc - 1) % nc) + 1 - bestfraction; return l + c / nc; }