diff options
author | rubidium <rubidium@openttd.org> | 2009-01-17 16:53:32 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2009-01-17 16:53:32 +0000 |
commit | 72b0341ed01caed7053df6f2ca7f9530d1f67e40 (patch) | |
tree | 76215ba6e27bc0b1f49919c01ff2608f276b8e3d /src/network/core | |
parent | 3940ed7e6f0bcb4fbe74c632254d6824692864dd (diff) | |
download | openttd-72b0341ed01caed7053df6f2ca7f9530d1f67e40.tar.xz |
(svn r15126) -Feature: downloading content from a central server (content.openttd.org) where authors can upload they NewGRFS/AI etc. This should make joining servers that use only NewGRFs that are distributed via this system easier as the players can download the NewGRFs from in the game. It should also make it easier to see whether there are updates for NewGRFs and make the necessary updates.
Diffstat (limited to 'src/network/core')
-rw-r--r-- | src/network/core/config.h | 3 | ||||
-rw-r--r-- | src/network/core/tcp_content.cpp | 136 | ||||
-rw-r--r-- | src/network/core/tcp_content.h | 208 |
3 files changed, 347 insertions, 0 deletions
diff --git a/src/network/core/config.h b/src/network/core/config.h index c41190cce..e2fbb5e3a 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -9,11 +9,14 @@ /** DNS hostname of the masterserver */ #define NETWORK_MASTER_SERVER_HOST "master.openttd.org" +/** DNS hostname of the content server */ +#define NETWORK_CONTENT_SERVER_HOST "content.openttd.org" /** Message sent to the masterserver to 'identify' this client as OpenTTD */ #define NETWORK_MASTER_SERVER_WELCOME_MESSAGE "OpenTTDRegister" enum { NETWORK_MASTER_SERVER_PORT = 3978, ///< The default port of the master server (UDP) + NETWORK_CONTENT_SERVER_PORT = 3978, ///< The default port of the content server (TCP) NETWORK_DEFAULT_PORT = 3979, ///< The default port of the game server (TCP & UDP) NETWORK_DEFAULT_DEBUGLOG_PORT = 3982, ///< The default port debug-log is sent too (TCP) diff --git a/src/network/core/tcp_content.cpp b/src/network/core/tcp_content.cpp new file mode 100644 index 000000000..32054a1e2 --- /dev/null +++ b/src/network/core/tcp_content.cpp @@ -0,0 +1,136 @@ +/* $Id$ */ + +/** + * @file tcp_content.cpp Basic functions to receive and send Content packets. + */ + +#ifdef ENABLE_NETWORK + +#include "../../stdafx.h" +#include "../../debug.h" +#include "tcp_content.h" + +ContentInfo::ContentInfo() +{ + memset(this, 0, sizeof(*this)); +} + +ContentInfo::~ContentInfo() +{ + free(this->dependencies); + free(this->tags); +} + +size_t ContentInfo::Size() const +{ + size_t len = 0; + for (uint i = 0; i < this->tag_count; i++) len += strlen(this->tags[i]) + 1; + + /* The size is never larger than the content info size plus the size of the + * tags and dependencies */ + return sizeof(*this) + + sizeof(this->dependency_count) + + sizeof(*this->dependencies) * this->dependency_count; +} + +bool ContentInfo::IsSelected() const +{ + switch (this->state) { + case ContentInfo::SELECTED: + case ContentInfo::AUTOSELECTED: + return true; + + default: + return false; + } +} + +bool ContentInfo::IsValid() const +{ + return this->state < ContentInfo::INVALID && this->type >= CONTENT_TYPE_BEGIN && this->type < CONTENT_TYPE_END; +} + +NetworkRecvStatus NetworkContentSocketHandler::CloseConnection() +{ + this->has_quit = true; + return NETWORK_RECV_STATUS_OKAY; +} + +void NetworkContentSocketHandler::Close() +{ + CloseConnection(); + if (this->sock == INVALID_SOCKET) return; + + closesocket(this->sock); + this->sock = INVALID_SOCKET; +} + +/** + * Defines a simple (switch) case for each network packet + * @param type the packet type to create the case for + */ +#define CONTENT_COMMAND(type) case type: return this->NetworkPacketReceive_ ## type ## _command(p); break; + +/** + * Handle an incoming packets by sending it to the correct function. + * @param p the received packet + */ +bool NetworkContentSocketHandler::HandlePacket(Packet *p) +{ + PacketContentType type = (PacketContentType)p->Recv_uint8(); + + switch (this->HasClientQuit() ? PACKET_CONTENT_END : type) { + CONTENT_COMMAND(PACKET_CONTENT_CLIENT_INFO_LIST); + CONTENT_COMMAND(PACKET_CONTENT_CLIENT_INFO_ID); + CONTENT_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID); + CONTENT_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); + CONTENT_COMMAND(PACKET_CONTENT_SERVER_INFO); + CONTENT_COMMAND(PACKET_CONTENT_CLIENT_CONTENT); + CONTENT_COMMAND(PACKET_CONTENT_SERVER_CONTENT); + + default: + if (this->HasClientQuit()) { + DEBUG(net, 0, "[tcp/content] received invalid packet type %d from %s:%d", type, inet_ntoa(this->client_addr.sin_addr), ntohs(this->client_addr.sin_port)); + } else { + DEBUG(net, 0, "[tcp/content] received illegal packet from %s:%d", inet_ntoa(this->client_addr.sin_addr), ntohs(this->client_addr.sin_port)); + } + return false; + } +} + +/** + * Receive a packet at UDP level + */ +void NetworkContentSocketHandler::Recv_Packets() +{ + Packet *p; + NetworkRecvStatus res; + while ((p = this->Recv_Packet(&res)) != NULL) { + bool cont = HandlePacket(p); + delete p; + if (!cont) return; + } +} + +/** + * Create stub implementations for all receive commands that only + * show a warning that the given command is not available for the + * socket where the packet came from. + * @param type the packet type to create the stub for + */ +#define DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(type) \ +bool NetworkContentSocketHandler::NetworkPacketReceive_## type ##_command(Packet *p) { \ + DEBUG(net, 0, "[tcp/content] received illegal packet type %d from %s:%d", \ + type, inet_ntoa(this->client_addr.sin_addr), ntohs(this->client_addr.sin_port)); \ + return false; \ +} + +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_LIST); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_ID); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_SERVER_INFO); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_CONTENT); +DEFINE_UNAVAILABLE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_SERVER_CONTENT); + +#endif /* ENABLE_NETWORK */ diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h new file mode 100644 index 000000000..5b7c2b850 --- /dev/null +++ b/src/network/core/tcp_content.h @@ -0,0 +1,208 @@ +/* $Id$ */ + +/** + * @file tcp_content.h Basic functions to receive and send TCP packets to/from the content server. + */ + +#ifndef NETWORK_CORE_CONTENT_H +#define NETWORK_CORE_CONTENT_H + +#ifdef ENABLE_NETWORK + +#include "os_abstraction.h" +#include "tcp.h" +#include "packet.h" +#include "../../debug.h" + +/** The values in the enum are important; they are used as database 'keys' */ +enum ContentType { + CONTENT_TYPE_BEGIN = 1, ///< Helper to mark the begin of the types + CONTENT_TYPE_BASE_GRAPHICS = 1, ///< The content consists of base graphics + CONTENT_TYPE_NEWGRF = 2, ///< The content consists of a NewGRF + CONTENT_TYPE_AI = 3, ///< The content consists of an AI + CONTENT_TYPE_AI_LIBRARY = 4, ///< The content consists of an AI library + CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario + CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap + CONTENT_TYPE_END, ///< Helper to mark the end of the types +}; + +/** Enum with all types of TCP content packets. The order MUST not be changed **/ +enum PacketContentType { + PACKET_CONTENT_CLIENT_INFO_LIST, ///< Queries the content server for a list of info of a given content type + PACKET_CONTENT_CLIENT_INFO_ID, ///< Queries the content server for information about a list of internal IDs + PACKET_CONTENT_CLIENT_INFO_EXTID, ///< Queries the content server for information about a list of external IDs + PACKET_CONTENT_CLIENT_INFO_EXTID_MD5, ///< Queries the content server for information about a list of external IDs and MD5 + PACKET_CONTENT_SERVER_INFO, ///< Reply of content server with information about content + PACKET_CONTENT_CLIENT_CONTENT, ///< Request a content file given an internal ID + PACKET_CONTENT_SERVER_CONTENT, ///< Reply with the content of the given ID + PACKET_CONTENT_END ///< Must ALWAYS be on the end of this list!! (period) +}; + +#define DECLARE_CONTENT_RECEIVE_COMMAND(type) virtual bool NetworkPacketReceive_## type ##_command(Packet *p) +#define DEF_CONTENT_RECEIVE_COMMAND(cls, type) bool cls ##NetworkContentSocketHandler::NetworkPacketReceive_ ## type ## _command(Packet *p) + +enum ContentID { + INVALID_CONTENT_ID = UINT32_MAX +}; + +/** Container for all important information about a piece of content. */ +struct ContentInfo { + enum State { + UNSELECTED, ///< The content has not been selected + SELECTED, ///< The content has been manually selected + AUTOSELECTED, ///< The content has been selected as dependency + ALREADY_HERE, ///< The content is already at the client side + DOES_NOT_EXIST, ///< The content does not exist in the content system + INVALID ///< The content's invalid + }; + + ContentType type; ///< Type of content + ContentID id; ///< Unique (server side) ID for the content + uint32 filesize; ///< Size of the file + char filename[48]; ///< Filename (for the .tar.gz; only valid on download) + char name[32]; ///< Name of the content + char version[16]; ///< Version of the content + char url[64]; ///< URL related to the content + char description[512]; ///< Description of the content + uint32 unique_id; ///< Unique ID; either GRF ID or shortname + byte md5sum[16]; ///< The MD5 checksum + uint8 dependency_count; ///< Number of dependencies + ContentID *dependencies; ///< Malloced array of dependencies (unique server side ids) + uint8 tag_count; ///< Number of tags + char (*tags)[32]; ///< Malloced array of tags (strings) + State state; ///< Whether the content info is selected (for download) + bool update; ///< This item is an update + + /** Clear everything in the struct */ + ContentInfo(); + + /** Free everything allocated */ + ~ContentInfo(); + + /** + * Get the size of the data as send over the network. + * @return the size. + */ + size_t Size() const; + + /** + * Is the state either selected or autoselected? + * @return true iff that's the case + */ + bool IsSelected() const; + + /** + * Is the information from this content info valid? + * @return true iff it's valid + */ + bool IsValid() const; +}; + +/** Base socket handler for all Content TCP sockets */ +class NetworkContentSocketHandler : public NetworkTCPSocketHandler { +protected: + struct sockaddr_in client_addr; ///< The address we're connected to. + NetworkRecvStatus CloseConnection(); + void Close(); + + /** + * Client requesting a list of content info: + * byte type + * uint32 openttd version + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_LIST); + + /** + * Client requesting a list of content info: + * uint16 count of ids + * uint32 id (count times) + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_ID); + + /** + * Client requesting a list of content info based on an external + * 'unique' id; GRF ID for NewGRFS, shortname and for base + * graphics and AIs. + * Scenarios and AI libraries are not supported + * uint8 count of requests + * for each request: + * uint8 type + * unique id (uint32) + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID); + + /** + * Client requesting a list of content info based on an external + * 'unique' id; GRF ID + MD5 checksum for NewGRFS, shortname and + * xor-ed MD5 checsums for base graphics and AIs. + * Scenarios and AI libraries are not supported + * uint8 count of requests + * for each request: + * uint8 type + * unique id (uint32) + * md5 (16 bytes) + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_INFO_EXTID_MD5); + + /** + * Server sending list of content info: + * byte type (invalid ID == does not exist) + * uint32 id + * uint32 file_size + * string name (max 32 characters) + * string version (max 16 characters) + * uint32 unique id + * uint8 md5sum (16 bytes) + * uint8 dependency count + * uint32 unique id of dependency (dependency count times) + * uint8 tag count + * string tag (max 32 characters for tag count times) + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_SERVER_INFO); + + /** + * Client requesting the actual content: + * uint16 count of unique ids + * uint32 unique id (count times) + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_CLIENT_CONTENT); + + /** + * Server sending list of content info: + * uint32 unique id + * uint32 file size (0 == does not exist) + * string file name (max 48 characters) + * After this initial packet, packets with the actual data are send using + * the same packet type. + */ + DECLARE_CONTENT_RECEIVE_COMMAND(PACKET_CONTENT_SERVER_CONTENT); + + /** + * Handle the given packet, i.e. pass it to the right + * parser receive command. + * @param p the packet to handle + * @return true if we should immediatelly handle further packets, false otherwise + */ + bool HandlePacket(Packet *p); +public: + /** + * Create a new cs socket handler for a given cs + * @param s the socket we are connected with + * @param sin IP etc. of the client + */ + NetworkContentSocketHandler(SOCKET s, const struct sockaddr_in *sin) : + NetworkTCPSocketHandler(s), + client_addr(*sin) + { + } + + /** On destructing of this class, the socket needs to be closed */ + virtual ~NetworkContentSocketHandler() { this->Close(); } + + /** Do the actual receiving of packets. */ + void Recv_Packets(); +}; + +#endif /* ENABLE_NETWORK */ + +#endif /* NETWORK_CORE_CONTENT_H */ |