/** * @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, int entnum) { pending_event_t *p = &sv->pendingEvent; const int rawType = eType &~ EVENT_INSTANTLY; if (rawType >= EV_NUM_EVENTS || rawType < 0) Com_Error(ERR_DROP, "SV_AddEvent: invalid event %i", rawType); const char *eventName = eventNames[rawType].name; Com_DPrintf(DEBUG_EVENTSYS, "Event type: %s (%i - %i) (mask %s) (entnum: %i)\n", eventName, rawType, eType, Com_UnsignedIntToBinary(mask), entnum); /* finish the last event */ if (p->pending) SV_EndEvents(); /* start the new event */ p->pending = true; p->playerMask = mask; p->type = eType; p->entnum = entnum; p->buf = new dbuffer(); /* write header */ NET_WriteByte(p->buf, svc_event); NET_WriteByte(p->buf, eType); if (entnum != -1) NET_WriteShort(p->buf, entnum); }
/** * @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; }
static void testDBufferNetHandling (void) { dbuffer buf; NET_WriteByte(&buf, 'b'); CU_ASSERT_EQUAL(1, buf.length()); NET_WriteShort(&buf, 128); CU_ASSERT_EQUAL(3, buf.length()); NET_WriteLong(&buf, 128); CU_ASSERT_EQUAL(7, buf.length()); }
/** * @brief Writes to buffer according to format; version without syntactic sugar * for variable arguments, to call it from other functions with variable arguments * @note short and char are promoted to int when passed to variadic functions! */ void NET_vWriteFormat (dbuffer* buf, const char* format, va_list ap) { while (*format) { const char typeID = *format++; switch (typeID) { case 'c': NET_WriteChar(buf, va_arg(ap, int)); break; case 'b': NET_WriteByte(buf, va_arg(ap, int)); break; case 's': NET_WriteShort(buf, va_arg(ap, int)); break; case 'l': NET_WriteLong(buf, va_arg(ap, int)); break; case 'p': NET_WritePos(buf, va_arg(ap, float*)); break; case 'g': NET_WriteGPos(buf, va_arg(ap, byte*)); break; case 'd': NET_WriteDir(buf, va_arg(ap, float*)); break; case 'a': /* NOTE: float is promoted to double through ... */ NET_WriteAngle(buf, va_arg(ap, double)); break; case '!': break; case '&': NET_WriteString(buf, va_arg(ap, char*)); break; case '*': { const int n = va_arg(ap, int); const byte* p = va_arg(ap, byte*); NET_WriteShort(buf, n); for (int i = 0; i < n; i++) NET_WriteByte(buf, *p++); break; } default: Com_Error(ERR_DROP, "WriteFormat: Unknown type!"); } } /* Too many arguments for the given format; too few cause crash above */ #ifdef PARANOID if (!ap) Com_Error(ERR_DROP, "WriteFormat: Too many arguments!"); #endif }
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 G_SendInventory */ static void CL_NetSendInventory (struct dbuffer *buf, const inventory_t *i) { containerIndex_t container; int nr = 0; const invList_t *ic; for (container = 0; container < csi.numIDs; container++) { for (ic = i->c[container]; ic; ic = ic->next) nr++; } NET_WriteShort(buf, nr * INV_INVENTORY_BYTES); for (container = 0; container < csi.numIDs; container++) { for (ic = i->c[container]; ic; ic = ic->next) CL_NetSendItem(buf, ic->item, container, ic->x, ic->y); } }
/** * @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); } }
/** * @sa CL_ParseConfigString */ static void SV_Configstring (int index, const char *fmt, ...) { char val[MAX_TOKEN_CHARS * MAX_TILESTRINGS]; va_list argptr; if (!Com_CheckConfigStringIndex(index)) SV_error("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 */ dbuffer msg(4 + strlen(val)); NET_WriteByte(&msg, svc_configstring); NET_WriteShort(&msg, index); NET_WriteString(&msg, val); /* send to all clients */ SV_Multicast(~0, msg); } }
/** * @brief Send the character information to the server that is needed to spawn the soldiers of the player. * @param[out] buf The net channel buffer to write the character data into. * @param[in] chr The character to get the data from. */ static void GAME_NetSendCharacter (struct dbuffer * buf, const character_t *chr) { int j; if (!chr) Com_Error(ERR_DROP, "No character given"); if (chr->fieldSize != ACTOR_SIZE_2x2 && chr->fieldSize != ACTOR_SIZE_NORMAL) Com_Error(ERR_DROP, "Invalid character size given for character '%s': %i", chr->name, chr->fieldSize); if (chr->teamDef == NULL) Com_Error(ERR_DROP, "Character with no teamdef set (%s)", chr->name); NET_WriteByte(buf, chr->fieldSize); NET_WriteShort(buf, chr->ucn); NET_WriteString(buf, chr->name); /* model */ NET_WriteString(buf, chr->path); NET_WriteString(buf, chr->body); NET_WriteString(buf, chr->head); NET_WriteByte(buf, chr->skin); NET_WriteShort(buf, chr->HP); NET_WriteShort(buf, chr->maxHP); NET_WriteByte(buf, chr->teamDef->idx); NET_WriteByte(buf, chr->gender); NET_WriteByte(buf, chr->STUN); NET_WriteByte(buf, chr->morale); for (j = 0; j < SKILL_NUM_TYPES + 1; j++) NET_WriteLong(buf, chr->score.experience[j]); for (j = 0; j < SKILL_NUM_TYPES; j++) NET_WriteByte(buf, chr->score.skills[j]); for (j = 0; j < SKILL_NUM_TYPES + 1; j++) NET_WriteByte(buf, chr->score.initialSkills[j]); for (j = 0; j < KILLED_NUM_TYPES; j++) NET_WriteShort(buf, chr->score.kills[j]); for (j = 0; j < KILLED_NUM_TYPES; j++) NET_WriteShort(buf, chr->score.stuns[j]); NET_WriteShort(buf, chr->score.assignedMissions); }
/** * @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"); }
static void SV_WriteShort (int c) { NET_WriteShort(sv->pendingEvent.buf, c); }
void NET_WriteAngle16 (dbuffer* buf, float f) { NET_WriteShort(buf, ANGLE2SHORT(f)); }