/** * @brief Called during startup of mission to send team info */ void GAME_SpawnSoldiers (void) { const cgame_export_t *list = GAME_GetCurrentType(); qboolean spawnStatus; /* this callback is responsible to set up the cl.chrList */ if (list && list->Spawn) spawnStatus = list->Spawn(); else spawnStatus = GAME_Spawn(); if (spawnStatus && cl.chrList.num > 0) { struct dbuffer *msg; /* send team info */ msg = new_dbuffer(); GAME_SendCurrentTeamSpawningInfo(msg, &cl.chrList); NET_WriteMsg(cls.netStream, msg); msg = new_dbuffer(); NET_WriteByte(msg, clc_stringcmd); NET_WriteString(msg, "spawn\n"); NET_WriteMsg(cls.netStream, msg); GAME_InitializeBattlescape(&cl.chrList); } }
static void testDBuffer (void) { int i; const int size = 10000000; dbuffer* buf = new_dbuffer(); char data[128]; size_t dataSize = sizeof(data); for (i = 0; i < size; i++) dbuffer_add(buf, "b", 1); CU_ASSERT_EQUAL(size, dbuffer_len(buf)); CU_ASSERT_EQUAL(dataSize, dbuffer_get(buf, data, dataSize)); CU_ASSERT_EQUAL(size, dbuffer_len(buf)); CU_ASSERT_EQUAL(dataSize, dbuffer_extract(buf, data, dataSize)); CU_ASSERT_EQUAL(size - dataSize, dbuffer_len(buf)); free_dbuffer(buf); buf = new_dbuffer(); dbuffer_add(buf, "b", 1); CU_ASSERT_EQUAL(1, dbuffer_len(buf)); CU_ASSERT_EQUAL(1, dbuffer_get(buf, data, dataSize)); CU_ASSERT_EQUAL(1, dbuffer_len(buf)); CU_ASSERT_EQUAL(1, dbuffer_extract(buf, data, dataSize)); CU_ASSERT_EQUAL(0, dbuffer_len(buf)); buf = dbuffer_dup(buf); CU_ASSERT_EQUAL(0, dbuffer_len(buf)); for (i = 0; i <= dataSize; i++) dbuffer_add(buf, "b", 1); CU_ASSERT_EQUAL(dataSize + 1, dbuffer_len(buf)); CU_ASSERT_EQUAL(dataSize, dbuffer_extract(buf, data, dataSize)); CU_ASSERT_EQUAL(1, dbuffer_len(buf)); CU_ASSERT_EQUAL(1, dbuffer_remove(buf, 1)); CU_ASSERT_EQUAL(0, dbuffer_len(buf)); CU_ASSERT_EQUAL(0, dbuffer_remove(buf, 1)); CU_ASSERT_EQUAL(0, dbuffer_get_at(buf, 1, data, dataSize)); for (i = 0; i <= dataSize; i++) dbuffer_add(buf, "b", 1); CU_ASSERT_EQUAL(dataSize + 1, dbuffer_len(buf)); CU_ASSERT_EQUAL(dataSize, dbuffer_get_at(buf, 1, data, dataSize)); CU_ASSERT_EQUAL(dataSize + 1, dbuffer_len(buf)); }
/** * @brief Search the index in the config strings relative to a given start * @param name The value of the config string to search the index for * @param start The relative start point for the search * @param max The max. searched entries in the config string before giving up * @param create if @c true the value will get written into the config strings (appended) * @return @c 0 if not found */ static unsigned int SV_FindIndex (const char *name, int start, int max, qboolean create) { int i; if (!name || !name[0]) return 0; for (i = 1; i < max && SV_GetConfigString(start + i)[0] != '\0'; i++) { const char *configString = SV_GetConfigString(start + i); if (Q_streq(configString, name)) return i; } if (!create) return 0; if (i == max) Com_Error(ERR_DROP, "*Index: overflow '%s' start: %i, max: %i", name, start, max); SV_SetConfigString(start + i, name); if (Com_ServerState() != ss_loading) { /* send the update to everyone */ struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_configstring); NET_WriteShort(msg, start + i); NET_WriteString(msg, name); SV_Multicast(~0, msg); } return i; }
/** * @brief Responds with teaminfo such as free team num * @sa CL_ParseTeamInfoMessage */ static void SVC_TeamInfo (struct net_stream *s) { client_t *cl; struct dbuffer *msg = new_dbuffer(); char infoGlobal[MAX_INFO_STRING] = ""; NET_WriteByte(msg, clc_oob); NET_WriteRawString(msg, "teaminfo\n"); Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_teamplay", Cvar_GetString("sv_teamplay")); Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxteams", Cvar_GetString("sv_maxteams")); Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxplayersperteam", Cvar_GetString("sv_maxplayersperteam")); NET_WriteString(msg, infoGlobal); cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { if (cl->state >= cs_connected) { char infoPlayer[MAX_INFO_STRING] = ""; /* show players that already have a team with their teamnum */ int teamId = svs.ge->ClientGetTeamNum(cl->player); if (!teamId) teamId = TEAM_NO_ACTIVE; Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_team", teamId); Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_ready", svs.ge->ClientIsReady(cl->player)); Info_SetValueForKey(infoPlayer, sizeof(infoPlayer), "cl_name", cl->name); NET_WriteString(msg, infoPlayer); } } NET_WriteByte(msg, 0); NET_WriteMsg(s, msg); }
/** * @brief Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quitting * or crashing. */ void SV_DropClient (client_t * drop, const char *message) { /* add the disconnect */ struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_disconnect); NET_WriteString(msg, message); NET_WriteMsg(drop->stream, msg); SV_BroadcastPrintf(PRINT_CHAT, "%s was dropped from the server - reason: %s\n", drop->name, message); if (drop->state == cs_spawned || drop->state == cs_spawning) { /* call the prog function for removing a client */ /* this will remove the body, among other things */ TH_MutexLock(svs.serverMutex); svs.ge->ClientDisconnect(drop->player); TH_MutexUnlock(svs.serverMutex); } NET_StreamFinished(drop->stream); drop->stream = NULL; drop->player->inuse = qfalse; SV_SetClientState(drop, cs_free); drop->name[0] = 0; if (svs.abandon) { int count = 0; client_t *cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state >= cs_connected) count++; if (count == 0) svs.killserver = qtrue; } }
/** * @brief Merges two dbuffers * @param[in] old the source buffer * @param[in] old2 the second source buffer * @return the newly allocated buffer * Allocates a new dbuffer and initialises it to contain a copy of the * data in old ones */ struct dbuffer *dbuffer_merge (struct dbuffer *old, struct dbuffer *old2) { /* element we're currently reading from */ const struct dbuffer_element *e; struct dbuffer *buf = new_dbuffer(); const char *p; e = old->head; p = old->start; while (e && (e->len > 0)) { dbuffer_add(buf, p, e->len); e = e->next; p = &e->data[0]; } e = old2->head; p = old2->start; while (e && (e->len > 0)) { dbuffer_add(buf, p, e->len); e = e->next; p = &e->data[0]; } return buf; }
static void testEvents (void) { int i; const event_t events[] = {EV_RESET, EV_START, EV_ENDROUND, EV_ENDROUNDANNOUNCE}; for (i = 0; i < lengthof(events); i++) { struct dbuffer* buf = new_dbuffer(); NET_WriteByte(buf, events[i]); CL_ParseEvent(buf); } CU_ASSERT_EQUAL(CL_ClearBattlescapeEvents(), lengthof(events)); }
static void testDBufferNetHandling (void) { dbuffer* buf = new_dbuffer(); NET_WriteByte(buf, 'b'); CU_ASSERT_EQUAL(1, dbuffer_len(buf)); NET_WriteShort(buf, 128); CU_ASSERT_EQUAL(3, dbuffer_len(buf)); NET_WriteLong(buf, 128); CU_ASSERT_EQUAL(7, dbuffer_len(buf)); free_dbuffer(buf); }
/** * @sa SV_BroadcastCommand */ void SV_ClientCommand (client_t *client, const char *fmt, ...) { va_list ap; char str[1024]; struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_stufftext); va_start(ap, fmt); NET_VPrintf(msg, fmt, ap, str, sizeof(str)); va_end(ap); NET_WriteMsg(client->stream, msg); }
static void testDBufferBigData (void) { int i; int count = 100; byte *data; /* this entity string may not contain any inline models, we don't have the bsp tree loaded here */ const int size = FS_LoadFile("game/entity.txt", &data); dbuffer* buf = new_dbuffer(); for (i = 0; i < count; i++) { dbuffer_add(buf, (char *)data, size); } CU_ASSERT_EQUAL(size * count, dbuffer_len(buf)); free_dbuffer(buf); FS_FreeFile(data); }
/** * @brief Finishes the current round of the player in battlescape and starts the round for the next team. */ static void CL_NextRound_f (void) { struct dbuffer *msg; /* can't end round if we are not in battlescape */ if (!CL_BattlescapeRunning()) return; /* can't end round if we're not active */ if (cls.team != cl.actTeam) { HUD_DisplayMessage(_("This isn't your round")); return; } /* send endround */ msg = new_dbuffer(); NET_WriteByte(msg, clc_endround); NET_WriteMsg(cls.netStream, msg); }
/** * @brief Allocate a dbuffer and prepend the given data to it * @param[in] old The source buffer * @param[in] data The data to insert at the beginning * @param[in] len The length of that data * @return the newly allocated buffer * Allocates a new dbuffer and initialises it to contain a copy of the * data in old */ struct dbuffer *dbuffer_prepend (struct dbuffer *old, const char *data, size_t len) { /* element we're currently reading from */ const struct dbuffer_element *e; struct dbuffer *buf = new_dbuffer(); const char *p; dbuffer_add(buf, data, len); e = old->head; p = old->start; while (e && (e->len > 0)) { dbuffer_add(buf, p, e->len); e = e->next; p = &e->data[0]; } return buf; }
/** * @sa gi.AddEvent * @param[in] mask The player bitmask to send the events to. Use @c PM_ALL to send to every connected player. */ static void SV_AddEvent (unsigned int mask, int eType) { pending_event_t *p = &sv->pendingEvent; Com_DPrintf(DEBUG_EVENTSYS, "Event type: %i (mask %i)\n", eType, mask); /* finish the last event */ if (p->pending) SV_EndEvents(); /* start the new event */ p->pending = qtrue; p->playerMask = mask; p->type = eType; p->buf = new_dbuffer(); /* write header */ NET_WriteByte(p->buf, svc_event); NET_WriteByte(p->buf, eType); }
static void SV_PingPlayers (void) { client_t *cl; /* check for time wraparound */ if (svs.lastPing > svs.realtime) svs.lastPing = svs.realtime; if (svs.realtime - svs.lastPing < PING_SECONDS * 1000) return; /* not time to send yet */ svs.lastPing = svs.realtime; cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state != cs_free) { struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_ping); NET_WriteMsg(cl->stream, msg); } }
/** * @brief Sends text to all active clients */ void SV_BroadcastPrintf (int level, const char *fmt, ...) { va_list argptr; struct dbuffer *msg; client_t *cl; char str[1024]; msg = new_dbuffer(); NET_WriteByte(msg, svc_print); NET_WriteByte(msg, level); va_start(argptr, fmt); NET_VPrintf(msg, fmt, argptr, str, sizeof(str)); va_end(argptr); /* echo to console */ if (sv_dedicated->integer) { char copy[1024]; int i; const int length = sizeof(copy) - 1; va_start(argptr, fmt); Q_vsnprintf(copy, sizeof(copy), fmt, argptr); va_end(argptr); /* mask off high bits */ for (i = 0; i < length && copy[i]; i++) copy[i] = copy[i] & 127; copy[i] = '\0'; Com_Printf("%s", copy); } cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { if (level > cl->messagelevel) continue; if (cl->state < cs_connected) continue; NET_WriteConstMsg(cl->stream, msg); } free_dbuffer(msg); }
/** * @brief Sends text across to be displayed if the level passes */ void SV_ClientPrintf (client_t * cl, int level, const char *fmt, ...) { va_list argptr; struct dbuffer *msg; char str[1024]; if (level > cl->messagelevel) return; msg = new_dbuffer(); NET_WriteByte(msg, svc_print); NET_WriteByte(msg, level); va_start(argptr, fmt); NET_VPrintf(msg, fmt, argptr, str, sizeof(str)); va_end(argptr); NET_WriteMsg(cl->stream, msg); }
/** * @brief Responds with all the info that the server browser can see * @sa SV_StatusString */ static void SVC_Status (struct net_stream *s) { client_t *cl; char player[1024]; struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, clc_oob); NET_WriteRawString(msg, "print\n"); NET_WriteRawString(msg, Cvar_Serverinfo()); NET_WriteRawString(msg, "\n"); cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) { if (cl->state > cs_free) { Com_sprintf(player, sizeof(player), "%i \"%s\"\n", svs.ge->ClientGetTeamNum(cl->player), cl->name); NET_WriteRawString(msg, player); } } NET_WriteMsg(s, msg); }
/** * @sa CL_ParseConfigString */ static void SV_Configstring (int index, const char *fmt, ...) { char val[MAX_TOKEN_CHARS * MAX_TILESTRINGS]; va_list argptr; if (index < 0 || index >= MAX_CONFIGSTRINGS) Com_Error(ERR_DROP, "configstring: bad index %i", index); va_start(argptr, fmt); Q_vsnprintf(val, sizeof(val), fmt, argptr); va_end(argptr); SV_SetConfigString(index, val); if (Com_ServerState() != ss_loading) { /* send the update to everyone */ struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_configstring); NET_WriteShort(msg, index); NET_WriteString(msg, val); /* send to all clients */ SV_Multicast(~0, msg); } }
/** * @brief If origin is NULL, the origin is determined from the entity origin or the midpoint of the entity box for bmodels. */ void SV_StartSound (int mask, const vec3_t origin, const edict_t *entity, const char *sound) { vec3_t origin_v; struct dbuffer *msg; /* use the entity origin unless it is a bmodel or explicitly specified */ if (!origin) { origin = origin_v; if (entity->solid == SOLID_BSP) { VectorCenterFromMinsMaxs(entity->mins, entity->maxs, origin_v); VectorAdd(entity->origin, origin_v, origin_v); } else { VectorCopy(entity->origin, origin_v); } } msg = new_dbuffer(); NET_WriteByte(msg, svc_sound); NET_WriteString(msg, sound); NET_WritePos(msg, origin); SV_Multicast(mask, msg); }
/** * @brief Used by SV_Shutdown to send a final message to all * connected clients before the server goes down. * @sa SV_Shutdown */ static void SV_FinalMessage (const char *message, qboolean reconnect) { client_t *cl; struct dbuffer *msg = new_dbuffer(); if (reconnect) NET_WriteByte(msg, svc_reconnect); else NET_WriteByte(msg, svc_disconnect); NET_WriteString(msg, message); cl = NULL; while ((cl = SV_GetNextClient(cl)) != NULL) if (cl->state >= cs_connected) { NET_WriteConstMsg(cl->stream, msg); NET_StreamFinished(cl->stream); cl->stream = NULL; } /* make sure, that this is send */ NET_Wait(0); free_dbuffer(msg); }
/** * @brief Sends the first message from the server to a connected client. * This will be sent on the initial connection and upon each server load. * Client reads via CL_ParseServerData in cl_parse.c * @sa CL_Reconnect_f * @sa CL_ConnectionlessPacket */ static void SV_New_f (client_t *cl) { Com_DPrintf(DEBUG_SERVER, "New() from %s\n", cl->name); if (cl->state != cs_connected) { if (cl->state == cs_spawning) { /* client typed 'reconnect/new' while connecting. */ Com_Printf("SV_New_f: client typed 'reconnect/new' while connecting\n"); SV_ClientCommand(cl, "\ndisconnect\nreconnect\n"); SV_DropClient(cl, ""); } else Com_DPrintf(DEBUG_SERVER, "WARNING: Illegal 'new' from %s, client state %d. This shouldn't happen...\n", cl->name, cl->state); return; } /* client state to prevent multiple new from causing high cpu / overflows. */ SV_SetClientState(cl, cs_spawning); /* serverdata needs to go over for all types of servers * to make sure the protocol is right, and to set the gamedir */ /* send the serverdata */ { const int playernum = cl - SV_GetClient(0); struct dbuffer *msg = new_dbuffer(); NET_WriteByte(msg, svc_serverdata); NET_WriteLong(msg, PROTOCOL_VERSION); NET_WriteShort(msg, playernum); /* send full levelname */ NET_WriteString(msg, SV_GetConfigString(CS_NAME)); NET_WriteMsg(cl->stream, msg); } /* game server */ if (Com_ServerState() == ss_game) { int i; for (i = 0; i < MAX_CONFIGSTRINGS; i++) { const char *configString; /* CS_TILES and CS_POSITIONS can stretch over multiple configstrings, * so don't send the middle parts again. */ if (i > CS_TILES && i < CS_POSITIONS) continue; if (i > CS_POSITIONS && i < CS_MODELS) continue; configString = SV_GetConfigString(i); if (configString[0] != '\0') { struct dbuffer *msg = new_dbuffer(); Com_DPrintf(DEBUG_SERVER, "sending configstring %d: %s\n", i, configString); NET_WriteByte(msg, svc_configstring); NET_WriteShort(msg, i); NET_WriteString(msg, configString); /* enqueue and free msg */ NET_WriteMsg(cl->stream, msg); } } } SV_ClientCommand(cl, "precache\n"); }