summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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: