/** * @brief Responds with teaminfo such as free team num * @sa CL_ParseTeamInfoMessage */ static void SVC_TeamInfo (struct net_stream *s) { client_t *cl; dbuffer msg; 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); }
void NET_WriteQueryData(net_packet_t *packet, net_querydata_t *query) { NET_WriteString(packet, query->version); NET_WriteInt8(packet, query->server_state); NET_WriteInt8(packet, query->num_players); NET_WriteInt8(packet, query->max_players); NET_WriteInt8(packet, query->gamemode); NET_WriteInt8(packet, query->gamemission); NET_WriteString(packet, query->description); }
static void NET_CL_SendSYN(net_connect_data_t *data) { net_packet_t *packet; packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_SYN); NET_WriteInt32(packet, NET_MAGIC_NUMBER); NET_WriteString(packet, PACKAGE_STRING); NET_WriteConnectData(packet, data); NET_WriteString(packet, net_player_name); NET_Conn_SendPacket(&client_connection, packet); NET_FreePacket(packet); }
/** * @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); } }
/** * @brief * @note Called after precache was sent from the server * @sa SV_Configstrings_f * @sa CL_Precache_f */ void CL_RequestNextDownload (void) { if (cls.state != ca_connected) { Com_Printf("CL_RequestNextDownload: Not connected (%i)\n", cls.state); return; } /* Use the map data from the server */ cl.mapTiles = SV_GetMapTiles(); cl.mapData = SV_GetMapData(); /* as a multiplayer client we have to load the map here and * check the compatibility with the server */ if (!Com_ServerState() && !CL_CanMultiplayerStart()) return; CL_ViewLoadMedia(); dbuffer msg(7); /* send begin */ /* this will activate the render process (see client state ca_active) */ NET_WriteByte(&msg, clc_stringcmd); /* see CL_StartGame */ NET_WriteString(&msg, NET_STATE_BEGIN "\n"); NET_WriteMsg(cls.netStream, msg); cls.waitingForStart = CL_Milliseconds(); S_MumbleLink(); }
char *NET_EndSecureDemo(sha1_digest_t demo_hash) { net_packet_t *request, *response; net_addr_t *master_addr; char *signature; master_addr = NET_Query_ResolveMaster(query_context); // Construct end request and send to master server. request = NET_NewPacket(10); NET_WriteInt16(request, NET_MASTER_PACKET_TYPE_SIGN_END); NET_WriteSHA1Sum(request, demo_hash); NET_WriteString(request, securedemo_start_message); NET_SendPacket(master_addr, request); NET_FreePacket(request); // Block for response. The response packet simply contains a string // with the ASCII signature. response = BlockForPacket(master_addr, NET_MASTER_PACKET_TYPE_SIGN_END_RESPONSE, SIGNATURE_TIMEOUT_SECS * 1000); if (response == NULL) { return NULL; } signature = NET_ReadString(response); NET_FreePacket(response); return signature; }
/** * @brief Sets the @c cls.state to @c ca_disconnected and informs the server * @sa CL_Drop * @note Goes from a connected state to disconnected state * Sends a disconnect message to the server * This is also called on @c Com_Error, so it shouldn't cause any errors */ void CL_Disconnect (void) { if (cls.state < ca_connecting) return; Com_Printf("Disconnecting...\n"); /* send a disconnect message to the server */ if (!Com_ServerState()) { dbuffer msg; NET_WriteByte(&msg, clc_stringcmd); NET_WriteString(&msg, NET_STATE_DISCONNECT "\n"); NET_WriteMsg(cls.netStream, msg); /* make sure, that this is send */ NET_Wait(0); } NET_StreamFinished(cls.netStream); cls.netStream = nullptr; CL_ClearState(); S_Stop(); R_ShutdownModels(false); R_FreeWorldImages(); CL_SetClientState(ca_disconnected); CL_ClearBattlescapeEvents(); GAME_EndBattlescape(); }
/** * @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 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 */ dbuffer msg(2 + strlen(message)); 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 */ const ScopedMutex scopedMutex(svs.serverMutex); svs.ge->ClientDisconnect(drop->player); } NET_StreamFinished(drop->stream); drop->stream = NULL; drop->player->inuse = false; 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 = true; } }
static void NET_CL_SendSYN(void) { net_packet_t *packet; packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_SYN); NET_WriteInt32(packet, NET_MAGIC_NUMBER); NET_WriteString(packet, PACKAGE_STRING); NET_WriteInt16(packet, gamemode); NET_WriteInt16(packet, gamemission); NET_WriteInt8(packet, lowres_turn); NET_WriteInt8(packet, drone); NET_WriteMD5Sum(packet, net_local_wad_md5sum); NET_WriteMD5Sum(packet, net_local_deh_md5sum); NET_WriteInt8(packet, net_local_is_freedoom); NET_WriteString(packet, net_player_name); NET_Conn_SendPacket(&client_connection, packet); NET_FreePacket(packet); }
static void NET_SV_SendReject(net_addr_t *addr, char *msg) { net_packet_t *packet; packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_REJECTED); NET_WriteString(packet, msg); NET_SendPacket(addr, packet); NET_FreePacket(packet); }
/** * @brief Responds with teaminfo such as free team num * @sa CL_ParseTeamInfoMessage */ static void SVC_TeamInfo (struct net_stream* s) { if (SVC_RateLimitAddress(*s)) { Com_DPrintf(DEBUG_SERVER, "SVC_TeamInfo: rate limit from %s exceeded, dropping request\n", NET_StreamToString(s)); return; } /* Allow getinfo to be DoSed relatively easily, but prevent excess outbound bandwidth usage when being flooded inbound */ if (SVC_RateLimit(&outboundLeakyBucket)) { Com_DPrintf(DEBUG_SERVER, "SVC_TeamInfo: rate limit exceeded, dropping request\n"); return; } char infoGlobal[MAX_INFO_STRING] = ""; 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")); dbuffer msg; NET_WriteByte(&msg, svc_oob); NET_WriteRawString(&msg, "teaminfo\n"); NET_WriteString(&msg, infoGlobal); client_t* cl = nullptr; while ((cl = SV_GetNextClient(cl)) != nullptr) { if (cl->state < cs_connected) continue; 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 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); }
void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data) { int i; NET_WriteInt8(packet, data->num_players); NET_WriteInt8(packet, data->num_drones); NET_WriteInt8(packet, data->ready_players); NET_WriteInt8(packet, data->max_players); NET_WriteInt8(packet, data->is_controller); NET_WriteInt8(packet, data->consoleplayer); for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i) { NET_WriteString(packet, data->player_names[i]); NET_WriteString(packet, data->player_addrs[i]); } NET_WriteSHA1Sum(packet, data->wad_sha1sum); NET_WriteSHA1Sum(packet, data->deh_sha1sum); NET_WriteInt8(packet, data->is_freedoom); }
static void NET_SV_SendReject(net_addr_t *addr, const char *msg) { net_packet_t *packet; NET_Log("server: sending reject to %s", NET_AddrToString(addr)); packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_REJECTED); NET_WriteString(packet, msg); NET_SendPacket(addr, packet); NET_FreePacket(packet); }
/** * @brief Send the userinfo to the server (and to all other clients) * when they changed (CVAR_USERINFO) * @sa CL_Connect */ static void CL_SendChangedUserinfos (void) { /* send a userinfo update if needed */ if (cls.state < ca_connected) return; if (!Com_IsUserinfoModified()) return; char info[MAX_INFO_STRING]; const char* userInfo = Cvar_Userinfo(info, sizeof(info)); dbuffer msg(strlen(userInfo) + 2); NET_WriteByte(&msg, clc_userinfo); NET_WriteString(&msg, userInfo); NET_WriteMsg(cls.netStream, msg); Com_SetUserinfoModified(false); }
static void NET_SV_SendConsoleMessage(net_client_t *client, char *s, ...) { char buf[1024]; va_list args; net_packet_t *packet; va_start(args, s); vsnprintf(buf, sizeof(buf), s, args); va_end(args); packet = NET_Conn_NewReliable(&client->connection, NET_PACKET_TYPE_CONSOLE_MESSAGE); NET_WriteString(packet, buf); }
/** * @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, bool reconnect) { client_t *cl; dbuffer msg(2 + strlen(message)); 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); }
/** * @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); }
/** * @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); } }
static void NET_SV_ParseSYN(net_packet_t *packet, net_client_t *client, net_addr_t *addr) { unsigned int magic; net_connect_data_t data; net_packet_t *reply; net_protocol_t protocol; char *player_name; char *client_version; int num_players; int i; NET_Log("server: processing SYN packet"); // Read the magic number and check it is the expected one. if (!NET_ReadInt32(packet, &magic)) { NET_Log("server: error: no magic number for SYN"); return; } switch (magic) { case NET_MAGIC_NUMBER: break; case NET_OLD_MAGIC_NUMBER: NET_Log("server: error: client using old magic number: %d", magic); NET_SV_SendReject(addr, "You are using an old client version that is not supported by " "this server. This server is running " PACKAGE_STRING "."); return; default: NET_Log("server: error: wrong magic number: %d", magic); return; } // Read the client version string. We actually now only use this when // sending a reject message, as we only reject if we can't negotiate a // common protocol (below). client_version = NET_ReadString(packet); if (client_version == NULL) { NET_Log("server: error: no version from client"); return; } // Read the client's list of accepted protocols. Net play between forks // of Chocolate Doom is accepted provided that they can negotiate a // common accepted protocol. protocol = NET_ReadProtocolList(packet); if (protocol == NET_PROTOCOL_UNKNOWN) { char reject_msg[256]; M_snprintf(reject_msg, sizeof(reject_msg), "Version mismatch: server version is: " PACKAGE_STRING "; " "client is: %s. No common compatible protocol could be " "negotiated.", client_version); NET_SV_SendReject(addr, reject_msg); NET_Log("server: error: no common protocol"); return; } // Read connect data, and check that the game mode/mission are valid // and the max_players value is in a sensible range. if (!NET_ReadConnectData(packet, &data)) { NET_Log("server: error: failed to read connect data"); return; } if (!D_ValidGameMode(data.gamemission, data.gamemode) || data.max_players > NET_MAXPLAYERS) { NET_Log("server: error: invalid connect data, max_players=%d, " "gamemission=%d, gamemode=%d", data.max_players, data.gamemission, data.gamemode); return; } // Read the player's name player_name = NET_ReadString(packet); if (player_name == NULL) { NET_Log("server: error: failed to read player name"); return; } // At this point we have received a valid SYN. // Not accepting new connections? if (server_state != SERVER_WAITING_LAUNCH) { NET_Log("server: error: not in waiting launch state, server_state=%d", server_state); NET_SV_SendReject(addr, "Server is not currently accepting connections"); return; } // Before accepting a new client, check that there is a slot free. NET_SV_AssignPlayers(); num_players = NET_SV_NumPlayers(); if ((!data.drone && num_players >= NET_SV_MaxPlayers()) || NET_SV_NumClients() >= MAXNETNODES) { NET_Log("server: no more players, num_players=%d, max=%d", num_players, NET_SV_MaxPlayers()); NET_SV_SendReject(addr, "Server is full!"); return; } // TODO: Add server option to allow rejecting clients which set // lowres_turn. This is potentially desirable as the presence of such // clients affects turning resolution. // Adopt the game mode and mission of the first connecting client: if (num_players == 0 && !data.drone) { sv_gamemode = data.gamemode; sv_gamemission = data.gamemission; NET_Log("server: new game, mode=%d, mission=%d", sv_gamemode, sv_gamemission); } // Check the connecting client is playing the same game as all // the other clients if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission) { char msg[128]; NET_Log("server: wrong mode/mission, %d != %d || %d != %d", data.gamemode, sv_gamemode, data.gamemission, sv_gamemission); M_snprintf(msg, sizeof(msg), "Game mismatch: server is %s (%s), client is %s (%s)", D_GameMissionString(sv_gamemission), D_GameModeString(sv_gamemode), D_GameMissionString(data.gamemission), D_GameModeString(data.gamemode)); NET_SV_SendReject(addr, msg); return; } // Allocate a client slot if there isn't one already if (client == NULL) { // find a slot, or return if none found for (i=0; i<MAXNETNODES; ++i) { if (!clients[i].active) { client = &clients[i]; break; } } if (client == NULL) { return; } } else { // If this is a recently-disconnected client, deactivate // to allow immediate reconnection if (client->connection.state == NET_CONN_STATE_DISCONNECTED) { client->active = false; } } // Client already connected? if (client->active) { NET_Log("server: client is already initialized (duplicate SYN?)"); return; } // Activate, initialize connection NET_SV_InitNewClient(client, addr, protocol); // Save the SHA1 checksums and other details. memcpy(client->wad_sha1sum, data.wad_sha1sum, sizeof(sha1_digest_t)); memcpy(client->deh_sha1sum, data.deh_sha1sum, sizeof(sha1_digest_t)); client->is_freedoom = data.is_freedoom; client->max_players = data.max_players; client->name = M_StringDuplicate(player_name); client->recording_lowres = data.lowres_turn; client->drone = data.drone; client->player_class = data.player_class; // Send a reply back to the client, indicating a successful connection // and specifying the protocol that will be used for communications. reply = NET_Conn_NewReliable(&client->connection, NET_PACKET_TYPE_SYN); NET_WriteString(reply, PACKAGE_STRING); NET_WriteProtocol(reply, protocol); }
/** * @brief Responses to broadcasts, etc * @sa CL_ReadPackets * @sa CL_Frame * @sa SVC_DirectConnect * @param[in,out] msg The client stream message buffer to read from */ static void CL_ConnectionlessPacket (dbuffer* msg) { char s[512]; NET_ReadStringLine(msg, s, sizeof(s)); Cmd_TokenizeString(s, false); const char* c = Cmd_Argv(0); Com_DPrintf(DEBUG_CLIENT, "server OOB: %s (%s)\n", c, Cmd_Args()); /* server connection */ if (Q_streq(c, CL_CMD_CLIENT_CONNECT)) { int i; for (i = 1; i < Cmd_Argc(); i++) { if (char const* const p = Q_strstart(Cmd_Argv(i), "dlserver=")) { Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "ufo://%s", cls.servername); CL_SetHTTPServer(p); if (cls.downloadServer[0]) Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer); } } if (cls.state == ca_connected) { Com_Printf("Dup connect received. Ignored.\n"); return; } dbuffer buf(5); NET_WriteByte(&buf, clc_stringcmd); NET_WriteString(&buf, NET_STATE_NEW "\n"); NET_WriteMsg(cls.netStream, buf); GAME_InitMissionBriefing(_("Loading")); return; } /* remote command from gui front end */ if (Q_streq(c, CL_CMD_COMMAND)) { if (!NET_StreamIsLoopback(cls.netStream)) { Com_Printf("Command packet from remote host. Ignored.\n"); return; } else { char str[512]; NET_ReadString(msg, str, sizeof(str)); Cbuf_AddText("%s\n", str); } return; } /* ping from server */ if (Q_streq(c, CL_CMD_PING)) { NET_OOB_Printf(cls.netStream, SV_CMD_ACK); return; } /* echo request from server */ if (Q_streq(c, CL_CMD_ECHO)) { NET_OOB_Printf(cls.netStream, "%s", Cmd_Argv(1)); return; } /* print */ if (Q_streq(c, SV_CMD_PRINT)) { NET_ReadString(msg, popupText, sizeof(popupText)); /* special reject messages needs proper handling */ if (strstr(popupText, REJ_PASSWORD_REQUIRED_OR_INCORRECT)) { UI_PushWindow("serverpassword"); if (Q_strvalid(Cvar_GetString("password"))) { Cvar_Set("password", ""); CL_Drop(); UI_Popup(_("Connection failure"), _("The password you specified was wrong.")); } else { CL_Drop(); UI_Popup(_("Connection failure"), _("This server requires a password.")); } } else if (strstr(popupText, REJ_SERVER_FULL)) { CL_Drop(); UI_Popup(_("Connection failure"), _("This server is full.")); } else if (strstr(popupText, REJ_BANNED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("You are banned on this server.")); } else if (strstr(popupText, REJ_GAME_ALREADY_STARTED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The game has already started.")); } else if (strstr(popupText, REJ_SERVER_VERSION_MISMATCH)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The server is running a different version of the game.")); } else if (strstr(popupText, BAD_RCON_PASSWORD)) { Cvar_Set("rcon_password", ""); UI_Popup(_("Bad rcon password"), _("The rcon password you specified was wrong.")); } else if (strstr(popupText, REJ_CONNECTION_REFUSED)) { CL_Drop(); UI_Popup(_("Connection failure"), _("The server refused the connection.")); } else if (Q_strvalid(popupText)) { UI_Popup(_("Notice"), _(popupText)); } return; } if (!GAME_HandleServerCommand(c, msg)) Com_Printf("Unknown command received \"%s\"\n", c); }
static void NET_SV_SendWaitingData(net_client_t *client) { net_packet_t *packet; net_client_t *controller; int num_players; int i; NET_SV_AssignPlayers(); controller = NET_SV_Controller(); num_players = NET_SV_NumPlayers(); // time to send the client another status packet packet = NET_NewPacket(10); NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA); // include the number of players waiting NET_WriteInt8(packet, num_players); // send the number of drone clients NET_WriteInt8(packet, NET_SV_NumDrones()); // indicate whether the client is the controller NET_WriteInt8(packet, client == controller); // send the player number of this client NET_WriteInt8(packet, client->player_number); // send the addresses of all players for (i=0; i<num_players; ++i) { char *addr; // name NET_WriteString(packet, sv_players[i]->name); // address addr = NET_AddrToString(sv_players[i]->addr); NET_WriteString(packet, addr); } // Send the WAD and dehacked checksums of the controlling client. if (controller != NULL) { NET_WriteMD5Sum(packet, controller->wad_md5sum); NET_WriteMD5Sum(packet, controller->deh_md5sum); NET_WriteInt8(packet, controller->is_freedoom); } else { NET_WriteMD5Sum(packet, client->wad_md5sum); NET_WriteMD5Sum(packet, client->deh_md5sum); NET_WriteInt8(packet, client->is_freedoom); } // send packet to client and free NET_Conn_SendPacket(&client->connection, packet); NET_FreePacket(packet); }
static void SV_WriteString (const char *s) { NET_WriteString(sv->pendingEvent.buf, s); }
/** * @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"); }