/** * @brief Reads from a buffer according to format; version without syntactic sugar for variable arguments, to call it from other functions with variable arguments * @sa SV_ReadFormat * @param[in] buf The buffer we read the data from * @param[in] format The format string may not be nullptr * @param ap The variadic function argument list corresponding to the format string */ void NET_vReadFormat (dbuffer* buf, const char* format, va_list ap) { while (*format) { const char typeID = *format++; switch (typeID) { case 'c': *va_arg(ap, int*) = NET_ReadChar(buf); break; case 'b': *va_arg(ap, int*) = NET_ReadByte(buf); break; case 's': *va_arg(ap, int*) = NET_ReadShort(buf); break; case 'l': *va_arg(ap, int*) = NET_ReadLong(buf); break; case 'p': NET_ReadPos(buf, *va_arg(ap, vec3_t*)); break; case 'g': NET_ReadGPos(buf, *va_arg(ap, pos3_t*)); break; case 'd': NET_ReadDir(buf, *va_arg(ap, vec3_t*)); break; case 'a': *va_arg(ap, float*) = NET_ReadAngle(buf); break; case '!': format++; break; case '&': { char* str = va_arg(ap, char*); const size_t length = va_arg(ap, size_t); NET_ReadString(buf, str, length); break; } case '*': { const int n = NET_ReadShort(buf); *va_arg(ap, int*) = n; byte* p = va_arg(ap, byte*); for (int i = 0; i < n; i++) *p++ = NET_ReadByte(buf); } break; default: Com_Error(ERR_DROP, "ReadFormat: Unknown type!"); } } /* Too many arguments for the given format; too few cause crash above */ #ifdef PARANOID if (!ap) Com_Error(ERR_DROP, "ReadFormat: Too many arguments!"); #endif }
/** * @brief Announces that a player ends his turn * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The message buffer to read from * @sa CL_DoEndRound * @note event EV_ENDROUNDANNOUNCE * @todo Build into hud */ void CL_EndRoundAnnounce (const eventRegister_t* self, dbuffer* msg) { /* get the needed values */ const int playerNum = NET_ReadByte(msg); const int team = NET_ReadByte(msg); GAME_EndRoundAnnounce(playerNum, team); }
void NET_SkipFormat (dbuffer* buf, const char* format) { while (*format) { const char typeID = *format++; switch (typeID) { case 'c': NET_ReadChar(buf); break; case 'b': NET_ReadByte(buf); break; case 's': NET_ReadShort(buf); break; case 'l': NET_ReadLong(buf); break; case 'p': { vec3_t v; NET_ReadPos(buf, v); break; } case 'g': { pos3_t p; NET_ReadGPos(buf, p); break; } case 'd': { vec3_t v; NET_ReadDir(buf, v); break; } case 'a': NET_ReadAngle(buf); break; case '!': format++; break; case '&': NET_ReadString(buf, nullptr, 0); break; case '*': { const int n = NET_ReadShort(buf); for (int i = 0; i < n; i++) NET_ReadByte(buf); break; } default: Com_Error(ERR_DROP, "ReadFormat: Unknown type!"); } } }
void NET_ReadDir (dbuffer* buf, vec3_t dir) { const int b = NET_ReadByte(buf); if (b >= lengthof(bytedirs)) Com_Error(ERR_DROP, "NET_ReadDir: out of range"); VectorCopy(bytedirs[b], dir); }
/** * @brief Activates the map render screen (ca_active) * @sa SCR_EndLoadingPlaque * @sa G_ClientBegin * @note EV_START */ void CL_StartGame (const eventRegister_t* self, dbuffer* msg) { const int isTeamPlay = NET_ReadByte(msg); /* init camera position and angles */ OBJZERO(cl.cam); VectorSet(cl.cam.angles, 60.0, 60.0, 0.0); VectorSet(cl.cam.omega, 0.0, 0.0, 0.0); cl.cam.zoom = 1.25; CL_ViewCalcFieldOfViewX(); Com_Printf("Starting the game...\n"); /* make sure selActor is null (after reconnect or server change this is needed) */ CL_ActorSelect(nullptr); /* center on first actor */ cl_worldlevel->modified = true; if (cl.numTeamList) { const le_t* le = cl.teamList[0]; CL_ViewCenterAtGridPosition(le->pos); } /* activate the renderer */ CL_SetClientState(ca_active); GAME_StartBattlescape(isTeamPlay); }
void NET_ReadData (dbuffer* buf, void* data, int len) { int i; for (i = 0; i < len; i++) ((byte*) data)[i] = NET_ReadByte(buf); }
/** * @brief Moves actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa LET_PathMove * @note EV_ACTOR_MOVE */ void CL_ActorDoMove (const eventRegister_t* self, dbuffer* msg) { const int number = NET_ReadShort(msg); /* get le */ le_t* le = LE_Get(number); if (!le) LE_NotFoundError(number); if (!LE_IsActor(le)) Com_Error(ERR_DROP, "Can't move, LE doesn't exist or is not an actor (entnum: %i, type: %i)\n", number, le->type); if (LE_IsDead(le)) Com_Error(ERR_DROP, "Can't move, actor on team %i dead (entnum: %i)", le->team, number); /* lock this le for other events, the corresponding unlock is in LE_DoEndPathMove() */ LE_Lock(le); if (le->isMoving()) { if (le->pathLength == le->pathPos) { LE_DoEndPathMove(le); } else { Com_Error(ERR_DROP, "Actor (entnum: %i) on team %i is still moving (%i steps left). Times: %i, %i, %i", le->entnum, le->team, le->pathLength - le->pathPos, le->startTime, le->endTime, cl.time); } } int i = 0; /* the end of this event is marked with a 0 */ while (NET_PeekLong(msg) != 0) { NET_ReadByte(msg); le->dvtab[i] = NET_ReadShort(msg); /** Don't adjust dv values here- the whole thing is needed to move the actor! */ le->speed[i] = NET_ReadShort(msg); le->pathContents[i] = NET_ReadShort(msg); i++; } le->pathLength = i; if (le->pathLength > MAX_ROUTE) Com_Error(ERR_DROP, "Overflow in pathLength (entnum: %i)", number); /* skip the end of move marker */ NET_ReadLong(msg); /* Also get the final position */ NET_ReadGPos(msg, le->newPos); if (VectorCompare(le->newPos, le->pos)) Com_Error(ERR_DROP, "start and end pos are the same (entnum: %i)", number); /* activate PathMove function */ le->resetFloor(); if (LE_IsInvisible(le)) /* Hack: this relies on the visibility events interrupting the EV_ACTOR_MOVE event */ LE_SetThink(le, LET_HiddenMove); else LE_SetThink(le, LET_StartPathMove); le->pathPos = 0; le->startTime = cl.time; le->endTime = cl.time; }
/** * @brief Decides if following events should be delayed. The delay is the amount of time the actor needs to walk * from the start to the end pos. */ int CL_ActorDoMoveTime (const eventRegister_t *self, dbuffer *msg, eventTiming_t *eventTiming) { int time = 0; const int eventTime = eventTiming->nextTime; const int number = NET_ReadShort(msg); /* get le */ const le_t *le = LE_Get(number); if (!le) LE_NotFoundError(number); pos3_t pos; VectorCopy(le->pos, pos); byte crouchingState = LE_IsCrouched(le) ? 1 : 0; /* the end of this event is marked with a 0 */ while (NET_PeekLong(msg) != 0) { const dvec_t dvec = NET_ReadShort(msg); const byte dir = getDVdir(dvec); pos3_t oldPos; VectorCopy(pos, oldPos); PosAddDV(pos, crouchingState, dvec); time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg)); NET_ReadShort(msg); } /* skip the end of move marker */ NET_ReadLong(msg); /* Also skip the final position */ NET_ReadByte(msg); NET_ReadByte(msg); NET_ReadByte(msg); assert(NET_PeekByte(msg) == EV_NULL); eventTiming->nextTime += time + 400; return eventTime; }
/** * @sa CL_ConnectionlessPacket * @sa CL_Frame * @sa CL_ParseServerMessage * @sa NET_ReadMsg * @sa SV_ReadPacket */ static void CL_ReadPackets (void) { dbuffer* msg; while ((msg = NET_ReadMsg(cls.netStream))) { const svc_ops_t cmd = NET_ReadByte(msg); if (cmd == svc_oob) CL_ConnectionlessPacket(msg); else CL_ParseServerMessage(cmd, msg); delete msg; } }
/** * @brief Reads mission result data from server * @sa EV_RESULTS * @sa G_MatchSendResults * @sa GAME_CP_Results_f */ void CL_ParseResults (const eventRegister_t *self, struct dbuffer *msg) { int winner; int i, j, num; int num_spawned[MAX_TEAMS]; int num_alive[MAX_TEAMS]; /* the first dimension contains the attacker team, the second the victim team */ int num_kills[MAX_TEAMS][MAX_TEAMS]; int num_stuns[MAX_TEAMS][MAX_TEAMS]; qboolean nextmap; OBJZERO(num_spawned); OBJZERO(num_alive); OBJZERO(num_kills); OBJZERO(num_stuns); /* get number of teams */ num = NET_ReadByte(msg); if (num > MAX_TEAMS) Com_Error(ERR_DROP, "Too many teams in result message"); Com_DPrintf(DEBUG_CLIENT, "Receiving results with %i teams.\n", num); /* get winning team */ winner = NET_ReadByte(msg); nextmap = NET_ReadByte(msg); if (cls.team > num) Com_Error(ERR_DROP, "Team number %d too high (only %d teams)", cls.team, num); /* get spawn and alive count */ for (i = 0; i < num; i++) { num_spawned[i] = NET_ReadByte(msg); num_alive[i] = NET_ReadByte(msg); } /* get kills */ for (i = 0; i < num; i++) for (j = 0; j < num; j++) num_kills[i][j] = NET_ReadByte(msg); /* get stuns */ for (i = 0; i < num; i++) for (j = 0; j < num; j++) num_stuns[i][j] = NET_ReadByte(msg); GAME_HandleResults(msg, winner, num_spawned, num_alive, num_kills, num_stuns, nextmap); }
/** * @sa NET_ReadString */ int NET_ReadStringLine (dbuffer* buf, char* string, size_t length) { unsigned int l = 0; do { int c = NET_ReadByte(buf); if (c == -1 || c == 0 || c == '\n') break; /* translate all format specs to avoid crash bugs */ if (c == '%') c = '.'; string[l] = c; l++; } while (l < length - 1); string[l] = 0; return l; }
/** * @sa CL_ReadPacket * @sa NET_ReadMsg * @sa SV_Start */ void SV_ReadPacket (struct net_stream *s) { client_t *cl = (client_t *)NET_StreamGetData(s); struct dbuffer *msg; while ((msg = NET_ReadMsg(s))) { const int cmd = NET_ReadByte(msg); if (cmd == clc_oob) SV_ConnectionlessPacket(s, msg); else if (cl) SV_ExecuteClientMessage(cl, cmd, msg); else NET_StreamFree(s); free_dbuffer(msg); } }
/** * @sa CL_ReadPacket * @sa NET_ReadMsg * @sa SV_Start */ void SV_ReadPacket (struct net_stream *s) { client_t *cl = static_cast<client_t *>(NET_StreamGetData(s)); dbuffer *msg; while ((msg = NET_ReadMsg(s))) { const int cmd = NET_ReadByte(msg); if (cmd == clc_oob) SV_ConnectionlessPacket(s, msg); else if (cl) SV_ExecuteClientMessage(cl, cmd, msg); else { NET_StreamFree(s); s = NULL; } delete msg; } }
/** * @note Don't use this function in a way like * <code> char* s = NET_ReadString(sb); * char* t = NET_ReadString(sb);</code> * The second reading uses the same data buffer for the string - so * s is no longer the first - but the second string * @sa NET_ReadStringLine * @param[in,out] buf The input buffer to read the string data from * @param[out] string The output buffer to read the string into * @param[in] length The size of the output buffer */ int NET_ReadString (dbuffer* buf, char* string, size_t length) { unsigned int l = 0; for (;;) { int c = NET_ReadByte(buf); if (c == -1 || c == 0) break; if (string && l < length - 1) { /* translate all format specs to avoid crash bugs */ if (c == '%') c = '.'; string[l] = c; } l++; } if (string) string[l] = '\0'; return l; }
/** * @brief Performs end-of-turn processing. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa CL_EndRoundAnnounce */ void CL_DoEndRound (const eventRegister_t* self, dbuffer* msg) { /* hud changes */ if (cls.isOurRound()) UI_ExecuteConfunc("endround"); refdef.rendererFlags &= ~RDF_IRGOGGLES; /* change active player */ Com_Printf("Team %i ended round\n", cl.actTeam); cl.actTeam = NET_ReadByte(msg); Com_Printf("Team %i's round started!\n", cl.actTeam); /* hud changes */ if (cls.isOurRound()) { /* check whether a particle has to go */ CL_ParticleCheckRounds(); UI_ExecuteConfunc("startround"); HUD_DisplayMessage(_("Your round started!")); S_StartLocalSample("misc/roundstart", SND_VOLUME_DEFAULT); CL_ActorConditionalMoveCalc(selActor); } }
/** * @brief Decides if following events should be delayed. The delay is the amount of time the actor needs to walk * from the start to the end pos. */ int CL_ActorDoMoveTime (const eventRegister_t* self, dbuffer* msg, eventTiming_t* eventTiming) { int time = 0; const int eventTime = eventTiming->nextTime; const int number = NET_ReadShort(msg); /* get le */ le_t* le = LE_Get(number); if (!le) LE_NotFoundError(number); pos3_t pos; VectorCopy(le->pos, pos); byte crouchingState = LE_IsCrouched(le) ? 1 : 0; leStep_t* newStep = Mem_AllocType(leStep_t); if (le->stepList == nullptr) { le->stepList = newStep; le->stepIndex = 0; } else { /* append to the list */ leStep_t* step = le->stepList; while (step) { if (step->next == nullptr) { step->next = newStep; le->stepIndex++; break; } step = step->next; } } /* the end of this event is marked with a 0 */ while (NET_PeekLong(msg) != 0) { newStep->steps = NET_ReadByte(msg); const dvec_t dvec = NET_ReadShort(msg); const byte dir = getDVdir(dvec); pos3_t oldPos; VectorCopy(pos, oldPos); PosAddDV(pos, crouchingState, dvec); const int stepTime = LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg)); newStep->stepTimes[newStep->steps] = stepTime; time += stepTime; NET_ReadShort(msg); } ++newStep->steps; if (newStep->steps > MAX_ROUTE) Com_Error(ERR_DROP, "route length overflow: %i", newStep->steps); /* skip the end of move marker */ NET_ReadLong(msg); /* Also skip the final position */ NET_ReadByte(msg); NET_ReadByte(msg); NET_ReadByte(msg); assert(NET_PeekByte(msg) == EV_NULL); eventTiming->nextTime += time + 400; newStep->lastMoveTime = eventTime; newStep->lastMoveDuration = time; return eventTime; }
/** * @brief Calculates the time the event should get executed. If two events return the same time, * they are going to be executed in the order the were parsed. * @param[in] eType The event type * @param[in,out] msg The message buffer that can be modified to get the event time * @param[in] dt Delta time in msec since the last event was parsed */ int CL_GetEventTime (const event_t eType, struct dbuffer *msg, const int dt) { const eventRegister_t *eventData = CL_GetEvent(eType); #ifdef OLDEVENTTIME /* the time the event should be executed. This value is used to sort the * event chain to determine which event must be executed at first. This * value also ensures, that the events are executed in the correct * order. E.g. @c impactTime is used to delay some events in case the * projectile needs some time to reach its target. */ int eventTime; if (eType == EV_RESET) { parsedDeath = qfalse; nextTime = 0; shootTime = 0; impactTime = 0; } else if (eType == EV_ACTOR_DIE) parsedDeath = qtrue; /* get event time */ if (nextTime < cl.time) nextTime = cl.time; if (impactTime < cl.time) impactTime = cl.time; if (eType == EV_ACTOR_DIE || eType == EV_MODEL_EXPLODE) eventTime = impactTime; else if (eType == EV_ACTOR_SHOOT || eType == EV_ACTOR_SHOOT_HIDDEN) eventTime = shootTime; else if (eType == EV_RESULTS) eventTime = nextTime + 1400; else eventTime = nextTime; if (eType == EV_ENT_APPEAR || eType == EV_INV_ADD || eType == EV_PARTICLE_APPEAR || eType == EV_PARTICLE_SPAWN) { if (parsedDeath) { /* drop items after death (caused by impact) */ eventTime = impactTime + 400; /* EV_INV_ADD messages are the last events sent after a death */ if (eType == EV_INV_ADD) parsedDeath = qfalse; } else if (impactTime > cl.time) { /* item thrown on the ground */ eventTime = impactTime + 75; } } /* calculate time interval before the next event */ switch (eType) { case EV_ACTOR_APPEAR: if (cl.actTeam != cls.team) nextTime += 600; break; case EV_INV_RELOAD: /* let the reload sound play */ nextTime += 600; break; case EV_ACTOR_START_SHOOT: nextTime += 300; shootTime = nextTime; break; case EV_ACTOR_SHOOT_HIDDEN: { int first; int objIdx; const objDef_t *obj; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fireDefIndex; NET_ReadFormat(msg, eventData->formatString, &first, &objIdx, &weapFdsIdx, &fireDefIndex); obj = INVSH_GetItemByIDX(objIdx); if (first) { nextTime += 500; impactTime = shootTime = nextTime; } else { const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fireDefIndex); /* impact right away - we don't see it at all * bouncing is not needed here, too (we still don't see it) */ impactTime = shootTime; nextTime = shootTime + 1400; if (fd->delayBetweenShots > 0.0) shootTime += 1000 / fd->delayBetweenShots; } parsedDeath = qfalse; } break; case EV_ACTOR_MOVE: { le_t *le; int number, i; int time = 0; int pathLength; byte crouchingState; pos3_t pos, oldPos; number = NET_ReadShort(msg); /* get le */ le = LE_Get(number); if (!le) LE_NotFoundError(number); pathLength = NET_ReadByte(msg); /* Also skip the final position */ NET_ReadByte(msg); NET_ReadByte(msg); NET_ReadByte(msg); VectorCopy(le->pos, pos); crouchingState = LE_IsCrouched(le) ? 1 : 0; for (i = 0; i < pathLength; i++) { const dvec_t dvec = NET_ReadShort(msg); const byte dir = getDVdir(dvec); VectorCopy(pos, oldPos); PosAddDV(pos, crouchingState, dvec); time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg)); NET_ReadShort(msg); } nextTime += time + 400; } break; case EV_ACTOR_SHOOT: { const fireDef_t *fd; int flags, dummy; int objIdx, surfaceFlags; objDef_t *obj; int weap_fds_idx, fd_idx; shoot_types_t shootType; vec3_t muzzle, impact; /* read data */ NET_ReadFormat(msg, eventData->formatString, &dummy, &dummy, &dummy, &objIdx, &weap_fds_idx, &fd_idx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &dummy); obj = INVSH_GetItemByIDX(objIdx); fd = FIRESH_GetFiredef(obj, weap_fds_idx, fd_idx); if (!(flags & SF_BOUNCED)) { /* shooting */ if (fd->speed > 0.0 && !CL_OutsideMap(impact, UNIT_SIZE * 10)) { impactTime = shootTime + 1000 * VectorDist(muzzle, impact) / fd->speed; } else { impactTime = shootTime; } if (cl.actTeam != cls.team) nextTime = impactTime + 1400; else nextTime = impactTime + 400; if (fd->delayBetweenShots > 0.0) shootTime += 1000 / fd->delayBetweenShots; } else { /* only a bounced shot */ eventTime = impactTime; if (fd->speed > 0.0) { impactTime += 1000 * VectorDist(muzzle, impact) / fd->speed; nextTime = impactTime; } } parsedDeath = qfalse; } break; case EV_ACTOR_THROW: nextTime += NET_ReadShort(msg); impactTime = shootTime = nextTime; parsedDeath = qfalse; break; default: break; } Com_DPrintf(DEBUG_EVENTSYS, "%s => eventTime: %i, nextTime: %i, impactTime: %i, shootTime: %i\n", eventData->name, eventTime, nextTime, impactTime, shootTime); return eventTime; #else if (!eventData->timeCallback) return cl.time; return eventData->timeCallback(eventData, msg, dt); #endif }
static int SV_ReadByte (void) { return NET_ReadByte(sv->messageBuffer); }
/** * @sa NET_WriteGPos * @sa NET_ReadByte * @note pos3_t are byte values */ void NET_ReadGPos (dbuffer* buf, pos3_t pos) { pos[0] = NET_ReadByte(buf); pos[1] = NET_ReadByte(buf); pos[2] = NET_ReadByte(buf); }