From 142e2d3b74ad886eed83b0fc9d6cfea100dae10a Mon Sep 17 00:00:00 2001 From: sapier Date: Thu, 13 Feb 2014 20:17:42 +0100 Subject: [PATCH] Cleanup client init states by bumping protocol version Don't use TOSERVER_RECEIVED_MEDIA but TOSERVER_CLIENT_READY as indicatio for client ready Handle clients with protocol version < 23 (almost) same way as before Make client tell server about it's version Add client state to not send bogus player position updates prior init complete Add access to statistics information (peer connction time,rtt,version) Fix clients standing stalled in world while preloading item visuals (new clients only) Add get_player_information to read client specific information from lua --- doc/lua_api.txt | 23 +++ src/client.cpp | 57 ++++--- src/client.h | 21 ++- src/clientiface.cpp | 46 ++++-- src/clientiface.h | 166 ++++++++++++++++++- src/clientserver.h | 18 ++- src/connection.cpp | 4 +- src/connection.h | 2 +- src/exceptions.h | 5 + src/game.cpp | 4 +- src/hud.cpp | 6 +- src/script/lua_api/l_server.cpp | 132 ++++++++++++++- src/script/lua_api/l_server.h | 3 + src/server.cpp | 277 +++++++++++++++++++++----------- src/server.h | 5 + 15 files changed, 625 insertions(+), 144 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 713800752..bd060f9f0 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1200,6 +1200,29 @@ minetest.features minetest.has_feature(arg) -> bool, missing_features ^ arg: string or table in format {foo=true, bar=true} ^ missing_features: {foo=true, bar=true} +minetest.get_player_information(playername) +^ table containing information about player peer: +{ + address = "127.0.0.1", -- ip address of client + ip_version = 4, -- IPv4 / IPv6 + min_rtt = 0.01, -- minimum round trip time + max_rtt = 0.2, -- maximum round trip time + avg_rtt = 0.02, -- average round trip time + min_jitter = 0.01, -- minimum packet time jitter + max_jitter = 0.5, -- maximum packet time jitter + avg_jitter = 0.03, -- average packet time jitter + connection_uptime = 200, -- seconds since client connected + + -- following information is available on debug build only!!! + -- DO NOT USE IN MODS + --ser_vers = 26, -- serialization version used by client + --prot_vers = 23, -- protocol version used by client + --major = 0, -- major version number + --minor = 4, -- minor version number + --patch = 10, -- patch version number + --vers_string = "0.4.9-git", -- full version string + --state = "Active" -- current client state +} Logging: minetest.debug(line) diff --git a/src/client.cpp b/src/client.cpp index 654052ac0..5b3ebed66 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "util/serialize.h" #include "config.h" +#include "cmake_config_githash.h" #include "util/directiontables.h" #include "util/pointedthing.h" #include "version.h" @@ -252,7 +253,8 @@ Client::Client( m_last_time_of_day_f(-1), m_time_of_day_update_timer(0), m_recommended_send_interval(0.1), - m_removed_sounds_check_timer(0) + m_removed_sounds_check_timer(0), + m_state(LC_Created) { m_packetcounter_timer = 0.0; //m_delete_unused_sectors_timer = 0.0; @@ -325,17 +327,6 @@ void Client::connect(Address address) m_con.Connect(address); } -bool Client::connectedAndInitialized() -{ - if(m_con.Connected() == false) - return false; - - if(m_server_ser_ver == SER_FMT_VER_INVALID) - return false; - - return true; -} - void Client::step(float dtime) { DSTACK(__FUNCTION_NAME); @@ -372,9 +363,6 @@ void Client::step(float dtime) m_packetcounter.clear(); } } - - // Get connection status - bool connected = connectedAndInitialized(); #if 0 { @@ -467,7 +455,7 @@ void Client::step(float dtime) } #endif - if(connected == false) + if(m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; @@ -632,7 +620,7 @@ void Client::step(float dtime) { counter = 0.0; // connectedAndInitialized() is true, peer exists. - float avg_rtt = m_con.GetPeerAvgRTT(PEER_ID_SERVER); + float avg_rtt = getRTT(); infostream<<"Client: avg_rtt="<= m_recommended_send_interval) + if((m_state == LC_Ready) && (counter >= m_recommended_send_interval)) { counter = 0.0; sendPlayerPos(); @@ -1051,6 +1039,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) // Send as reliable m_con.Send(PEER_ID_SERVER, 1, reply, true); + m_state = LC_Init; + return; } @@ -1937,7 +1927,7 @@ void Client::Send(u16 channelnum, SharedBuffer data, bool reliable) void Client::interact(u8 action, const PointedThing& pointed) { - if(connectedAndInitialized() == false){ + if(m_state != LC_Ready){ infostream<<"Client::interact() " "cancelled (not connected)" < data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(0, data, true); +} + void Client::sendPlayerPos() { LocalPlayer *myplayer = m_env.getLocalPlayer(); @@ -2650,16 +2661,14 @@ void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font) infostream<<"- Starting mesh update thread"< m_mesh_data; + + // own state + LocalClientState m_state; }; #endif // !CLIENT_HEADER diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 5394cd002..626e5da74 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "clientiface.h" #include "player.h" #include "settings.h" @@ -397,10 +399,11 @@ void RemoteClient::SetBlocksNotSent(std::map &blocks) void RemoteClient::notifyEvent(ClientStateEvent event) { + std::ostringstream myerror; switch (m_state) { case Invalid: - assert("State update for client in invalid state" != 0); + //intentionally do nothing break; case Created: @@ -420,7 +423,8 @@ void RemoteClient::notifyEvent(ClientStateEvent event) /* GotInit2 SetDefinitionsSent SetMediaSent */ default: - assert("Invalid client state transition!" == 0); + myerror << "Created: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); } break; @@ -446,7 +450,8 @@ void RemoteClient::notifyEvent(ClientStateEvent event) /* Init SetDefinitionsSent SetMediaSent */ default: - assert("Invalid client state transition!" == 0); + myerror << "InitSent: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); } break; @@ -467,14 +472,15 @@ void RemoteClient::notifyEvent(ClientStateEvent event) /* Init GotInit2 SetMediaSent */ default: - assert("Invalid client state transition!" == 0); + myerror << "InitDone: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); } break; case DefinitionsSent: switch(event) { - case SetMediaSent: + case SetClientReady: m_state = Active; break; @@ -488,7 +494,8 @@ void RemoteClient::notifyEvent(ClientStateEvent event) /* Init GotInit2 SetDefinitionsSent */ default: - assert("Invalid client state transition!" == 0); + myerror << "DefinitionsSent: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); } break; @@ -505,7 +512,8 @@ void RemoteClient::notifyEvent(ClientStateEvent event) /* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */ default: - assert("Invalid client state transition!" == 0); + myerror << "Active: Invalid client state transition! " << event; + throw ClientStateError(myerror.str()); break; } break; @@ -516,6 +524,11 @@ void RemoteClient::notifyEvent(ClientStateEvent event) } } +u32 RemoteClient::uptime() +{ + return getTime(PRECISION_SECONDS) - m_connection_time; +} + ClientInterface::ClientInterface(con::Connection* con) : m_con(con), @@ -749,7 +762,7 @@ void ClientInterface::event(u16 peer_id, ClientStateEvent event) n->second->notifyEvent(event); } - if ((event == SetMediaSent) || (event == Disconnect) || (event == SetDenied)) + if ((event == SetClientReady) || (event == Disconnect) || (event == SetDenied)) { UpdatePlayerList(); } @@ -763,9 +776,24 @@ u16 ClientInterface::getProtocolVersion(u16 peer_id) std::map::iterator n; n = m_clients.find(peer_id); - // No client to deliver event + // No client to get version if (n == m_clients.end()) return 0; return n->second->net_proto_version; } + +void ClientInterface::setClientVersion(u16 peer_id, u8 major, u8 minor, u8 patch, std::string full) +{ + JMutexAutoLock conlock(m_clients_mutex); + + // Error check + std::map::iterator n; + n = m_clients.find(peer_id); + + // No client to set versions + if (n == m_clients.end()) + return; + + n->second->setVersionInfo(major,minor,patch,full); +} diff --git a/src/clientiface.h b/src/clientiface.h index a2315b3bd..13bb45a84 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -34,6 +34,109 @@ class MapBlock; class ServerEnvironment; class EmergeManager; +/* + * State Transitions + + Start + (peer connect) + | + v + /-----------------\ + | | + | Created | + | | + \-----------------/ + | + | ++-----------------------------+ invalid playername, password +|IN: | or denied by mod +| TOSERVER_INIT |------------------------------ ++-----------------------------+ | + | | + | Auth ok | + | | ++-----------------------------+ | +|OUT: | | +| TOCLIENT_INIT | | ++-----------------------------+ | + | | + v | + /-----------------\ | + | | | + | InitSent | | + | | | + \-----------------/ +------------------ + | | | ++-----------------------------+ +-----------------------------+ | +|IN: | |OUT: | | +| TOSERVER_INIT2 | | TOCLIENT_ACCESS_DENIED | | ++-----------------------------+ +-----------------------------+ | + | | | + v v | + /-----------------\ /-----------------\ | + | | | | | + | InitDone | | Denied | | + | | | | | + \-----------------/ \-----------------/ | + | | ++-----------------------------+ | +|OUT: | | +| TOCLIENT_MOVEMENT | | +| TOCLIENT_ITEMDEF | | +| TOCLIENT_NODEDEF | | +| TOCLIENT_ANNOUNCE_MEDIA | | +| TOCLIENT_DETACHED_INVENTORY | | +| TOCLIENT_TIME_OF_DAY | | ++-----------------------------+ | + | | + | | + | ----------------------------------- | + v | | | + /-----------------\ v | + | | +-----------------------------+ | + | DefinitionsSent | |IN: | | + | | | TOSERVER_REQUEST_MEDIA | | + \-----------------/ | TOSERVER_RECEIVED_MEDIA | | + | +-----------------------------+ | + | ^ | | + | ----------------------------------- | + | | ++-----------------------------+ | +|IN: | | +| TOSERVER_CLIENT_READY | | ++-----------------------------+ | + | async | + v mod action | ++-----------------------------+ (ban,kick) | +|OUT: | | +| TOCLIENT_MOVE_PLAYER | | +| TOCLIENT_PRIVILEGES | | +| TOCLIENT_INVENTORY_FORMSPEC | | +| UpdateCrafting | | +| TOCLIENT_INVENTORY | | +| TOCLIENT_HP (opt) | | +| TOCLIENT_BREATH | | +| TOCLIENT_DEATHSCREEN | | ++-----------------------------+ | + | | + v | + /-----------------\ | + | |------------------------------------------------------ + | Active | + | |---------------------------------- + \-----------------/ timeout | + | +-----------------------------+ + | |OUT: | + | | TOCLIENT_DISCONNECT | + | +-----------------------------+ + | | + | v ++-----------------------------+ /-----------------\ +|IN: | | | +| TOSERVER_DISCONNECT |------------------->| Disconnecting | ++-----------------------------+ | | + \-----------------/ +*/ namespace con { class Connection; } @@ -50,13 +153,24 @@ enum ClientState Active }; +static const char** statenames = (const char*[]) { + "Invalid", + "Disconnecting", + "Denied", + "Created", + "InitSent", + "InitDone", + "DefinitionsSent", + "Active" +}; + enum ClientStateEvent { Init, GotInit2, SetDenied, SetDefinitionsSent, - SetMediaSent, + SetClientReady, Disconnect }; @@ -107,7 +221,12 @@ public: m_excess_gotblocks(0), m_nothing_to_send_counter(0), m_nothing_to_send_pause_timer(0.0), - m_name("") + m_name(""), + m_version_major(0), + m_version_minor(0), + m_version_patch(0), + m_full_version("unknown"), + m_connection_time(getTime(PRECISION_SECONDS)) { } ~RemoteClient() @@ -178,6 +297,23 @@ public: void confirmSerializationVersion() { serialization_version = m_pending_serialization_version; } + /* get uptime */ + u32 uptime(); + + + /* set version information */ + void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) { + m_version_major = major; + m_version_minor = minor; + m_version_patch = patch; + m_full_version = full; + } + + /* read version information */ + u8 getMajor() { return m_version_major; } + u8 getMinor() { return m_version_minor; } + u8 getPatch() { return m_version_patch; } + std::string getVersion() { return m_full_version; } private: // Version is stored in here after INIT before INIT2 u8 m_pending_serialization_version; @@ -221,7 +357,25 @@ private: // CPU usage optimization u32 m_nothing_to_send_counter; float m_nothing_to_send_pause_timer; + + /* + name of player using this client + */ std::string m_name; + + /* + client information + */ + u8 m_version_major; + u8 m_version_minor; + u8 m_version_patch; + + std::string m_full_version; + + /* + time this client was created + */ + const u32 m_connection_time; }; class ClientInterface { @@ -268,6 +422,9 @@ public: /* get protocol version of client */ u16 getProtocolVersion(u16 peer_id); + /* set client version */ + void setClientVersion(u16 peer_id, u8 major, u8 minor, u8 patch, std::string full); + /* event to update client state */ void event(u16 peer_id, ClientStateEvent event); @@ -275,6 +432,11 @@ public: void setEnv(ServerEnvironment* env) { assert(m_env == 0); m_env = env; } + static std::string state2Name(ClientState state) { + assert(state < sizeof(statenames)); + return statenames[state]; + } + protected: //TODO find way to avoid this functions void Lock() diff --git a/src/clientserver.h b/src/clientserver.h index d1e250ea8..5c5418632 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -100,9 +100,13 @@ with this program; if not, write to the Free Software Foundation, Inc., version, heat and humidity transfer in MapBock automatic_face_movement_dir and automatic_face_movement_dir_offset added to object properties + PROTOCOL_VERSION 22: + add swap_node + PROTOCOL_VERSION 23: + TOSERVER_CLIENT_READY */ -#define LATEST_PROTOCOL_VERSION 22 +#define LATEST_PROTOCOL_VERSION 23 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 @@ -129,7 +133,7 @@ enum ToClientCommand [0] u16 TOSERVER_INIT [2] u8 deployed version - [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd + [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd [12] u64 map seed (new as of 2011-02-27) [20] f1000 recommended send interval (in seconds) (new as of 14) @@ -755,6 +759,16 @@ enum ToServerCommand u16 command u16 breath */ + + TOSERVER_CLIENT_READY = 0x43, + /* + u8 major + u8 minor + u8 patch + u8 reserved + u16 len + u8[len] full_version_string + */ }; #endif diff --git a/src/connection.cpp b/src/connection.cpp index 8a23f67c3..290e2cb21 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -2875,11 +2875,11 @@ Address Connection::GetPeerAddress(u16 peer_id) return peer_address; } -float Connection::GetPeerAvgRTT(u16 peer_id) +float Connection::getPeerStat(u16 peer_id, rtt_stat_type type) { PeerHelper peer = getPeerNoEx(peer_id); if (!peer) return -1; - return peer->getStat(AVG_RTT); + return peer->getStat(type); } u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd) diff --git a/src/connection.h b/src/connection.h index 9d646f499..0f936eb31 100644 --- a/src/connection.h +++ b/src/connection.h @@ -1004,7 +1004,7 @@ public: void Send(u16 peer_id, u8 channelnum, SharedBuffer data, bool reliable); u16 GetPeerID(){ return m_peer_id; } Address GetPeerAddress(u16 peer_id); - float GetPeerAvgRTT(u16 peer_id); + float getPeerStat(u16 peer_id, rtt_stat_type type); const u32 GetProtocolID() const { return m_protocol_id; }; const std::string getDesc(); void DisconnectPeer(u16 peer_id); diff --git a/src/exceptions.h b/src/exceptions.h index 970c68e40..6d6ad333a 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -116,6 +116,11 @@ public: FatalSystemException(const std::string &s): BaseException(s) {} }; +class ClientStateError : public BaseException { +public: + ClientStateError(std::string s): BaseException(s) {} +}; + /* Some "old-style" interrupts: */ diff --git a/src/game.cpp b/src/game.cpp index 699314d30..7d881fa88 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1266,7 +1266,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input, server->step(dtime); // End condition - if(client.connectedAndInitialized()){ + if(client.getState() == LC_Init){ could_connect = true; break; } @@ -1373,7 +1373,7 @@ void the_game(bool &kill, bool random_input, InputHandler *input, errorstream< rect = imgrect + pos + steppos; @@ -334,7 +334,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s steppos = v2s32(0, -1); break; default: - steppos = v2s32(1, 0); + steppos = v2s32(1, 0); } steppos.X *= srcd.Width; steppos.Y *= srcd.Height; @@ -363,7 +363,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture, s void Hud::drawHotbar(v2s32 centerlowerpos, s32 halfheartcount, u16 playeritem, s32 breath) { InventoryList *mainlist = inventory->getList("main"); if (mainlist == NULL) { - errorstream << "draw_hotbar(): mainlist == NULL" << std::endl; + //silently ignore this we may not be initialized completely return; } diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index bbf5a707d..531d044ef 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -99,7 +99,7 @@ int ModApiServer::l_get_player_ip(lua_State *L) } try { - Address addr = getServer(L)->getPeerAddress(getEnv(L)->getPlayer(name)->peer_id); + Address addr = getServer(L)->getPeerAddress(player->peer_id); std::string ip_str = addr.serializeString(); lua_pushstring(L, ip_str.c_str()); return 1; @@ -112,6 +112,135 @@ int ModApiServer::l_get_player_ip(lua_State *L) } } +// get_player_information() +int ModApiServer::l_get_player_information(lua_State *L) +{ + + NO_MAP_LOCK_REQUIRED; + const char * name = luaL_checkstring(L, 1); + Player *player = getEnv(L)->getPlayer(name); + if(player == NULL) + { + lua_pushnil(L); // no such player + return 1; + } + + Address addr; + try + { + addr = getServer(L)->getPeerAddress(player->peer_id); + } + catch(con::PeerNotFoundException) // unlikely + { + dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; + } + + float min_rtt,max_rtt,avg_rtt,min_jitter,max_jitter,avg_jitter; + ClientState state; + u32 uptime; + u16 prot_vers; + u8 ser_vers,major,minor,patch; + std::string vers_string; + +#define ERET(code) \ + if (!(code)) { \ + dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; \ + lua_pushnil(L); /* error */ \ + return 1; \ + } + + ERET(getServer(L)->getClientConInfo(player->peer_id,con::MIN_RTT,&min_rtt)) + ERET(getServer(L)->getClientConInfo(player->peer_id,con::MAX_RTT,&max_rtt)) + ERET(getServer(L)->getClientConInfo(player->peer_id,con::AVG_RTT,&avg_rtt)) + ERET(getServer(L)->getClientConInfo(player->peer_id,con::MIN_JITTER,&min_jitter)) + ERET(getServer(L)->getClientConInfo(player->peer_id,con::MAX_JITTER,&max_jitter)) + ERET(getServer(L)->getClientConInfo(player->peer_id,con::AVG_JITTER,&avg_jitter)) + + ERET(getServer(L)->getClientInfo(player->peer_id, + &state, &uptime, &ser_vers, &prot_vers, + &major, &minor, &patch, &vers_string)) + + lua_newtable(L); + int table = lua_gettop(L); + + lua_pushstring(L,"address"); + lua_pushstring(L, addr.serializeString().c_str()); + lua_settable(L, table); + + lua_pushstring(L,"ip_version"); + if (addr.getFamily() == AF_INET) { + lua_pushnumber(L, 4); + } else if (addr.getFamily() == AF_INET6) { + lua_pushnumber(L, 6); + } else { + lua_pushnumber(L, 0); + } + lua_settable(L, table); + + lua_pushstring(L,"min_rtt"); + lua_pushnumber(L, min_rtt); + lua_settable(L, table); + + lua_pushstring(L,"max_rtt"); + lua_pushnumber(L, max_rtt); + lua_settable(L, table); + + lua_pushstring(L,"avg_rtt"); + lua_pushnumber(L, avg_rtt); + lua_settable(L, table); + + lua_pushstring(L,"min_jitter"); + lua_pushnumber(L, min_jitter); + lua_settable(L, table); + + lua_pushstring(L,"max_jitter"); + lua_pushnumber(L, max_jitter); + lua_settable(L, table); + + lua_pushstring(L,"avg_jitter"); + lua_pushnumber(L, avg_jitter); + lua_settable(L, table); + + lua_pushstring(L,"connection_uptime"); + lua_pushnumber(L, uptime); + lua_settable(L, table); + +#ifndef NDEBUG + lua_pushstring(L,"serialization_version"); + lua_pushnumber(L, ser_vers); + lua_settable(L, table); + + lua_pushstring(L,"protocol_version"); + lua_pushnumber(L, prot_vers); + lua_settable(L, table); + + lua_pushstring(L,"major"); + lua_pushnumber(L, major); + lua_settable(L, table); + + lua_pushstring(L,"minor"); + lua_pushnumber(L, minor); + lua_settable(L, table); + + lua_pushstring(L,"patch"); + lua_pushnumber(L, patch); + lua_settable(L, table); + + lua_pushstring(L,"version_string"); + lua_pushstring(L, vers_string.c_str()); + lua_settable(L, table); + + lua_pushstring(L,"state"); + lua_pushstring(L,ClientInterface::state2Name(state).c_str()); + lua_settable(L, table); +#endif + +#undef ERET + return 1; +} + // get_ban_list() int ModApiServer::l_get_ban_list(lua_State *L) { @@ -343,6 +472,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(sound_play); API_FCT(sound_stop); + API_FCT(get_player_information); API_FCT(get_player_privs); API_FCT(get_player_ip); API_FCT(get_ban_list); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 0d0aa45c8..4101f2856 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -67,6 +67,9 @@ private: // get_player_ip() static int l_get_player_ip(lua_State *L); + // get_player_information() + static int l_get_player_information(lua_State *L); + // get_ban_list() static int l_get_ban_list(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index ba7ac1ece..ebb76b087 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1191,6 +1191,111 @@ void Server::Receive() m_env->removePlayer(peer_id);*/ } + catch(ClientStateError &e) + { + errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl; + DenyAccess(peer_id, L"Your client sent something server didn't expect." + L"Try reconnecting or updating your client"); + } +} + +PlayerSAO* Server::StageTwoClientInit(u16 peer_id) +{ + std::string playername = ""; + PlayerSAO *playersao = NULL; + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,InitDone); + if (client != NULL) { + playername = client->getName(); + playersao = emergePlayer(playername.c_str(), peer_id); + } + m_clients.Unlock(); + + RemotePlayer *player = + static_cast(m_env->getPlayer(playername.c_str())); + + // If failed, cancel + if((playersao == NULL) || (player == NULL)) + { + if(player && player->peer_id != 0){ + errorstream<<"Server: "<getBool("enable_damage")) + SendPlayerHP(peer_id); + + // Send Breath + SendPlayerBreath(peer_id); + + // Show death screen if necessary + if(player->hp == 0) + SendDeathscreen(peer_id, false, v3f(0,0,0)); + + // Note things in chat if not in simple singleplayer mode + if(!m_simple_singleplayer_mode) + { + // Send information about server to player in chat + SendChatMessage(peer_id, getStatusString()); + + // Send information about joining in chat + { + std::wstring name = L"unknown"; + Player *player = m_env->getPlayer(peer_id); + if(player != NULL) + name = narrow_to_wide(player->getName()); + + std::wstring message; + message += L"*** "; + message += name; + message += L" joined the game."; + SendChatMessage(PEER_ID_INEXISTENT,message); + } + } + + actionstream<getName() <<" joins game. " << std::endl; + /* + Print out action + */ + { + std::vector names = m_clients.getPlayerNames(); + + actionstream<getName() <<" joins game. List of players: "; + + for (std::vector::iterator i = names.begin(); + i != names.end(); i++) + { + actionstream << *i << " "; + } + + actionstream<getFloat("time_speed"); SendTimeOfDay(peer_id, time, time_speed); + ///// begin compatibility code + if (protocol_version <= 22) { + m_clients.event(peer_id, SetClientReady); + m_script->on_joinplayer(playersao); + } + ///// end compatibility code + // Warnings about protocol version can be issued here if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION) { @@ -1583,6 +1710,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version; + u16 peer_proto_ver = getClient(peer_id,InitDone)->net_proto_version; if(peer_ser_ver == SER_FMT_VER_INVALID) { @@ -1615,105 +1743,34 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } else if(command == TOSERVER_RECEIVED_MEDIA) { - std::string playername = ""; - PlayerSAO *playersao = NULL; - m_clients.Lock(); - RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,DefinitionsSent); - if (client != NULL) { - playername = client->getName(); - playersao = emergePlayer(playername.c_str(), peer_id); - } - m_clients.Unlock(); + return; + } + else if(command == TOSERVER_CLIENT_READY) { + // clients <= protocol version 22 did not send ready message, + // they're already initialized + assert(peer_proto_ver > 22); - RemotePlayer *player = - static_cast(m_env->getPlayer(playername.c_str())); + PlayerSAO* playersao = StageTwoClientInit(peer_id); - // If failed, cancel - if((playersao == NULL) || (player == NULL)) - { - if(player && player->peer_id != 0){ - errorstream<<"Server: "<getBool("enable_damage")) - SendPlayerHP(peer_id); - - // Send Breath - SendPlayerBreath(peer_id); - - // Show death screen if necessary - if(player->hp == 0) - SendDeathscreen(peer_id, false, v3f(0,0,0)); - - // Note things in chat if not in simple singleplayer mode - if(!m_simple_singleplayer_mode) - { - // Send information about server to player in chat - SendChatMessage(peer_id, getStatusString()); - - // Send information about joining in chat - { - std::wstring name = L"unknown"; - Player *player = m_env->getPlayer(peer_id); - if(player != NULL) - name = narrow_to_wide(player->getName()); - - std::wstring message; - message += L"*** "; - message += name; - message += L" joined the game."; - SendChatMessage(PEER_ID_INEXISTENT,message); - } - } - - actionstream<getName()<<" ["< names = m_clients.getPlayerNames(); - - actionstream<getName()<<" ["<::iterator i = names.begin(); - i != names.end(); i++) - { - actionstream << *i << " "; - } - - actionstream<on_joinplayer(playersao); - return; + } else if(command == TOSERVER_GOTBLOCKS) { @@ -2809,6 +2866,46 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) m_peer_change_queue.push_back(c); } +bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval) +{ + *retval = m_con.getPeerStat(peer_id,type); + if (*retval == -1) return false; + return true; +} + +bool Server::getClientInfo( + u16 peer_id, + ClientState* state, + u32* uptime, + u8* ser_vers, + u16* prot_vers, + u8* major, + u8* minor, + u8* patch, + std::string* vers_string + ) +{ + *state = m_clients.getClientState(peer_id); + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,Invalid); + + if (client == NULL) + return false; + + *uptime = client->uptime(); + *ser_vers = client->serialization_version; + *prot_vers = client->net_proto_version; + + *major = client->getMajor(); + *minor = client->getMinor(); + *patch = client->getPatch(); + *vers_string = client->getPatch(); + + m_clients.Unlock(); + + return true; +} + void Server::handlePeerChanges() { while(m_peer_change_queue.size() > 0) diff --git a/src/server.h b/src/server.h index 7813eabbd..c0928e574 100644 --- a/src/server.h +++ b/src/server.h @@ -185,6 +185,7 @@ public: // This is run by ServerThread and does the actual processing void AsyncRunStep(bool initial_step=false); void Receive(); + PlayerSAO* StageTwoClientInit(u16 peer_id); void ProcessData(u8 *data, u32 datasize, u16 peer_id); // Environment must be locked when called @@ -331,6 +332,10 @@ public: void deletingPeer(con::Peer *peer, bool timeout); void DenyAccess(u16 peer_id, const std::wstring &reason); + bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval); + bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime, + u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch, + std::string* vers_string); private: