aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsapier <Sapier at GMX dot net>2014-02-13 20:17:42 +0100
committersapier <Sapier at GMX dot net>2014-04-08 21:12:20 +0200
commit142e2d3b74ad886eed83b0fc9d6cfea100dae10a (patch)
tree869bd3599c590e062bc838013b83088280734a81
parent556bdc260a6938ddab8db22e2ebc4033ec3757eb (diff)
downloadminetest-142e2d3b74ad886eed83b0fc9d6cfea100dae10a.tar.gz
minetest-142e2d3b74ad886eed83b0fc9d6cfea100dae10a.tar.bz2
minetest-142e2d3b74ad886eed83b0fc9d6cfea100dae10a.zip
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
-rw-r--r--doc/lua_api.txt23
-rw-r--r--src/client.cpp57
-rw-r--r--src/client.h21
-rw-r--r--src/clientiface.cpp46
-rw-r--r--src/clientiface.h166
-rw-r--r--src/clientserver.h18
-rw-r--r--src/connection.cpp4
-rw-r--r--src/connection.h2
-rw-r--r--src/exceptions.h5
-rw-r--r--src/game.cpp4
-rw-r--r--src/hud.cpp6
-rw-r--r--src/script/lua_api/l_server.cpp132
-rw-r--r--src/script/lua_api/l_server.h3
-rw-r--r--src/server.cpp277
-rw-r--r--src/server.h5
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: