diff options
-rw-r--r-- | doc/lua_api.txt | 23 | ||||
-rw-r--r-- | src/client.cpp | 57 | ||||
-rw-r--r-- | src/client.h | 21 | ||||
-rw-r--r-- | src/clientiface.cpp | 46 | ||||
-rw-r--r-- | src/clientiface.h | 166 | ||||
-rw-r--r-- | src/clientserver.h | 18 | ||||
-rw-r--r-- | src/connection.cpp | 4 | ||||
-rw-r--r-- | src/connection.h | 2 | ||||
-rw-r--r-- | src/exceptions.h | 5 | ||||
-rw-r--r-- | src/game.cpp | 4 | ||||
-rw-r--r-- | src/hud.cpp | 6 | ||||
-rw-r--r-- | src/script/lua_api/l_server.cpp | 132 | ||||
-rw-r--r-- | src/script/lua_api/l_server.h | 3 | ||||
-rw-r--r-- | src/server.cpp | 277 | ||||
-rw-r--r-- | 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="<<avg_rtt<<std::endl; } } @@ -643,7 +631,7 @@ void Client::step(float dtime) { float &counter = m_playerpos_send_timer; counter += dtime; - if(counter >= 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<u8> 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)" <<std::endl; @@ -2152,6 +2142,27 @@ void Client::sendRespawn() Send(0, data, true); } +void Client::sendReady() +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOSERVER_CLIENT_READY); + writeU8(os,VERSION_MAJOR); + writeU8(os,VERSION_MINOR); + writeU8(os,VERSION_PATCH_ORIG); + writeU8(os,0); + + writeU16(os,strlen(CMAKE_VERSION_GITHASH)); + os.write(CMAKE_VERSION_GITHASH,strlen(CMAKE_VERSION_GITHASH)); + + // Make data buffer + std::string s = os.str(); + SharedBuffer<u8> 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"<<std::endl; m_mesh_update_thread.Start(); + m_state = LC_Ready; + sendReady(); infostream<<"Client::afterContentReceived() done"<<std::endl; } float Client::getRTT(void) { - try{ - return m_con.GetPeerAvgRTT(PEER_ID_SERVER); - } catch(con::PeerNotFoundException &e){ - return 1337; - } + return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT); } // IGameDef interface diff --git a/src/client.h b/src/client.h index f2e1b86d7..a5fda98d7 100644 --- a/src/client.h +++ b/src/client.h @@ -57,6 +57,12 @@ struct QueuedMeshUpdate ~QueuedMeshUpdate(); }; +enum LocalClientState { + LC_Created, + LC_Init, + LC_Ready +}; + /* A thread-safe queue of mesh update tasks */ @@ -319,14 +325,7 @@ public: calling this, as it is sent in the initialization. */ void connect(Address address); - /* - returns true when - m_con.Connected() == true - AND m_server_ser_ver != SER_FMT_VER_INVALID - throws con::PeerNotFoundException if connection has been deleted, - eg. timed out. - */ - bool connectedAndInitialized(); + /* Stuff that references the environment is valid only as long as this is not called. (eg. Players) @@ -354,6 +353,7 @@ public: void sendDamage(u8 damage); void sendBreath(u16 breath); void sendRespawn(); + void sendReady(); ClientEnvironment& getEnv() { return m_env; } @@ -454,6 +454,8 @@ public: // Send a notification that no conventional media transfer is needed void received_media(); + LocalClientState getState() { return m_state; } + private: // Virtual methods from con::PeerHandler @@ -537,6 +539,9 @@ private: // Storage for mesh data for creating multiple instances of the same mesh std::map<std::string, std::string> 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 <sstream> + #include "clientiface.h" #include "player.h" #include "settings.h" @@ -397,10 +399,11 @@ void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &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<u16, RemoteClient*>::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<u16, RemoteClient*>::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<u8> 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<<wide_to_narrow(error_message)<<std::endl; break; } - if(!client.connectedAndInitialized()){ + if(client.getState() < LC_Init){ error_message = L"Client disconnected"; errorstream<<wide_to_narrow(error_message)<<std::endl; break; diff --git a/src/hud.cpp b/src/hud.cpp index 80112a6ec..f87fdfc14 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -143,7 +143,7 @@ void Hud::drawItem(v2s32 upperleftpos, s32 imgsize, s32 itemcount, steppos = v2s32(padding, -(padding + i * fullimglen)); break; default: - steppos = v2s32(padding + i * fullimglen, padding); + steppos = v2s32(padding + i * fullimglen, padding); } core::rect<s32> 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<RemotePlayer*>(m_env->getPlayer(playername.c_str())); + + // If failed, cancel + if((playersao == NULL) || (player == NULL)) + { + if(player && player->peer_id != 0){ + errorstream<<"Server: "<<playername<<": Failed to emerge player" + <<" (player allocated to an another client)"<<std::endl; + DenyAccess(peer_id, L"Another client is connected with this " + L"name. If your client closed unexpectedly, try again in " + L"a minute."); + } else { + errorstream<<"Server: "<<playername<<": Failed to emerge player" + <<std::endl; + DenyAccess(peer_id, L"Could not allocate player."); + } + return NULL; + } + + /* + Send complete position information + */ + SendMovePlayer(peer_id); + + // Send privileges + SendPlayerPrivileges(peer_id); + + // Send inventory formspec + SendPlayerInventoryFormspec(peer_id); + + // Send inventory + UpdateCrafting(peer_id); + SendInventory(peer_id); + + // Send HP + if(g_settings->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<<player->getName() <<" joins game. " << std::endl; + /* + Print out action + */ + { + std::vector<std::string> names = m_clients.getPlayerNames(); + + actionstream<<player->getName() <<" joins game. List of players: "; + + for (std::vector<std::string>::iterator i = names.begin(); + i != names.end(); i++) + { + actionstream << *i << " "; + } + + actionstream<<std::endl; + } + return playersao; } void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) @@ -1543,6 +1648,21 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_clients.event(peer_id, GotInit2); u16 protocol_version = m_clients.getProtocolVersion(peer_id); + + ///// begin compatibility code + PlayerSAO* playersao = NULL; + if (protocol_version <= 22) { + playersao = StageTwoClientInit(peer_id); + + if (playersao == NULL) { + errorstream + << "TOSERVER_INIT2 stage 2 client init failed for peer " + << peer_id << std::endl; + return; + } + } + ///// end compatibility code + /* Send some initialization data */ @@ -1572,6 +1692,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) float time_speed = g_settings->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<RemotePlayer*>(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: "<<playername<<": Failed to emerge player" - <<" (player allocated to an another client)"<<std::endl; - DenyAccess(peer_id, L"Another client is connected with this " - L"name. If your client closed unexpectedly, try again in " - L"a minute."); - } else { - errorstream<<"Server: "<<playername<<": Failed to emerge player" - <<std::endl; - DenyAccess(peer_id, L"Could not allocate player."); - } + if (playersao == NULL) { + errorstream + << "TOSERVER_CLIENT_READY stage 2 client init failed for peer " + << peer_id << std::endl; return; } - /* - Send complete position information - */ - SendMovePlayer(peer_id); - - // Send privileges - SendPlayerPrivileges(peer_id); - - // Send inventory formspec - SendPlayerInventoryFormspec(peer_id); - - // Send inventory - UpdateCrafting(peer_id); - SendInventory(peer_id); - - // Send HP - if(g_settings->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<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. " << std::endl; - /* - Print out action - */ - { - std::vector<std::string> names = m_clients.getPlayerNames(); - - actionstream<<player->getName()<<" ["<<addr_s<<"] " - <<"joins game. List of players: "; - for (std::vector<std::string>::iterator i = names.begin(); - i != names.end(); i++) - { - actionstream << *i << " "; - } + if(datasize < 2+8) + return; - actionstream<<std::endl; - } + m_clients.setClientVersion( + peer_id, + data[2], data[3], data[4], + std::string((char*) &data[8],(u16) data[6])); - m_clients.event(peer_id,SetMediaSent); + m_clients.event(peer_id, SetClientReady); m_script->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: |