// // [Toke - CTF] SV_CTFEvent // Sends CTF events to player // void SV_CTFEvent (flag_t f, flag_score_t event, player_t &who) { if(event == SCORE_NONE) return; if(validplayer(who)) who.points += ctf_points[event]; for (size_t i = 0; i < players.size(); ++i) { if (!players[i].ingame()) continue; client_t *cl = &players[i].client; MSG_WriteMarker (&cl->reliablebuf, svc_ctfevent); MSG_WriteByte (&cl->reliablebuf, event); MSG_WriteByte (&cl->reliablebuf, f); if(validplayer(who)) { MSG_WriteByte (&cl->reliablebuf, who.id); MSG_WriteLong (&cl->reliablebuf, who.points); } else { MSG_WriteByte (&cl->reliablebuf, 0); MSG_WriteLong (&cl->reliablebuf, 0); } for(size_t j = 0; j < NUMFLAGS; j++) MSG_WriteLong (&cl->reliablebuf, TEAMpoints[j]); } }
// // [Toke - CTF] CL_CTFEvent // Deals with CTF specific network data // void CL_CTFEvent (void) { flag_score_t event = (flag_score_t)MSG_ReadByte(); if(event == SCORE_NONE) // CTF state refresh { CTF_Connect(); return; } flag_t flag = (flag_t)MSG_ReadByte(); player_t &player = idplayer(MSG_ReadByte()); int points = MSG_ReadLong(); if(validplayer(player)) player.points = points; for(size_t i = 0; i < NUMFLAGS; i++) TEAMpoints[i] = MSG_ReadLong (); switch(event) { default: case SCORE_NONE: case SCORE_REFRESH: case SCORE_KILL: case SCORE_BETRAYAL: case SCORE_CARRIERKILL: break; case SCORE_GRAB: case SCORE_FIRSTGRAB: case SCORE_MANUALRETURN: if(validplayer(player)) CTF_CarryFlag(player, flag); break; case SCORE_RETURN: case SCORE_CAPTURE: if(validplayer(player)) CTF_CheckFlags(player); else CTFdata[flag].flagger = 0; CTFdata[flag].state = flag_home; if(CTFdata[flag].actor) CTFdata[flag].actor->Destroy(); break; case SCORE_DROP: if(validplayer(player)) CTF_CheckFlags(player); else CTFdata[flag].flagger = 0; CTFdata[flag].state = flag_dropped; if(CTFdata[flag].actor) CTFdata[flag].actor->Destroy(); break; } }
// // [Toke - CTF] CTF_MoveFlag // Moves the flag that is linked to a player // void CTF_MoveFlags () { // denis - flag is now a boolean for(size_t i = 0; i < NUMFLAGS; i++) { if(CTFdata[i].flagger && CTFdata[i].actor) { player_t &player = idplayer(CTFdata[i].flagger); AActor *flag = CTFdata[i].actor; if (!validplayer(player) || !player.mo) { // [SL] 2012-12-13 - Remove a flag if it's being carried but // there's not a valid player carrying it (should not happen) CTFdata[i].flagger = 0; CTFdata[i].state = flag_home; if(CTFdata[i].actor) CTFdata[i].actor->Destroy(); continue; } unsigned an = player.mo->angle >> ANGLETOFINESHIFT; fixed_t x = (player.mo->x + FixedMul (-2*FRACUNIT, finecosine[an])); fixed_t y = (player.mo->y + FixedMul (-2*FRACUNIT, finesine[an])); CL_MoveThing(flag, x, y, player.mo->z); } }
// // P_PreservePlayer // void P_PreservePlayer(player_t &player) { if (!serverside || sv_gametype != GM_COOP || !validplayer(player) || !player.ingame()) return; if(!unnatural_level_progression) player.playerstate = PST_LIVE; // denis - carry weapons and keys over to next level G_DoReborn(player); // inform client { size_t i; client_t *cl = &player.client; MSG_WriteMarker (&cl->reliablebuf, svc_playerinfo); for(i = 0; i < NUMWEAPONS; i++) MSG_WriteByte (&cl->reliablebuf, player.weaponowned[i]); for(i = 0; i < NUMAMMO; i++) { MSG_WriteShort (&cl->reliablebuf, player.maxammo[i]); MSG_WriteShort (&cl->reliablebuf, player.ammo[i]); } MSG_WriteByte (&cl->reliablebuf, player.health); MSG_WriteByte (&cl->reliablebuf, player.armorpoints); MSG_WriteByte (&cl->reliablebuf, player.armortype); MSG_WriteByte (&cl->reliablebuf, player.readyweapon); MSG_WriteByte (&cl->reliablebuf, player.backpack); } }
// // [Toke - CTF] CTF_CarryFlag // Spawns a flag on a players location and links the flag to the player // void CTF_CarryFlag (player_t &player, flag_t flag) { if (!validplayer(player)) return; player.flags[flag] = true; CTFdata[flag].flagger = player.id; CTFdata[flag].state = flag_carried; AActor *actor = new AActor(0, 0, 0, flag_table[flag][flag_carried]); CTFdata[flag].actor = actor->ptr(); CTF_MoveFlags(); }
// // [Toke - CTF] CTF_CarryFlag // Spawns a flag on a players location and links the flag to the player // void CTF_CarryFlag (player_t &player, flag_t flag) { if (!validplayer(player)) return; player.flags[flag] = true; CTFdata[flag].flagger = player.id; CTFdata[flag].state = flag_carried; // spawn visible flags on other players if(&player != &consoleplayer()) { AActor *actor = new AActor(0, 0, 0, flag_table[flag][flag_carried]); CTFdata[flag].actor = actor->ptr(); CTF_MoveFlags(); } }
bool tic() { if (!validplayer(idplayer(this->id))) { std::ostringstream buffer; buffer << this->netname << " left the server."; this->error = buffer.str(); return false; } if (idplayer(this->id).spectator) { std::ostringstream buffer; buffer << this->netname << " became a spectator on his own."; this->error = buffer.str(); return false; } return true; }
// // CTF_Connect // Receive states of all flags // void CTF_Connect() { size_t i; // clear player flags client may have imagined for(i = 0; i < players.size(); i++) for(size_t j = 0; j < NUMFLAGS; j++) players[i].flags[j] = false; for(i = 0; i < NUMFLAGS; i++) { CTFdata[i].state = (flag_state_t)MSG_ReadByte(); byte flagger = MSG_ReadByte(); if(CTFdata[i].state == flag_carried) { player_t &player = idplayer(flagger); if(validplayer(player)) CTF_CarryFlag(player, (flag_t)i); } } }
// // [Toke - CTF] CL_CTFEvent // Deals with CTF specific network data // void CL_CTFEvent (void) { flag_score_t event = (flag_score_t)MSG_ReadByte(); if(event == SCORE_NONE) // CTF state refresh { CTF_Connect(); return; } flag_t flag = (flag_t)MSG_ReadByte(); player_t &player = idplayer(MSG_ReadByte()); int points = MSG_ReadLong(); if(validplayer(player)) player.points = points; for(size_t i = 0; i < NUMFLAGS; i++) TEAMpoints[i] = MSG_ReadLong (); switch(event) { default: case SCORE_NONE: case SCORE_REFRESH: case SCORE_KILL: case SCORE_BETRAYAL: case SCORE_CARRIERKILL: break; case SCORE_GRAB: case SCORE_FIRSTGRAB: case SCORE_MANUALRETURN: if(validplayer(player)) { CTF_CarryFlag(player, flag); if (player.id == displayplayer().id) player.bonuscount = BONUSADD; } break; case SCORE_CAPTURE: if (validplayer(player)) { player.flags[flag] = 0; } CTFdata[flag].flagger = 0; CTFdata[flag].state = flag_home; if(CTFdata[flag].actor) CTFdata[flag].actor->Destroy(); break; case SCORE_RETURN: if (validplayer(player)) { player.flags[flag] = 0; } CTFdata[flag].flagger = 0; CTFdata[flag].state = flag_home; if(CTFdata[flag].actor) CTFdata[flag].actor->Destroy(); break; case SCORE_DROP: if (validplayer(player)) { player.flags[flag] = 0; } CTFdata[flag].flagger = 0; CTFdata[flag].state = flag_dropped; if(CTFdata[flag].actor) CTFdata[flag].actor->Destroy(); break; } // [AM] Play CTF sound, moved from server. CTF_Sound(flag, event); }
// Distribute X number of players between teams. bool Pickup_DistributePlayers(size_t num_players, std::string &error) { // This function shouldn't do anything unless you're in a teamgame. if (!(sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF)) { error = "Server is not in a team game."; return false; } // We can't distribute more than MAXPLAYERS so don't even try. if (num_players > MAXPLAYERS) { error = "Can't distribute that many players."; return false; } // Track all eligible players. std::vector<size_t> eligible; for (size_t i = 0; i < players.size(); i++) { if (validplayer(players[i]) && players[i].ingame() && (!players[i].spectator || (players[i].spectator && players[i].ready))) { eligible.push_back(i); } } if (eligible.empty()) { error = "No eligible players for distribution."; return false; } if (eligible.size() < num_players) { error = "Not enough eligible players for distribution."; return false; } // Jumble up our eligible players and cut the number of // eligible players to the passed number. std::random_shuffle(eligible.begin(), eligible.end()); eligible.resize(num_players); // Rip through our eligible vector, forcing players in the vector // onto alternating teams. team_t dest_team = TEAM_BLUE; for (size_t i = 0; i < eligible.size(); i++) { player_t &player = players[eligible[i]]; // Force-join the player if he's spectating. SV_SetPlayerSpec(player, false, true); // Is the last player an odd-one-out? Randomize // the team he is put on. if ((eligible.size() % 2) == 1 && i == (eligible.size() - 1)) { dest_team = (team_t)(P_Random() % NUMTEAMS); } // Switch player to the proper team, ensure the correct color, // and then update everyone else in the game about it. // // [SL] Kill the player if they are switching teams so they don't end up // holding their own team's flags if (player.mo && player.userinfo.team != dest_team) P_DamageMobj(player.mo, 0, 0, 1000, 0); SV_ForceSetTeam(player, dest_team); SV_CheckTeam(player); for (size_t j = 0; j< players.size(); j++) { SV_SendUserInfo(player, &clients[j]); } if (dest_team == TEAM_BLUE) { dest_team = TEAM_RED; } else { dest_team = TEAM_BLUE; } } // Force-spectate everyone who is not eligible. for (size_t i = 0; i < players.size(); i++) { if (std::find(eligible.begin(), eligible.end(), i) == eligible.end()) { SV_SetPlayerSpec(players[i], true, true); } } return true; }
void NetDemo::readSnapshotData(byte *buf, size_t length) { byte cid = consoleplayer_id; byte did = displayplayer_id; P_ClearAllNetIds(); // Remove all players players.clear(); // Remove all actors TThinkerIterator<AActor> iterator; AActor *mo; while ( (mo = iterator.Next() ) ) mo->Destroy(); gameaction = ga_nothing; FLZOMemFile memfile; length = 0; memfile.Open(buf); // open for reading FArchive arc(memfile); // Read the server cvars byte vars[4096], *vars_p; vars_p = vars; size_t len = arc.ReadCount (); arc.Read(vars, len); cvar_t::C_ReadCVars(&vars_p); std::string mapname; bool intermission; arc >> mapname; arc >> intermission; G_SerializeSnapshots(arc); P_SerializeRNGState(arc); P_SerializeACSDefereds(arc); // Read the status of flags in CTF for (int i = 0; i < NUMFLAGS; i++) arc >> CTFdata[i]; // Read team points for (int i = 0; i < NUMTEAMS; i++) arc >> TEAMpoints[i]; arc >> level.time; for (int i = 0; i < NUM_WORLDVARS; i++) arc >> ACS_WorldVars[i]; for (int i = 0; i < NUM_GLOBALVARS; i++) arc >> ACS_GlobalVars[i]; netgame = multiplayer = true; // load a base level savegamerestore = true; // Use the player actors in the savegame serverside = false; G_InitNew(mapname.c_str()); displayplayer_id = consoleplayer_id = 1; savegamerestore = false; // read consistancy marker byte check; arc >> check; arc.Close(); if (check != 0x1d) error("Bad snapshot"); consoleplayer_id = cid; // try to restore display player player_t *disp = &idplayer(did); if (validplayer(*disp) && disp->ingame() && !disp->spectator) displayplayer_id = did; else displayplayer_id = cid; // restore player colors for (size_t i = 0; i < players.size(); i++) R_BuildPlayerTranslation(players[i].id, players[i].userinfo.color); // Link the CTF flag actors to CTFdata[i].actor TThinkerIterator<AActor> flagiterator; while ( (mo = flagiterator.Next() ) ) { if (mo->type == MT_BDWN || mo->type == MT_BCAR) CTFdata[it_blueflag].actor = mo->ptr(); if (mo->type == MT_RDWN || mo->type == MT_RCAR) CTFdata[it_redflag].actor = mo->ptr(); } // Make sure the status bar is displayed correctly ST_Start(); }
// // CL_PredictWorld // // Main function for client-side prediction. // void CL_PredictWorld(void) { if (gamestate != GS_LEVEL) return; player_t *p = &consoleplayer(); if (!validplayer(*p) || !p->mo || noservermsgs || netdemo.isPaused()) return; // tenatively tell the netgraph that our prediction was successful netgraph.setMisprediction(false); if (consoleplayer_id != displayplayer_id) CL_PredictSpying(); // [SL] 2012-03-10 - Spectators can predict their position without server // correction. Handle them as a special case and leave. if (consoleplayer().spectator) { CL_PredictSpectator(); return; } if (p->tic <= 0) // No verified position from the server return; // Disable sounds, etc, during prediction predicting = true; // Figure out where to start predicting from int predtic = consoleplayer().tic > 0 ? consoleplayer().tic: 0; // Last position update from the server is too old! if (predtic < gametic - MAXSAVETICS) predtic = gametic - MAXSAVETICS; // Save a snapshot of the player's state before prediction PlayerSnapshot prevsnap(p->tic, p); cl_savedsnaps[gametic % MAXSAVETICS] = prevsnap; // Move sectors to the last position received from the server if (cl_predictsectors) CL_ResetSectors(); // Move the client to the last position received from the sever int snaptime = p->snapshots.getMostRecentTime(); PlayerSnapshot snap = p->snapshots.getSnapshot(snaptime); snap.toPlayer(p); if (cl_predictlocalplayer) { while (++predtic < gametic) { if (cl_predictsectors) CL_PredictSectors(predtic); CL_PredictLocalPlayer(predtic); } // If the player didn't just spawn or teleport, nudge the player from // his position last tic to this new corrected position. This smooths the // view when there's a misprediction. if (snap.isContinuous()) { PlayerSnapshot correctedprevsnap(p->tic, p); // Did we predict correctly? bool correct = (correctedprevsnap.getX() == prevsnap.getX()) && (correctedprevsnap.getY() == prevsnap.getY()) && (correctedprevsnap.getZ() == prevsnap.getZ()); if (!correct) { // Update the netgraph concerning our prediction's error netgraph.setMisprediction(true); // Lerp from the our previous position to the correct position PlayerSnapshot lerpedsnap = P_LerpPlayerPosition(prevsnap, correctedprevsnap, cl_prednudge); lerpedsnap.toPlayer(p); // [SL] 2012-04-26 - Snap directly to the corrected position in // the z direction. This prevents players from floating above // lifts when the lift height is mispredicted. p->mo->z = correctedprevsnap.getZ(); } } } predicting = false; // Run thinkers for current gametic if (cl_predictsectors) CL_PredictSectors(gametic); CL_PredictLocalPlayer(gametic); }
void AActor::Serialize (FArchive &arc) { Super::Serialize (arc); if (arc.IsStoring ()) { int playerid = player ? player->id : 0; arc << x << y << z << pitch << angle << roll << sprite << frame << effects << floorz << ceilingz << radius << height << momx << momy << momz << type << tics << state << flags << health << movedir << visdir << movecount /*<< target ? target->netid : 0*/ /*<< lastenemy ? lastenemy->netid : 0*/ << reactiontime << threshold << playerid << lastlook /*<< tracer ? tracer->netid : 0*/ << tid /*<< goal ? goal->netid : 0*/ << (unsigned)0 << translucency << waterlevel; if (translation) arc << (DWORD)(translation - translationtables); else arc << (DWORD)0xffffffff; spawnpoint.Serialize (arc); } else { unsigned dummy; unsigned playerid; arc >> x >> y >> z >> pitch >> angle >> roll >> sprite >> frame >> effects >> floorz >> ceilingz >> radius >> height >> momx >> momy >> momz >> type >> tics >> state >> flags >> health >> movedir >> visdir >> movecount /*>> target->netid*/ /*>> lastenemy->netid*/ >> reactiontime >> threshold >> playerid >> lastlook /*>> tracer->netid*/ >> tid /*>> goal->netid*/ >> dummy >> translucency >> waterlevel; DWORD trans; arc >> trans; if (trans == (DWORD)0xffffffff) translation = NULL; else translation = translationtables + trans; spawnpoint.Serialize (arc); info = &mobjinfo[type]; touching_sectorlist = NULL; LinkToWorld (); AddToHash (); if(playerid && validplayer(idplayer(playerid))) { player = &idplayer(playerid); player->mo = ptr(); player->camera = player->mo; } } }
// Return a "spread" of personal frags or team points that the // current player or team is ahead or behind by. std::string PersonalSpread(int& color) { color = CR_BRICK; player_t *plyr = &displayplayer(); if (sv_gametype == GM_DM) { // Seek the highest number of frags. byte ingame = 0; size_t maxother = 0; short maxfrags = -32768; for (size_t i = 0;i < players.size();i++) { if (!validplayer(players[i])) { continue; } if (!players[i].ingame() || players[i].spectator) { continue; } if (players[i].fragcount == maxfrags) { maxother++; } if (players[i].fragcount > maxfrags) { maxfrags = players[i].fragcount; maxother = 0; } ingame += 1; } // A spread needs two players to make sense. if (ingame <= 1) { return ""; } // Return the correct spread. if (maxfrags == plyr->fragcount && maxother > 0) { // We have the maximum number of frags but we share the // throne with someone else. But at least we can take // a little shortcut here. color = CR_GREEN; return "+0"; } std::ostringstream buffer; if (maxfrags == plyr->fragcount) { // We have the maximum number of frags. Calculate how // far above the other players we are. short nextfrags = -32768; for (size_t i = 0;i < players.size();i++) { if (!validplayer(players[i])) { continue; } if (!players[i].ingame() || players[i].spectator) { continue; } if (players[i].id == plyr->id) { continue; } if (players[i].fragcount > nextfrags) { nextfrags = players[i].fragcount; } } color = CR_GREEN; buffer << "+" << maxfrags - nextfrags; return buffer.str(); } // We are behind the leader. buffer << (plyr->fragcount - maxfrags); return buffer.str(); } else if (sv_gametype == GM_TEAMDM || sv_gametype == GM_CTF) { // Team spreads are significantly easier. Just compare two numbers. // FIXME: Not if we have more than two teams! std::ostringstream buffer; switch (plyr->userinfo.team) { case TEAM_BLUE: if (TEAMpoints[TEAM_BLUE] >= TEAMpoints[TEAM_RED]) { color = CR_GREEN; buffer << "+"; } buffer << (TEAMpoints[TEAM_BLUE] - TEAMpoints[TEAM_RED]); break; case TEAM_RED: if (TEAMpoints[TEAM_RED] >= TEAMpoints[TEAM_BLUE]) { color = CR_GREEN; buffer << "+"; } buffer << (TEAMpoints[TEAM_RED] - TEAMpoints[TEAM_BLUE]); break; default: // No valid team? Something is wrong... return ""; } return buffer.str(); } // We're not in an appropriate gamemode. return ""; }