ref: f6824b14fd2fcc81ad5e81d2655100ec0bb1af68
dir: /src/net_structrw.c/
// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Reading and writing various structures into packets // #include <stdio.h> #include <stdlib.h> #include <string.h> #include "doomtype.h" #include "i_system.h" #include "m_misc.h" #include "net_packet.h" #include "net_structrw.h" // String names for the enum values in net_protocol_t, which are what is // sent over the wire. Every enum value must have an entry in this list. static struct { net_protocol_t protocol; const char *name; } protocol_names[] = { {NET_PROTOCOL_CHOCOLATE_DOOM_0, "CHOCOLATE_DOOM_0"}, }; void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data) { NET_WriteInt8(packet, data->gamemode); NET_WriteInt8(packet, data->gamemission); NET_WriteInt8(packet, data->lowres_turn); NET_WriteInt8(packet, data->drone); NET_WriteInt8(packet, data->max_players); NET_WriteInt8(packet, data->is_freedoom); NET_WriteSHA1Sum(packet, data->wad_sha1sum); NET_WriteSHA1Sum(packet, data->deh_sha1sum); NET_WriteInt8(packet, data->player_class); } boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data) { return NET_ReadInt8(packet, (unsigned int *) &data->gamemode) && NET_ReadInt8(packet, (unsigned int *) &data->gamemission) && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &data->drone) && NET_ReadInt8(packet, (unsigned int *) &data->max_players) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom) && NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->player_class); } void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings) { int i; NET_WriteInt8(packet, settings->ticdup); NET_WriteInt8(packet, settings->extratics); NET_WriteInt8(packet, settings->deathmatch); NET_WriteInt8(packet, settings->nomonsters); NET_WriteInt8(packet, settings->fast_monsters); NET_WriteInt8(packet, settings->respawn_monsters); NET_WriteInt8(packet, settings->episode); NET_WriteInt8(packet, settings->map); NET_WriteInt8(packet, settings->skill); NET_WriteInt8(packet, settings->gameversion); NET_WriteInt8(packet, settings->lowres_turn); NET_WriteInt8(packet, settings->new_sync); NET_WriteInt32(packet, settings->timelimit); NET_WriteInt8(packet, settings->loadgame); NET_WriteInt8(packet, settings->random); NET_WriteInt8(packet, settings->num_players); NET_WriteInt8(packet, settings->consoleplayer); for (i = 0; i < settings->num_players; ++i) { NET_WriteInt8(packet, settings->player_classes[i]); } } boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings) { boolean success; int i; success = NET_ReadInt8(packet, (unsigned int *) &settings->ticdup) && NET_ReadInt8(packet, (unsigned int *) &settings->extratics) && NET_ReadInt8(packet, (unsigned int *) &settings->deathmatch) && NET_ReadInt8(packet, (unsigned int *) &settings->nomonsters) && NET_ReadInt8(packet, (unsigned int *) &settings->fast_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->respawn_monsters) && NET_ReadInt8(packet, (unsigned int *) &settings->episode) && NET_ReadInt8(packet, (unsigned int *) &settings->map) && NET_ReadSInt8(packet, &settings->skill) && NET_ReadInt8(packet, (unsigned int *) &settings->gameversion) && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn) && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync) && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit) && NET_ReadSInt8(packet, (signed int *) &settings->loadgame) && NET_ReadInt8(packet, (unsigned int *) &settings->random) && NET_ReadInt8(packet, (unsigned int *) &settings->num_players) && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer); if (!success) { return false; } for (i = 0; i < settings->num_players && i < NET_MAXPLAYERS; ++i) { if (!NET_ReadInt8(packet, (unsigned int *) &settings->player_classes[i])) { return false; } } return true; } boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query) { boolean success; query->version = NET_ReadSafeString(packet); success = query->version != NULL && NET_ReadInt8(packet, (unsigned int *) &query->server_state) && NET_ReadInt8(packet, (unsigned int *) &query->num_players) && NET_ReadInt8(packet, (unsigned int *) &query->max_players) && NET_ReadInt8(packet, (unsigned int *) &query->gamemode) && NET_ReadInt8(packet, (unsigned int *) &query->gamemission); if (!success) { return false; } query->description = NET_ReadSafeString(packet); // We read the list of protocols supported by the server. However, // old versions of Chocolate Doom do not support this field; it is // okay if it cannot be successfully read. query->protocol = NET_ReadProtocolList(packet); return query->description != NULL; } 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); // Write a list of all supported protocols. Note that the query->protocol // field is ignored here; it is only used when receiving. NET_WriteProtocolList(packet); } void NET_WriteTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn) { // Header NET_WriteInt8(packet, diff->diff); // Write the fields which are enabled: if (diff->diff & NET_TICDIFF_FORWARD) NET_WriteInt8(packet, diff->cmd.forwardmove); if (diff->diff & NET_TICDIFF_SIDE) NET_WriteInt8(packet, diff->cmd.sidemove); if (diff->diff & NET_TICDIFF_TURN) { if (lowres_turn) { NET_WriteInt8(packet, diff->cmd.angleturn / 256); } else { NET_WriteInt16(packet, diff->cmd.angleturn); } } if (diff->diff & NET_TICDIFF_BUTTONS) NET_WriteInt8(packet, diff->cmd.buttons); if (diff->diff & NET_TICDIFF_CONSISTANCY) NET_WriteInt8(packet, diff->cmd.consistancy); if (diff->diff & NET_TICDIFF_CHATCHAR) NET_WriteInt8(packet, diff->cmd.chatchar); if (diff->diff & NET_TICDIFF_RAVEN) { NET_WriteInt8(packet, diff->cmd.lookfly); NET_WriteInt8(packet, diff->cmd.arti); } if (diff->diff & NET_TICDIFF_STRIFE) { NET_WriteInt8(packet, diff->cmd.buttons2); NET_WriteInt16(packet, diff->cmd.inventory); } } boolean NET_ReadTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn) { unsigned int val; signed int sval; // Read header if (!NET_ReadInt8(packet, &diff->diff)) return false; // Read fields if (diff->diff & NET_TICDIFF_FORWARD) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.forwardmove = sval; } if (diff->diff & NET_TICDIFF_SIDE) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.sidemove = sval; } if (diff->diff & NET_TICDIFF_TURN) { if (lowres_turn) { if (!NET_ReadSInt8(packet, &sval)) return false; diff->cmd.angleturn = sval * 256; } else { if (!NET_ReadSInt16(packet, &sval)) return false; diff->cmd.angleturn = sval; } } if (diff->diff & NET_TICDIFF_BUTTONS) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons = val; } if (diff->diff & NET_TICDIFF_CONSISTANCY) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.consistancy = val; } if (diff->diff & NET_TICDIFF_CHATCHAR) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.chatchar = val; } if (diff->diff & NET_TICDIFF_RAVEN) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.lookfly = val; if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.arti = val; } if (diff->diff & NET_TICDIFF_STRIFE) { if (!NET_ReadInt8(packet, &val)) return false; diff->cmd.buttons2 = val; if (!NET_ReadInt16(packet, &val)) return false; diff->cmd.inventory = val; } return true; } void NET_TiccmdDiff(ticcmd_t *tic1, ticcmd_t *tic2, net_ticdiff_t *diff) { diff->diff = 0; diff->cmd = *tic2; if (tic1->forwardmove != tic2->forwardmove) diff->diff |= NET_TICDIFF_FORWARD; if (tic1->sidemove != tic2->sidemove) diff->diff |= NET_TICDIFF_SIDE; if (tic1->angleturn != tic2->angleturn) diff->diff |= NET_TICDIFF_TURN; if (tic1->buttons != tic2->buttons) diff->diff |= NET_TICDIFF_BUTTONS; if (tic1->consistancy != tic2->consistancy) diff->diff |= NET_TICDIFF_CONSISTANCY; if (tic2->chatchar != 0) diff->diff |= NET_TICDIFF_CHATCHAR; // Heretic/Hexen-specific if (tic1->lookfly != tic2->lookfly || tic2->arti != 0) diff->diff |= NET_TICDIFF_RAVEN; // Strife-specific if (tic1->buttons2 != tic2->buttons2 || tic2->inventory != 0) diff->diff |= NET_TICDIFF_STRIFE; } void NET_TiccmdPatch(ticcmd_t *src, net_ticdiff_t *diff, ticcmd_t *dest) { memmove(dest, src, sizeof(ticcmd_t)); // Apply the diff if (diff->diff & NET_TICDIFF_FORWARD) dest->forwardmove = diff->cmd.forwardmove; if (diff->diff & NET_TICDIFF_SIDE) dest->sidemove = diff->cmd.sidemove; if (diff->diff & NET_TICDIFF_TURN) dest->angleturn = diff->cmd.angleturn; if (diff->diff & NET_TICDIFF_BUTTONS) dest->buttons = diff->cmd.buttons; if (diff->diff & NET_TICDIFF_CONSISTANCY) dest->consistancy = diff->cmd.consistancy; if (diff->diff & NET_TICDIFF_CHATCHAR) dest->chatchar = diff->cmd.chatchar; else dest->chatchar = 0; // Heretic/Hexen specific: if (diff->diff & NET_TICDIFF_RAVEN) { dest->lookfly = diff->cmd.lookfly; dest->arti = diff->cmd.arti; } else { dest->arti = 0; } // Strife-specific: if (diff->diff & NET_TICDIFF_STRIFE) { dest->buttons2 = diff->cmd.buttons2; dest->inventory = diff->cmd.inventory; } else { dest->inventory = 0; } } // // net_full_ticcmd_t // boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn) { unsigned int bitfield; int i; // Latency if (!NET_ReadSInt16(packet, &cmd->latency)) { return false; } // Regenerate playeringame from the "header" bitfield if (!NET_ReadInt8(packet, &bitfield)) { return false; } for (i=0; i<NET_MAXPLAYERS; ++i) { cmd->playeringame[i] = (bitfield & (1 << i)) != 0; } // Read cmds for (i=0; i<NET_MAXPLAYERS; ++i) { if (cmd->playeringame[i]) { if (!NET_ReadTiccmdDiff(packet, &cmd->cmds[i], lowres_turn)) { return false; } } } return true; } void NET_WriteFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd, boolean lowres_turn) { unsigned int bitfield; int i; // Write the latency NET_WriteInt16(packet, cmd->latency); // Write "header" byte indicating which players are active // in this ticcmd bitfield = 0; for (i=0; i<NET_MAXPLAYERS; ++i) { if (cmd->playeringame[i]) { bitfield |= 1 << i; } } NET_WriteInt8(packet, bitfield); // Write player ticcmds for (i=0; i<NET_MAXPLAYERS; ++i) { if (cmd->playeringame[i]) { NET_WriteTiccmdDiff(packet, &cmd->cmds[i], lowres_turn); } } } 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); } boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data) { int i; char *s; if (!NET_ReadInt8(packet, (unsigned int *) &data->num_players) || !NET_ReadInt8(packet, (unsigned int *) &data->num_drones) || !NET_ReadInt8(packet, (unsigned int *) &data->ready_players) || !NET_ReadInt8(packet, (unsigned int *) &data->max_players) || !NET_ReadInt8(packet, (unsigned int *) &data->is_controller) || !NET_ReadSInt8(packet, &data->consoleplayer)) { return false; } for (i = 0; i < data->num_players && i < NET_MAXPLAYERS; ++i) { s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } M_StringCopy(data->player_names[i], s, MAXPLAYERNAME); s = NET_ReadString(packet); if (s == NULL || strlen(s) >= MAXPLAYERNAME) { return false; } M_StringCopy(data->player_addrs[i], s, MAXPLAYERNAME); } return NET_ReadSHA1Sum(packet, data->wad_sha1sum) && NET_ReadSHA1Sum(packet, data->deh_sha1sum) && NET_ReadInt8(packet, (unsigned int *) &data->is_freedoom); } static boolean NET_ReadBlob(net_packet_t *packet, uint8_t *buf, size_t len) { unsigned int b; int i; for (i=0; i<len; ++i) { if (!NET_ReadInt8(packet, &b)) { return false; } buf[i] = b; } return true; } static void NET_WriteBlob(net_packet_t *packet, uint8_t *buf, size_t len) { int i; for (i=0; i<len; ++i) { NET_WriteInt8(packet, buf[i]); } } boolean NET_ReadSHA1Sum(net_packet_t *packet, sha1_digest_t digest) { return NET_ReadBlob(packet, digest, sizeof(sha1_digest_t)); } void NET_WriteSHA1Sum(net_packet_t *packet, sha1_digest_t digest) { NET_WriteBlob(packet, digest, sizeof(sha1_digest_t)); } boolean NET_ReadPRNGSeed(net_packet_t *packet, prng_seed_t seed) { return NET_ReadBlob(packet, seed, sizeof(prng_seed_t)); } void NET_WritePRNGSeed(net_packet_t *packet, prng_seed_t seed) { NET_WriteBlob(packet, seed, sizeof(prng_seed_t)); } static net_protocol_t ParseProtocolName(const char *name) { int i; for (i = 0; i < arrlen(protocol_names); ++i) { if (!strcmp(protocol_names[i].name, name)) { return protocol_names[i].protocol; } } return NET_PROTOCOL_UNKNOWN; } // NET_ReadProtocol reads a single string-format protocol name from the given // packet, returning NET_PROTOCOL_UNKNOWN if the string describes an unknown // protocol. net_protocol_t NET_ReadProtocol(net_packet_t *packet) { const char *name; name = NET_ReadString(packet); if (name == NULL) { return NET_PROTOCOL_UNKNOWN; } return ParseProtocolName(name); } // NET_WriteProtocol writes a single string-format protocol name to a packet. void NET_WriteProtocol(net_packet_t *packet, net_protocol_t protocol) { int i; for (i = 0; i < arrlen(protocol_names); ++i) { if (protocol_names[i].protocol == protocol) { NET_WriteString(packet, protocol_names[i].name); return; } } // If you add an entry to the net_protocol_t enum, a corresponding entry // must be added to the protocol_names list. I_Error("NET_WriteProtocol: protocol %d missing from protocol_names " "list; please add it.", protocol); } // NET_ReadProtocolList reads a list of string-format protocol names from // the given packet, returning a single protocol number. The protocol that is // returned is the last protocol in the list that is a supported protocol. If // no recognized protocols are read, NET_PROTOCOL_UNKNOWN is returned. net_protocol_t NET_ReadProtocolList(net_packet_t *packet) { net_protocol_t result; unsigned int num_protocols; int i; if (!NET_ReadInt8(packet, &num_protocols)) { return NET_PROTOCOL_UNKNOWN; } result = NET_PROTOCOL_UNKNOWN; for (i = 0; i < num_protocols; ++i) { net_protocol_t p; const char *name; name = NET_ReadString(packet); if (name == NULL) { return NET_PROTOCOL_UNKNOWN; } p = ParseProtocolName(name); if (p != NET_PROTOCOL_UNKNOWN) { result = p; } } return result; } // NET_WriteProtocolList writes a list of string-format protocol names into // the given packet, all the supported protocols in the net_protocol_t enum. // This is slightly different to other functions in this file, in that there // is nothing the caller can "choose" to write; the built-in list of all // protocols is always sent. void NET_WriteProtocolList(net_packet_t *packet) { int i; NET_WriteInt8(packet, NET_NUM_PROTOCOLS); for (i = 0; i < NET_NUM_PROTOCOLS; ++i) { NET_WriteProtocol(packet, i); } }