2010-11-29 19:13:04 +01:00
|
|
|
/*
|
2013-02-24 18:40:43 +01:00
|
|
|
Minetest
|
2013-02-24 19:38:45 +01:00
|
|
|
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
2010-11-29 19:13:04 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
2012-06-05 16:56:56 +02:00
|
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2010-11-29 19:13:04 +01:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2012-06-05 16:56:56 +02:00
|
|
|
GNU Lesser General Public License for more details.
|
2010-11-29 19:13:04 +01:00
|
|
|
|
2012-06-05 16:56:56 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
2010-11-29 19:13:04 +01:00
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
#include <iostream>
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
#include <algorithm>
|
2014-09-12 00:22:05 +02:00
|
|
|
#include <sstream>
|
|
|
|
#include <IFileSystem.h>
|
2013-09-16 05:00:01 +02:00
|
|
|
#include "jthread/jmutexautolock.h"
|
2014-09-12 00:22:05 +02:00
|
|
|
#include "util/directiontables.h"
|
|
|
|
#include "util/pointedthing.h"
|
|
|
|
#include "util/serialize.h"
|
|
|
|
#include "util/string.h"
|
|
|
|
#include "strfnd.h"
|
|
|
|
#include "client.h"
|
|
|
|
#include "clientserver.h"
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "main.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "filesys.h"
|
2010-12-22 02:33:58 +01:00
|
|
|
#include "porting.h"
|
2011-06-25 23:03:58 +02:00
|
|
|
#include "mapsector.h"
|
2011-06-26 01:34:36 +02:00
|
|
|
#include "mapblock_mesh.h"
|
|
|
|
#include "mapblock.h"
|
2011-10-12 12:53:38 +02:00
|
|
|
#include "settings.h"
|
|
|
|
#include "profiler.h"
|
2013-05-11 16:02:41 +02:00
|
|
|
#include "gettext.h"
|
2011-10-16 13:57:53 +02:00
|
|
|
#include "log.h"
|
2011-11-13 11:54:33 +01:00
|
|
|
#include "nodemetadata.h"
|
2011-11-14 20:41:30 +01:00
|
|
|
#include "nodedef.h"
|
2012-01-12 06:10:39 +01:00
|
|
|
#include "itemdef.h"
|
2012-03-19 02:59:12 +01:00
|
|
|
#include "shader.h"
|
2012-01-02 12:31:50 +01:00
|
|
|
#include "base64.h"
|
2012-03-15 23:25:18 +01:00
|
|
|
#include "clientmap.h"
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
#include "clientmedia.h"
|
2012-03-23 11:05:17 +01:00
|
|
|
#include "sound.h"
|
2012-10-24 21:10:05 +02:00
|
|
|
#include "IMeshCache.h"
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
#include "serialization.h"
|
2012-12-14 12:30:17 +01:00
|
|
|
#include "config.h"
|
2013-11-05 13:57:43 +01:00
|
|
|
#include "version.h"
|
2014-04-27 16:09:21 +02:00
|
|
|
#include "drawscene.h"
|
2014-10-09 14:02:02 +02:00
|
|
|
#include "subgame.h"
|
|
|
|
#include "server.h"
|
|
|
|
#include "database.h"
|
|
|
|
#include "database-sqlite3.h"
|
2014-04-27 16:09:21 +02:00
|
|
|
|
|
|
|
extern gui::IGUIEnvironment* guienv;
|
2012-12-14 12:30:17 +01:00
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
/*
|
|
|
|
QueuedMeshUpdate
|
|
|
|
*/
|
|
|
|
|
|
|
|
QueuedMeshUpdate::QueuedMeshUpdate():
|
|
|
|
p(-1337,-1337,-1337),
|
|
|
|
data(NULL),
|
|
|
|
ack_block_to_server(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QueuedMeshUpdate::~QueuedMeshUpdate()
|
|
|
|
{
|
|
|
|
if(data)
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
MeshUpdateQueue
|
|
|
|
*/
|
|
|
|
|
|
|
|
MeshUpdateQueue::MeshUpdateQueue()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MeshUpdateQueue::~MeshUpdateQueue()
|
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_mutex);
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
for(std::vector<QueuedMeshUpdate*>::iterator
|
|
|
|
i = m_queue.begin();
|
|
|
|
i != m_queue.end(); i++)
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
|
|
|
QueuedMeshUpdate *q = *i;
|
|
|
|
delete q;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
peer_id=0 adds with nobody to send to
|
|
|
|
*/
|
2012-03-13 18:56:12 +01:00
|
|
|
void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
|
|
|
|
assert(data);
|
|
|
|
|
|
|
|
JMutexAutoLock lock(m_mutex);
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
if(urgent)
|
|
|
|
m_urgents.insert(p);
|
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
/*
|
|
|
|
Find if block is already in queue.
|
|
|
|
If it is, update the data and quit.
|
|
|
|
*/
|
2012-03-13 18:56:12 +01:00
|
|
|
for(std::vector<QueuedMeshUpdate*>::iterator
|
|
|
|
i = m_queue.begin();
|
|
|
|
i != m_queue.end(); i++)
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
|
|
|
QueuedMeshUpdate *q = *i;
|
|
|
|
if(q->p == p)
|
|
|
|
{
|
|
|
|
if(q->data)
|
|
|
|
delete q->data;
|
|
|
|
q->data = data;
|
|
|
|
if(ack_block_to_server)
|
|
|
|
q->ack_block_to_server = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add the block
|
|
|
|
*/
|
|
|
|
QueuedMeshUpdate *q = new QueuedMeshUpdate;
|
|
|
|
q->p = p;
|
|
|
|
q->data = data;
|
|
|
|
q->ack_block_to_server = ack_block_to_server;
|
|
|
|
m_queue.push_back(q);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returned pointer must be deleted
|
|
|
|
// Returns NULL if queue is empty
|
|
|
|
QueuedMeshUpdate * MeshUpdateQueue::pop()
|
|
|
|
{
|
|
|
|
JMutexAutoLock lock(m_mutex);
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
bool must_be_urgent = !m_urgents.empty();
|
|
|
|
for(std::vector<QueuedMeshUpdate*>::iterator
|
|
|
|
i = m_queue.begin();
|
|
|
|
i != m_queue.end(); i++)
|
|
|
|
{
|
|
|
|
QueuedMeshUpdate *q = *i;
|
|
|
|
if(must_be_urgent && m_urgents.count(q->p) == 0)
|
|
|
|
continue;
|
|
|
|
m_queue.erase(i);
|
|
|
|
m_urgents.erase(q->p);
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
return NULL;
|
2011-06-26 01:34:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
MeshUpdateThread
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
void * MeshUpdateThread::Thread()
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
ThreadStarted();
|
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
log_register_thread("MeshUpdateThread");
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
DSTACK(__FUNCTION_NAME);
|
2010-12-27 13:34:17 +01:00
|
|
|
|
|
|
|
BEGIN_DEBUG_EXCEPTION_HANDLER
|
2011-04-04 01:05:12 +02:00
|
|
|
|
2014-04-05 15:27:33 +02:00
|
|
|
porting::setThreadName("MeshUpdateThread");
|
|
|
|
|
2013-12-03 23:32:03 +01:00
|
|
|
while(!StopRequested())
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-04-04 01:05:12 +02:00
|
|
|
QueuedMeshUpdate *q = m_queue_in.pop();
|
|
|
|
if(q == NULL)
|
|
|
|
{
|
2011-06-18 21:31:24 +02:00
|
|
|
sleep_ms(3);
|
2011-04-04 01:05:12 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-10-19 01:17:23 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Client: Mesh making");
|
2011-06-18 21:31:24 +02:00
|
|
|
|
2014-01-26 11:40:21 +01:00
|
|
|
MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
|
2012-03-13 18:56:12 +01:00
|
|
|
if(mesh_new->getMesh()->getMeshBufferCount() == 0)
|
|
|
|
{
|
|
|
|
delete mesh_new;
|
|
|
|
mesh_new = NULL;
|
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
|
|
|
|
MeshUpdateResult r;
|
|
|
|
r.p = q->p;
|
|
|
|
r.mesh = mesh_new;
|
|
|
|
r.ack_block_to_server = q->ack_block_to_server;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
m_queue_out.push_back(r);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
delete q;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2010-12-27 13:34:17 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
END_DEBUG_EXCEPTION_HANDLER(errorstream)
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
/*
|
|
|
|
Client
|
|
|
|
*/
|
2012-12-14 12:30:17 +01:00
|
|
|
|
2010-12-19 18:11:05 +01:00
|
|
|
Client::Client(
|
|
|
|
IrrlichtDevice *device,
|
|
|
|
const char *playername,
|
2011-05-20 21:28:03 +02:00
|
|
|
std::string password,
|
2014-11-24 04:52:27 +01:00
|
|
|
bool is_simple_singleplayer_game,
|
2011-11-13 23:19:48 +01:00
|
|
|
MapDrawControl &control,
|
2011-11-14 20:41:30 +01:00
|
|
|
IWritableTextureSource *tsrc,
|
2012-03-19 02:59:12 +01:00
|
|
|
IWritableShaderSource *shsrc,
|
2012-01-12 06:10:39 +01:00
|
|
|
IWritableItemDefManager *itemdef,
|
2012-03-23 14:29:30 +01:00
|
|
|
IWritableNodeDefManager *nodedef,
|
2012-03-23 19:23:03 +01:00
|
|
|
ISoundManager *sound,
|
2013-06-23 09:31:22 +02:00
|
|
|
MtEventManager *event,
|
|
|
|
bool ipv6
|
2011-11-14 20:41:30 +01:00
|
|
|
):
|
2014-05-02 00:18:25 +02:00
|
|
|
m_packetcounter_timer(0.0),
|
|
|
|
m_connection_reinit_timer(0.1),
|
|
|
|
m_avg_rtt_timer(0.0),
|
|
|
|
m_playerpos_send_timer(0.0),
|
|
|
|
m_ignore_damage_timer(0.0),
|
2011-11-13 23:19:48 +01:00
|
|
|
m_tsrc(tsrc),
|
2012-03-19 02:59:12 +01:00
|
|
|
m_shsrc(shsrc),
|
2012-01-12 06:10:39 +01:00
|
|
|
m_itemdef(itemdef),
|
2011-11-14 20:41:30 +01:00
|
|
|
m_nodedef(nodedef),
|
2012-03-23 14:29:30 +01:00
|
|
|
m_sound(sound),
|
2012-03-23 19:23:03 +01:00
|
|
|
m_event(event),
|
2011-11-14 20:41:30 +01:00
|
|
|
m_mesh_update_thread(this),
|
2011-02-20 23:45:14 +01:00
|
|
|
m_env(
|
2011-11-13 23:19:48 +01:00
|
|
|
new ClientMap(this, this, control,
|
2010-11-27 00:02:21 +01:00
|
|
|
device->getSceneManager()->getRootSceneNode(),
|
|
|
|
device->getSceneManager(), 666),
|
2011-11-13 23:19:48 +01:00
|
|
|
device->getSceneManager(),
|
2011-12-01 17:23:58 +01:00
|
|
|
tsrc, this, device
|
2011-02-20 23:45:14 +01:00
|
|
|
),
|
2013-06-23 09:31:22 +02:00
|
|
|
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
|
2010-11-27 00:02:21 +01:00
|
|
|
m_device(device),
|
|
|
|
m_server_ser_ver(SER_FMT_VER_INVALID),
|
2011-11-29 16:15:18 +01:00
|
|
|
m_playeritem(0),
|
2010-12-18 16:46:00 +01:00
|
|
|
m_inventory_updated(false),
|
2012-01-22 00:49:02 +01:00
|
|
|
m_inventory_from_server(NULL),
|
|
|
|
m_inventory_from_server_age(0.0),
|
2014-12-01 13:05:36 +01:00
|
|
|
m_show_highlighted(false),
|
2012-03-13 18:56:12 +01:00
|
|
|
m_animation_time(0),
|
|
|
|
m_crack_level(-1),
|
|
|
|
m_crack_pos(0,0,0),
|
2014-12-01 13:05:36 +01:00
|
|
|
m_highlighted_pos(0,0,0),
|
2011-05-20 21:28:03 +02:00
|
|
|
m_map_seed(0),
|
|
|
|
m_password(password),
|
2011-11-15 22:58:56 +01:00
|
|
|
m_access_denied(false),
|
2012-01-12 06:10:39 +01:00
|
|
|
m_itemdef_received(false),
|
2012-03-16 15:34:30 +01:00
|
|
|
m_nodedef_received(false),
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
m_media_downloader(new ClientMediaDownloader()),
|
2012-03-16 15:34:30 +01:00
|
|
|
m_time_of_day_set(false),
|
|
|
|
m_last_time_of_day_f(-1),
|
2012-03-24 18:01:26 +01:00
|
|
|
m_time_of_day_update_timer(0),
|
2012-11-26 21:31:21 +01:00
|
|
|
m_recommended_send_interval(0.1),
|
2014-02-13 20:17:42 +01:00
|
|
|
m_removed_sounds_check_timer(0),
|
|
|
|
m_state(LC_Created)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
/*
|
|
|
|
Add local player
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2014-08-03 22:19:07 +02:00
|
|
|
Player *player = new LocalPlayer(this, playername);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
m_env.addPlayer(player);
|
|
|
|
}
|
2014-10-09 14:02:02 +02:00
|
|
|
|
2014-11-24 04:52:27 +01:00
|
|
|
if (g_settings->getBool("enable_local_map_saving")
|
|
|
|
&& !is_simple_singleplayer_game) {
|
2014-10-09 14:02:02 +02:00
|
|
|
const std::string world_path = porting::path_user + DIR_DELIM + "worlds"
|
|
|
|
+ DIR_DELIM + "server_" + g_settings->get("address")
|
|
|
|
+ "_" + g_settings->get("remote_port");
|
|
|
|
|
|
|
|
SubgameSpec gamespec;
|
|
|
|
if (!getWorldExists(world_path)) {
|
|
|
|
gamespec = findSubgame(g_settings->get("default_game"));
|
|
|
|
if (!gamespec.isValid())
|
|
|
|
gamespec = findSubgame("minimal");
|
|
|
|
} else {
|
|
|
|
std::string world_gameid = getWorldGameId(world_path, false);
|
|
|
|
gamespec = findWorldSubgame(world_path);
|
|
|
|
}
|
|
|
|
if (!gamespec.isValid()) {
|
|
|
|
errorstream << "Couldn't find subgame for local map saving." << std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
localserver = new Server(world_path, gamespec, false, false);
|
|
|
|
localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
|
|
|
|
localdb->beginSave();
|
|
|
|
actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
|
|
|
|
} else {
|
|
|
|
localdb = NULL;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2014-01-06 12:45:42 +01:00
|
|
|
void Client::Stop()
|
|
|
|
{
|
|
|
|
//request all client managed threads to stop
|
|
|
|
m_mesh_update_thread.Stop();
|
2014-10-09 14:02:02 +02:00
|
|
|
if (localdb != NULL) {
|
|
|
|
actionstream << "Local map saving ended" << std::endl;
|
|
|
|
localdb->endSave();
|
|
|
|
}
|
2014-01-06 12:45:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Client::isShutdown()
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!m_mesh_update_thread.IsRunning()) return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
Client::~Client()
|
|
|
|
{
|
2014-02-05 00:17:57 +01:00
|
|
|
m_con.Disconnect();
|
2010-12-24 16:08:50 +01:00
|
|
|
|
2013-12-03 23:32:03 +01:00
|
|
|
m_mesh_update_thread.Stop();
|
|
|
|
m_mesh_update_thread.Wait();
|
2013-05-18 01:52:18 +02:00
|
|
|
while(!m_mesh_update_thread.m_queue_out.empty()) {
|
2014-01-06 12:45:42 +01:00
|
|
|
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
|
2013-05-18 01:52:18 +02:00
|
|
|
delete r.mesh;
|
|
|
|
}
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
|
|
|
|
delete m_inventory_from_server;
|
2012-07-24 19:57:17 +02:00
|
|
|
|
|
|
|
// Delete detached inventories
|
2014-02-05 00:17:57 +01:00
|
|
|
for(std::map<std::string, Inventory*>::iterator
|
|
|
|
i = m_detached_inventories.begin();
|
|
|
|
i != m_detached_inventories.end(); i++){
|
|
|
|
delete i->second;
|
2012-07-24 19:57:17 +02:00
|
|
|
}
|
2012-12-17 17:56:59 +01:00
|
|
|
|
2013-04-07 20:13:21 +02:00
|
|
|
// cleanup 3d model meshes on client shutdown
|
|
|
|
while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
|
|
|
|
scene::IAnimatedMesh * mesh =
|
|
|
|
m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
|
|
|
|
|
|
|
|
if (mesh != NULL)
|
|
|
|
m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Client::connect(Address address)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2011-10-20 22:04:09 +02:00
|
|
|
m_con.SetTimeoutMs(0);
|
2010-11-27 00:02:21 +01:00
|
|
|
m_con.Connect(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::step(float dtime)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2014-05-02 00:18:25 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Limit a bit
|
|
|
|
if(dtime > 2.0)
|
|
|
|
dtime = 2.0;
|
2014-05-02 00:18:25 +02:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
if(m_ignore_damage_timer > dtime)
|
|
|
|
m_ignore_damage_timer -= dtime;
|
|
|
|
else
|
|
|
|
m_ignore_damage_timer = 0.0;
|
2010-12-18 16:46:00 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
m_animation_time += dtime;
|
|
|
|
if(m_animation_time > 60.0)
|
|
|
|
m_animation_time -= 60.0;
|
|
|
|
|
2012-03-16 15:34:30 +01:00
|
|
|
m_time_of_day_update_timer += dtime;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
ReceiveAll();
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2010-11-27 16:18:34 +01:00
|
|
|
/*
|
|
|
|
Packet counter
|
|
|
|
*/
|
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_packetcounter_timer;
|
2010-11-27 16:18:34 +01:00
|
|
|
counter -= dtime;
|
|
|
|
if(counter <= 0.0)
|
|
|
|
{
|
2010-11-29 11:16:17 +01:00
|
|
|
counter = 20.0;
|
2010-11-27 16:18:34 +01:00
|
|
|
|
2014-05-02 00:18:25 +02:00
|
|
|
infostream << "Client packetcounter (" << m_packetcounter_timer
|
|
|
|
<< "):"<<std::endl;
|
2011-10-16 13:57:53 +02:00
|
|
|
m_packetcounter.print(infostream);
|
2010-11-27 16:18:34 +01:00
|
|
|
m_packetcounter.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
#if 0
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Delete unused sectors
|
2010-11-29 09:52:07 +01:00
|
|
|
|
|
|
|
NOTE: This jams the game for a while because deleting sectors
|
|
|
|
clear caches
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
|
|
|
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_delete_unused_sectors_timer;
|
2010-11-27 00:02:21 +01:00
|
|
|
counter -= dtime;
|
|
|
|
if(counter <= 0.0)
|
|
|
|
{
|
2010-11-29 09:52:07 +01:00
|
|
|
// 3 minute interval
|
2010-12-19 15:51:45 +01:00
|
|
|
//counter = 180.0;
|
|
|
|
counter = 60.0;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
//JMutexAutoLock lock(m_env_mutex); //bulk comment-out
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
core::list<v3s16> deleted_blocks;
|
2010-12-13 23:21:18 +01:00
|
|
|
|
2013-12-01 01:52:06 +01:00
|
|
|
float delete_unused_sectors_timeout =
|
2011-10-12 12:53:38 +02:00
|
|
|
g_settings->getFloat("client_delete_unused_sectors_timeout");
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
// Delete sector blocks
|
2011-06-25 03:25:14 +02:00
|
|
|
/*u32 num = m_env.getMap().unloadUnusedData
|
2010-12-13 23:21:18 +01:00
|
|
|
(delete_unused_sectors_timeout,
|
2010-11-27 00:02:21 +01:00
|
|
|
true, &deleted_blocks);*/
|
|
|
|
|
|
|
|
// Delete whole sectors
|
2011-06-26 20:53:11 +02:00
|
|
|
m_env.getMap().unloadUnusedData
|
2010-12-13 23:21:18 +01:00
|
|
|
(delete_unused_sectors_timeout,
|
2011-06-26 20:53:11 +02:00
|
|
|
&deleted_blocks);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-06-26 20:53:11 +02:00
|
|
|
if(deleted_blocks.size() > 0)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"Client: Deleted blocks of "<<num
|
2010-11-27 16:18:34 +01:00
|
|
|
<<" unused sectors"<<std::endl;*/
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"Client: Deleted "<<num
|
2011-06-26 20:53:11 +02:00
|
|
|
<<" unused sectors"<<std::endl;*/
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Send info to server
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Env is locked so con can be locked.
|
2011-04-04 01:05:12 +02:00
|
|
|
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
core::list<v3s16>::Iterator i = deleted_blocks.begin();
|
|
|
|
core::list<v3s16> sendlist;
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
if(sendlist.size() == 255 || i == deleted_blocks.end())
|
|
|
|
{
|
|
|
|
if(sendlist.size() == 0)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
[0] u16 command
|
|
|
|
[2] u8 count
|
|
|
|
[3] v3s16 pos_0
|
|
|
|
[3+6] v3s16 pos_1
|
|
|
|
...
|
|
|
|
*/
|
|
|
|
u32 replysize = 2+1+6*sendlist.size();
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
|
|
|
|
reply[2] = sendlist.size();
|
|
|
|
u32 k = 0;
|
|
|
|
for(core::list<v3s16>::Iterator
|
|
|
|
j = sendlist.begin();
|
|
|
|
j != sendlist.end(); j++)
|
|
|
|
{
|
|
|
|
writeV3S16(&reply[2+1+6*k], *j);
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
m_con.Send(PEER_ID_SERVER, 1, reply, true);
|
|
|
|
|
|
|
|
if(i == deleted_blocks.end())
|
|
|
|
break;
|
|
|
|
|
|
|
|
sendlist.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
sendlist.push_back(*i);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-26 23:27:17 +02:00
|
|
|
#endif
|
2014-05-02 00:18:25 +02:00
|
|
|
// UGLY hack to fix 2 second startup delay caused by non existent
|
|
|
|
// server client startup synchronization in local server or singleplayer mode
|
|
|
|
static bool initial_step = true;
|
|
|
|
if (initial_step) {
|
|
|
|
initial_step = false;
|
|
|
|
}
|
|
|
|
else if(m_state == LC_Created)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_connection_reinit_timer;
|
2010-11-27 00:02:21 +01:00
|
|
|
counter -= dtime;
|
|
|
|
if(counter <= 0.0)
|
|
|
|
{
|
|
|
|
counter = 2.0;
|
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
Player *myplayer = m_env.getLocalPlayer();
|
|
|
|
assert(myplayer != NULL);
|
|
|
|
// Send TOSERVER_INIT
|
|
|
|
// [0] u16 TOSERVER_INIT
|
2013-08-01 22:51:36 +02:00
|
|
|
// [2] u8 SER_FMT_VER_HIGHEST_READ
|
2010-11-27 00:02:21 +01:00
|
|
|
// [3] u8[20] player_name
|
2011-07-30 19:02:17 +02:00
|
|
|
// [23] u8[28] password (new in some version)
|
2012-11-26 08:49:07 +01:00
|
|
|
// [51] u16 minimum supported network protocol version (added sometime)
|
|
|
|
// [53] u16 maximum supported network protocol version (added later than the previous one)
|
|
|
|
SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
|
2010-11-27 00:02:21 +01:00
|
|
|
writeU16(&data[0], TOSERVER_INIT);
|
2013-08-01 22:51:36 +02:00
|
|
|
writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
|
2011-06-01 23:01:11 +02:00
|
|
|
|
2011-02-08 00:12:55 +01:00
|
|
|
memset((char*)&data[3], 0, PLAYERNAME_SIZE);
|
|
|
|
snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
|
2011-06-01 23:01:11 +02:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
/*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
|
2011-06-01 23:01:11 +02:00
|
|
|
<<std::endl;*/
|
|
|
|
|
|
|
|
memset((char*)&data[23], 0, PASSWORD_SIZE);
|
2011-05-20 21:28:03 +02:00
|
|
|
snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
|
2011-07-30 19:02:17 +02:00
|
|
|
|
2012-11-26 08:49:07 +01:00
|
|
|
writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
|
|
|
|
writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
|
2011-05-20 21:28:03 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Send as unreliable
|
2014-01-06 20:05:28 +01:00
|
|
|
Send(1, data, false);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Not connected, return
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Do stuff if connected
|
|
|
|
*/
|
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
/*
|
|
|
|
Run Map's timers and unload unused data
|
|
|
|
*/
|
|
|
|
const float map_timer_and_unload_dtime = 5.25;
|
|
|
|
if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
|
|
|
|
{
|
2011-10-12 12:53:38 +02:00
|
|
|
ScopeProfiler sp(g_profiler, "Client: map timer and unload");
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<v3s16> deleted_blocks;
|
2011-06-26 23:27:17 +02:00
|
|
|
m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
|
2011-10-12 12:53:38 +02:00
|
|
|
g_settings->getFloat("client_unload_unused_data_timeout"),
|
2011-06-26 23:27:17 +02:00
|
|
|
&deleted_blocks);
|
|
|
|
|
|
|
|
/*if(deleted_blocks.size() > 0)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: Unloaded "<<deleted_blocks.size()
|
2011-06-26 23:27:17 +02:00
|
|
|
<<" unused blocks"<<std::endl;*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Send info to server
|
|
|
|
NOTE: This loop is intentionally iterated the way it is.
|
|
|
|
*/
|
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::list<v3s16>::iterator i = deleted_blocks.begin();
|
|
|
|
std::list<v3s16> sendlist;
|
2011-06-26 23:27:17 +02:00
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
if(sendlist.size() == 255 || i == deleted_blocks.end())
|
|
|
|
{
|
|
|
|
if(sendlist.size() == 0)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
[0] u16 command
|
|
|
|
[2] u8 count
|
|
|
|
[3] v3s16 pos_0
|
|
|
|
[3+6] v3s16 pos_1
|
|
|
|
...
|
|
|
|
*/
|
|
|
|
u32 replysize = 2+1+6*sendlist.size();
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
|
|
|
|
reply[2] = sendlist.size();
|
|
|
|
u32 k = 0;
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::list<v3s16>::iterator
|
2011-06-26 23:27:17 +02:00
|
|
|
j = sendlist.begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
j != sendlist.end(); ++j)
|
2011-06-26 23:27:17 +02:00
|
|
|
{
|
|
|
|
writeV3S16(&reply[2+1+6*k], *j);
|
|
|
|
k++;
|
|
|
|
}
|
2014-01-06 20:05:28 +01:00
|
|
|
m_con.Send(PEER_ID_SERVER, 2, reply, true);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
|
|
|
if(i == deleted_blocks.end())
|
|
|
|
break;
|
|
|
|
|
|
|
|
sendlist.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
sendlist.push_back(*i);
|
2012-12-20 18:19:49 +01:00
|
|
|
++i;
|
2011-06-26 23:27:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Handle environment
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
// Control local player (0ms)
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
player->applyControl(dtime);
|
|
|
|
|
|
|
|
// Step environment
|
|
|
|
m_env.step(dtime);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Get events
|
|
|
|
*/
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
ClientEnvEvent event = m_env.getClientEvent();
|
|
|
|
if(event.type == CEE_NONE)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if(event.type == CEE_PLAYER_DAMAGE)
|
|
|
|
{
|
|
|
|
if(m_ignore_damage_timer <= 0)
|
|
|
|
{
|
|
|
|
u8 damage = event.player_damage.amount;
|
2011-12-02 10:16:51 +01:00
|
|
|
|
|
|
|
if(event.player_damage.send_to_server)
|
|
|
|
sendDamage(damage);
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
// Add to ClientEvent queue
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_PLAYER_DAMAGE;
|
|
|
|
event.player_damage.amount = damage;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
}
|
2013-07-19 19:50:33 +02:00
|
|
|
else if(event.type == CEE_PLAYER_BREATH)
|
|
|
|
{
|
|
|
|
u16 breath = event.player_breath.amount;
|
|
|
|
sendBreath(breath);
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-19 19:50:33 +02:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
/*
|
|
|
|
Print some info
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_avg_rtt_timer;
|
2010-11-27 00:02:21 +01:00
|
|
|
counter += dtime;
|
|
|
|
if(counter >= 10)
|
|
|
|
{
|
|
|
|
counter = 0.0;
|
|
|
|
// connectedAndInitialized() is true, peer exists.
|
2014-02-13 20:17:42 +01:00
|
|
|
float avg_rtt = getRTT();
|
2011-10-20 22:04:09 +02:00
|
|
|
infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Send player position to server
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2010-12-19 15:51:45 +01:00
|
|
|
float &counter = m_playerpos_send_timer;
|
2010-11-27 00:02:21 +01:00
|
|
|
counter += dtime;
|
2014-02-13 20:17:42 +01:00
|
|
|
if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
counter = 0.0;
|
|
|
|
sendPlayerPos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
/*
|
|
|
|
Replace updated meshes
|
|
|
|
*/
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2012-03-21 02:33:02 +01:00
|
|
|
int num_processed_meshes = 0;
|
2012-12-20 18:19:49 +01:00
|
|
|
while(!m_mesh_update_thread.m_queue_out.empty())
|
2011-04-04 01:05:12 +02:00
|
|
|
{
|
2012-03-21 02:33:02 +01:00
|
|
|
num_processed_meshes++;
|
2014-01-06 12:45:42 +01:00
|
|
|
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
|
2011-04-04 01:05:12 +02:00
|
|
|
MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
|
|
|
|
if(block)
|
|
|
|
{
|
2012-03-13 18:56:12 +01:00
|
|
|
// Delete the old mesh
|
|
|
|
if(block->mesh != NULL)
|
|
|
|
{
|
|
|
|
// TODO: Remove hardware buffers of meshbuffers of block->mesh
|
|
|
|
delete block->mesh;
|
|
|
|
block->mesh = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Replace with the new mesh
|
|
|
|
block->mesh = r.mesh;
|
2013-05-18 01:52:18 +02:00
|
|
|
} else {
|
|
|
|
delete r.mesh;
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
if(r.ack_block_to_server)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Acknowledge block
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
[0] u16 command
|
|
|
|
[2] u8 count
|
|
|
|
[3] v3s16 pos_0
|
|
|
|
[3+6] v3s16 pos_1
|
|
|
|
...
|
|
|
|
*/
|
|
|
|
u32 replysize = 2+1+6;
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOSERVER_GOTBLOCKS);
|
|
|
|
reply[2] = 1;
|
|
|
|
writeV3S16(&reply[3], r.p);
|
|
|
|
// Send as reliable
|
2014-01-06 20:05:28 +01:00
|
|
|
m_con.Send(PEER_ID_SERVER, 2, reply, true);
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
}
|
2012-03-21 02:33:02 +01:00
|
|
|
if(num_processed_meshes > 0)
|
|
|
|
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
2012-01-22 00:49:02 +01:00
|
|
|
|
2012-12-14 12:30:17 +01:00
|
|
|
/*
|
|
|
|
Load fetched media
|
|
|
|
*/
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
if (m_media_downloader && m_media_downloader->isStarted()) {
|
|
|
|
m_media_downloader->step(this);
|
|
|
|
if (m_media_downloader->isDone()) {
|
2014-01-31 00:24:00 +01:00
|
|
|
received_media();
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
delete m_media_downloader;
|
|
|
|
m_media_downloader = NULL;
|
2012-12-14 12:30:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-22 00:49:02 +01:00
|
|
|
/*
|
|
|
|
If the server didn't update the inventory in a while, revert
|
|
|
|
the local inventory (so the player notices the lag problem
|
|
|
|
and knows something is wrong).
|
|
|
|
*/
|
|
|
|
if(m_inventory_from_server)
|
|
|
|
{
|
|
|
|
float interval = 10.0;
|
|
|
|
float count_before = floor(m_inventory_from_server_age / interval);
|
|
|
|
|
|
|
|
m_inventory_from_server_age += dtime;
|
|
|
|
|
|
|
|
float count_after = floor(m_inventory_from_server_age / interval);
|
|
|
|
|
|
|
|
if(count_after != count_before)
|
|
|
|
{
|
|
|
|
// Do this every <interval> seconds after TOCLIENT_INVENTORY
|
|
|
|
// Reset the locally changed inventory to the authoritative inventory
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
player->inventory = *m_inventory_from_server;
|
|
|
|
m_inventory_updated = true;
|
|
|
|
}
|
|
|
|
}
|
2012-03-24 18:01:26 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Update positions of sounds attached to objects
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
for(std::map<int, u16>::iterator
|
|
|
|
i = m_sounds_to_objects.begin();
|
|
|
|
i != m_sounds_to_objects.end(); i++)
|
|
|
|
{
|
|
|
|
int client_id = i->first;
|
|
|
|
u16 object_id = i->second;
|
|
|
|
ClientActiveObject *cao = m_env.getActiveObject(object_id);
|
|
|
|
if(!cao)
|
|
|
|
continue;
|
|
|
|
v3f pos = cao->getPosition();
|
|
|
|
m_sound->updateSoundPosition(client_id, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Handle removed remotely initiated sounds
|
|
|
|
*/
|
|
|
|
m_removed_sounds_check_timer += dtime;
|
|
|
|
if(m_removed_sounds_check_timer >= 2.32)
|
|
|
|
{
|
|
|
|
m_removed_sounds_check_timer = 0;
|
|
|
|
// Find removed sounds and clear references to them
|
|
|
|
std::set<s32> removed_server_ids;
|
|
|
|
for(std::map<s32, int>::iterator
|
|
|
|
i = m_sounds_server_to_client.begin();
|
|
|
|
i != m_sounds_server_to_client.end();)
|
|
|
|
{
|
|
|
|
s32 server_id = i->first;
|
|
|
|
int client_id = i->second;
|
|
|
|
i++;
|
|
|
|
if(!m_sound->soundExists(client_id)){
|
|
|
|
m_sounds_server_to_client.erase(server_id);
|
|
|
|
m_sounds_client_to_server.erase(client_id);
|
|
|
|
m_sounds_to_objects.erase(client_id);
|
|
|
|
removed_server_ids.insert(server_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Sync to server
|
|
|
|
if(removed_server_ids.size() != 0)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOSERVER_REMOVED_SOUNDS);
|
2014-02-05 00:17:57 +01:00
|
|
|
size_t server_ids = removed_server_ids.size();
|
|
|
|
assert(server_ids <= 0xFFFF);
|
|
|
|
writeU16(os, (u16) (server_ids & 0xFFFF));
|
2012-03-24 18:01:26 +01:00
|
|
|
for(std::set<s32>::iterator i = removed_server_ids.begin();
|
|
|
|
i != removed_server_ids.end(); i++)
|
|
|
|
writeS32(os, *i);
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-06 20:05:28 +01:00
|
|
|
Send(1, data, true);
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2012-03-25 13:47:51 +02:00
|
|
|
bool Client::loadMedia(const std::string &data, const std::string &filename)
|
|
|
|
{
|
|
|
|
// Silly irrlicht's const-incorrectness
|
|
|
|
Buffer<char> data_rw(data.c_str(), data.size());
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
const char *image_ext[] = {
|
|
|
|
".png", ".jpg", ".bmp", ".tga",
|
|
|
|
".pcx", ".ppm", ".psd", ".wal", ".rgb",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
name = removeStringEnd(filename, image_ext);
|
|
|
|
if(name != "")
|
|
|
|
{
|
2012-11-12 15:35:10 +01:00
|
|
|
verbosestream<<"Client: Attempting to load image "
|
|
|
|
<<"file \""<<filename<<"\""<<std::endl;
|
2012-03-25 13:47:51 +02:00
|
|
|
|
|
|
|
io::IFileSystem *irrfs = m_device->getFileSystem();
|
|
|
|
video::IVideoDriver *vdrv = m_device->getVideoDriver();
|
|
|
|
|
|
|
|
// Create an irrlicht memory file
|
|
|
|
io::IReadFile *rfile = irrfs->createMemoryReadFile(
|
|
|
|
*data_rw, data_rw.getSize(), "_tempreadfile");
|
|
|
|
assert(rfile);
|
|
|
|
// Read image
|
|
|
|
video::IImage *img = vdrv->createImageFromFile(rfile);
|
|
|
|
if(!img){
|
|
|
|
errorstream<<"Client: Cannot create image from data of "
|
|
|
|
<<"file \""<<filename<<"\""<<std::endl;
|
|
|
|
rfile->drop();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_tsrc->insertSourceImage(filename, img);
|
|
|
|
img->drop();
|
|
|
|
rfile->drop();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *sound_ext[] = {
|
2012-03-25 15:21:34 +02:00
|
|
|
".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
|
|
|
|
".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
|
2012-03-25 13:47:51 +02:00
|
|
|
".ogg", NULL
|
|
|
|
};
|
|
|
|
name = removeStringEnd(filename, sound_ext);
|
|
|
|
if(name != "")
|
|
|
|
{
|
2012-11-12 15:35:10 +01:00
|
|
|
verbosestream<<"Client: Attempting to load sound "
|
|
|
|
<<"file \""<<filename<<"\""<<std::endl;
|
2012-03-25 13:47:51 +02:00
|
|
|
m_sound->loadSoundData(name, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-23 23:11:24 +02:00
|
|
|
const char *model_ext[] = {
|
2012-10-24 21:10:05 +02:00
|
|
|
".x", ".b3d", ".md2", ".obj",
|
2012-10-23 23:11:24 +02:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
name = removeStringEnd(filename, model_ext);
|
|
|
|
if(name != "")
|
|
|
|
{
|
2014-01-06 12:24:06 +01:00
|
|
|
verbosestream<<"Client: Storing model into memory: "
|
2012-10-24 21:10:05 +02:00
|
|
|
<<"\""<<filename<<"\""<<std::endl;
|
2014-01-06 12:24:06 +01:00
|
|
|
if(m_mesh_data.count(filename))
|
|
|
|
errorstream<<"Multiple models with name \""<<filename.c_str()
|
|
|
|
<<"\" found; replacing previous model"<<std::endl;
|
|
|
|
m_mesh_data[filename] = data;
|
2012-10-23 23:11:24 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-25 13:47:51 +02:00
|
|
|
errorstream<<"Client: Don't know how to load file \""
|
|
|
|
<<filename<<"\""<<std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Virtual methods from con::PeerHandler
|
|
|
|
void Client::peerAdded(con::Peer *peer)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client::peerAdded(): peer->id="
|
2010-11-27 00:02:21 +01:00
|
|
|
<<peer->id<<std::endl;
|
|
|
|
}
|
|
|
|
void Client::deletingPeer(con::Peer *peer, bool timeout)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client::deletingPeer(): "
|
2010-11-27 00:02:21 +01:00
|
|
|
"Server Peer is getting deleted "
|
|
|
|
<<"(timeout="<<timeout<<")"<<std::endl;
|
|
|
|
}
|
|
|
|
|
2012-12-14 12:30:17 +01:00
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 number of files requested
|
|
|
|
for each file {
|
|
|
|
u16 length of name
|
|
|
|
string name
|
|
|
|
}
|
|
|
|
*/
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
void Client::request_media(const std::list<std::string> &file_requests)
|
2012-12-14 12:30:17 +01:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOSERVER_REQUEST_MEDIA);
|
2014-02-05 00:17:57 +01:00
|
|
|
size_t file_requests_size = file_requests.size();
|
|
|
|
assert(file_requests_size <= 0xFFFF);
|
|
|
|
writeU16(os, (u16) (file_requests_size & 0xFFFF));
|
2012-12-14 12:30:17 +01:00
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
for(std::list<std::string>::const_iterator i = file_requests.begin();
|
2012-12-20 18:19:49 +01:00
|
|
|
i != file_requests.end(); ++i) {
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
os<<serializeString(*i);
|
2012-12-14 12:30:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-06 20:05:28 +01:00
|
|
|
Send(1, data, true);
|
2012-12-14 12:30:17 +01:00
|
|
|
infostream<<"Client: Sending media request list to server ("
|
|
|
|
<<file_requests.size()<<" files)"<<std::endl;
|
|
|
|
}
|
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
void Client::received_media()
|
|
|
|
{
|
|
|
|
// notify server we received everything
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
writeU16(os, TOSERVER_RECEIVED_MEDIA);
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
2014-01-06 20:05:28 +01:00
|
|
|
Send(1, data, true);
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
infostream<<"Client: Notifying server that we received all media"
|
|
|
|
<<std::endl;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Client::ReceiveAll()
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2011-11-15 22:58:56 +01:00
|
|
|
u32 start_ms = porting::getTimeMs();
|
2010-11-27 00:02:21 +01:00
|
|
|
for(;;)
|
|
|
|
{
|
2011-11-15 22:58:56 +01:00
|
|
|
// Limit time even if there would be huge amounts of data to
|
|
|
|
// process
|
|
|
|
if(porting::getTimeMs() > start_ms + 100)
|
|
|
|
break;
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
try{
|
|
|
|
Receive();
|
2012-03-21 02:33:02 +01:00
|
|
|
g_profiler->graphAdd("client_received_packets", 1);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
catch(con::NoIncomingDataException &e)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
catch(con::InvalidIncomingDataException &e)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client::ReceiveAll(): "
|
2010-11-27 00:02:21 +01:00
|
|
|
"InvalidIncomingDataException: what()="
|
|
|
|
<<e.what()<<std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::Receive()
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2011-11-07 04:20:33 +01:00
|
|
|
SharedBuffer<u8> data;
|
2010-11-27 00:02:21 +01:00
|
|
|
u16 sender_peer_id;
|
2014-02-05 00:17:57 +01:00
|
|
|
u32 datasize = m_con.Receive(sender_peer_id, data);
|
2010-11-27 00:02:21 +01:00
|
|
|
ProcessData(*data, datasize, sender_peer_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
sender_peer_id given to this shall be quaranteed to be a valid peer
|
|
|
|
*/
|
|
|
|
void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2010-11-27 16:18:34 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Ignore packets that don't even fit a command
|
|
|
|
if(datasize < 2)
|
2010-11-27 16:18:34 +01:00
|
|
|
{
|
|
|
|
m_packetcounter.add(60000);
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
2010-11-27 16:18:34 +01:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
ToClientCommand command = (ToClientCommand)readU16(&data[0]);
|
2010-11-27 16:18:34 +01:00
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Client: received command="<<command<<std::endl;
|
2010-11-27 16:18:34 +01:00
|
|
|
m_packetcounter.add((u16)command);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
If this check is removed, be sure to change the queue
|
|
|
|
system to know the ids
|
|
|
|
*/
|
|
|
|
if(sender_peer_id != PEER_ID_SERVER)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client::ProcessData(): Discarding data not "
|
2010-11-27 00:02:21 +01:00
|
|
|
"coming from server: peer_id="<<sender_peer_id
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 ser_version = m_server_ser_ver;
|
|
|
|
|
|
|
|
if(command == TOCLIENT_INIT)
|
|
|
|
{
|
|
|
|
if(datasize < 3)
|
|
|
|
return;
|
|
|
|
|
|
|
|
u8 deployed = data[2];
|
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: TOCLIENT_INIT received with "
|
2010-11-27 00:02:21 +01:00
|
|
|
"deployed="<<((int)deployed&0xff)<<std::endl;
|
|
|
|
|
2013-08-01 22:51:36 +02:00
|
|
|
if(!ser_ver_supported(deployed))
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: TOCLIENT_INIT: Server sent "
|
2010-11-27 00:02:21 +01:00
|
|
|
<<"unsupported ser_fmt_ver"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_server_ser_ver = deployed;
|
|
|
|
|
|
|
|
// Get player position
|
|
|
|
v3s16 playerpos_s16(0, BS*2+BS*20, 0);
|
|
|
|
if(datasize >= 2+1+6)
|
|
|
|
playerpos_s16 = readV3S16(&data[2+1]);
|
2011-02-20 23:45:14 +01:00
|
|
|
v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
// Set player position
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
player->setPosition(playerpos_f);
|
2011-03-02 01:00:11 +01:00
|
|
|
|
|
|
|
if(datasize >= 2+1+6+8)
|
|
|
|
{
|
|
|
|
// Get map seed
|
|
|
|
m_map_seed = readU64(&data[2+1+6]);
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
|
2011-03-02 01:00:11 +01:00
|
|
|
}
|
2012-11-26 21:31:21 +01:00
|
|
|
|
|
|
|
if(datasize >= 2+1+6+8+4)
|
|
|
|
{
|
|
|
|
// Get map seed
|
|
|
|
m_recommended_send_interval = readF1000(&data[2+1+6+8]);
|
|
|
|
infostream<<"Client: received recommended send interval "
|
|
|
|
<<m_recommended_send_interval<<std::endl;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
// Reply to server
|
|
|
|
u32 replysize = 2;
|
|
|
|
SharedBuffer<u8> reply(replysize);
|
|
|
|
writeU16(&reply[0], TOSERVER_INIT2);
|
|
|
|
// Send as reliable
|
|
|
|
m_con.Send(PEER_ID_SERVER, 1, reply, true);
|
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
m_state = LC_Init;
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2011-05-20 21:28:03 +02:00
|
|
|
|
|
|
|
if(command == TOCLIENT_ACCESS_DENIED)
|
|
|
|
{
|
|
|
|
// The server didn't like our password. Note, this needs
|
|
|
|
// to be processed even if the serialisation format has
|
|
|
|
// not been agreed yet, the same as TOCLIENT_INIT.
|
|
|
|
m_access_denied = true;
|
2011-05-29 20:11:16 +02:00
|
|
|
m_access_denied_reason = L"Unknown";
|
|
|
|
if(datasize >= 4)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
m_access_denied_reason = deSerializeWideString(is);
|
|
|
|
}
|
2011-05-20 21:28:03 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
if(ser_version == SER_FMT_VER_INVALID)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: Server serialization"
|
2010-11-27 00:02:21 +01:00
|
|
|
" format invalid or not initialized."
|
|
|
|
" Skipping incoming command="<<command<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
/*
|
|
|
|
Handle runtime commands
|
|
|
|
*/
|
|
|
|
// there's no sane reason why we shouldn't have a player and
|
|
|
|
// almost everyone needs a player reference
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2010-12-24 02:08:05 +01:00
|
|
|
if(command == TOCLIENT_REMOVENODE)
|
|
|
|
{
|
|
|
|
if(datasize < 8)
|
|
|
|
return;
|
|
|
|
v3s16 p;
|
|
|
|
p.X = readS16(&data[2]);
|
|
|
|
p.Y = readS16(&data[4]);
|
|
|
|
p.Z = readS16(&data[6]);
|
|
|
|
removeNode(p);
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_ADDNODE)
|
|
|
|
{
|
|
|
|
if(datasize < 8 + MapNode::serializedLength(ser_version))
|
|
|
|
return;
|
|
|
|
|
|
|
|
v3s16 p;
|
|
|
|
p.X = readS16(&data[2]);
|
|
|
|
p.Y = readS16(&data[4]);
|
|
|
|
p.Z = readS16(&data[6]);
|
|
|
|
|
|
|
|
MapNode n;
|
2011-11-16 12:03:28 +01:00
|
|
|
n.deSerialize(&data[8], ser_version);
|
2010-12-24 02:08:05 +01:00
|
|
|
|
2013-11-23 15:35:49 +01:00
|
|
|
bool remove_metadata = true;
|
|
|
|
u32 index = 8 + MapNode::serializedLength(ser_version);
|
|
|
|
if ((datasize >= index+1) && data[index]){
|
|
|
|
remove_metadata = false;
|
2013-12-01 01:52:06 +01:00
|
|
|
}
|
2013-11-23 15:35:49 +01:00
|
|
|
|
|
|
|
addNode(p, n, remove_metadata);
|
2010-12-24 02:08:05 +01:00
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
else if(command == TOCLIENT_BLOCKDATA)
|
|
|
|
{
|
|
|
|
// Ignore too small packet
|
|
|
|
if(datasize < 8)
|
|
|
|
return;
|
|
|
|
|
|
|
|
v3s16 p;
|
|
|
|
p.X = readS16(&data[2]);
|
|
|
|
p.Y = readS16(&data[4]);
|
|
|
|
p.Z = readS16(&data[6]);
|
|
|
|
|
|
|
|
std::string datastring((char*)&data[8], datasize-8);
|
|
|
|
std::istringstream istr(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
MapSector *sector;
|
|
|
|
MapBlock *block;
|
|
|
|
|
2011-06-26 02:14:52 +02:00
|
|
|
v2s16 p2d(p.X, p.Z);
|
|
|
|
sector = m_env.getMap().emergeSector(p2d);
|
|
|
|
|
|
|
|
assert(sector->getPos() == p2d);
|
|
|
|
|
|
|
|
block = sector->getBlockNoCreateNoEx(p.Y);
|
|
|
|
if(block)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Update an existing block
|
|
|
|
*/
|
2012-01-21 00:11:44 +01:00
|
|
|
block->deSerialize(istr, ser_version, false);
|
2013-08-01 22:51:36 +02:00
|
|
|
block->deSerializeNetworkSpecific(istr);
|
2011-06-26 02:14:52 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Create a new block
|
|
|
|
*/
|
2011-11-14 20:41:30 +01:00
|
|
|
block = new MapBlock(&m_env.getMap(), p, this);
|
2012-01-21 00:11:44 +01:00
|
|
|
block->deSerialize(istr, ser_version, false);
|
2013-08-01 22:51:36 +02:00
|
|
|
block->deSerializeNetworkSpecific(istr);
|
2011-06-26 02:14:52 +02:00
|
|
|
sector->insertBlock(block);
|
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
|
2014-10-09 14:02:02 +02:00
|
|
|
if (localdb != NULL) {
|
|
|
|
((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
|
|
|
|
}
|
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
Add it to mesh update queue and set it to be acknowledged after update.
|
|
|
|
*/
|
2011-04-04 01:05:12 +02:00
|
|
|
addUpdateMeshTaskWithEdge(p, true);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
else if(command == TOCLIENT_INVENTORY)
|
|
|
|
{
|
|
|
|
if(datasize < 3)
|
|
|
|
return;
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
player->inventory.deSerialize(is);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
m_inventory_updated = true;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
delete m_inventory_from_server;
|
|
|
|
m_inventory_from_server = new Inventory(player->inventory);
|
|
|
|
m_inventory_from_server_age = 0.0;
|
2012-01-22 00:49:02 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2010-12-20 13:04:31 +01:00
|
|
|
else if(command == TOCLIENT_TIME_OF_DAY)
|
|
|
|
{
|
|
|
|
if(datasize < 4)
|
|
|
|
return;
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
u16 time_of_day = readU16(&data[2]);
|
|
|
|
time_of_day = time_of_day % 24000;
|
2012-03-16 15:34:30 +01:00
|
|
|
float time_speed = 0;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
|
|
|
if(datasize >= 2 + 2 + 4)
|
|
|
|
{
|
2012-03-16 15:34:30 +01:00
|
|
|
time_speed = readF1000(&data[4]);
|
2014-02-05 00:17:57 +01:00
|
|
|
}
|
|
|
|
else {
|
2012-03-16 15:34:30 +01:00
|
|
|
// Old message; try to approximate speed of time by ourselves
|
|
|
|
float time_of_day_f = (float)time_of_day / 24000.0;
|
|
|
|
float tod_diff_f = 0;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2012-03-16 15:34:30 +01:00
|
|
|
if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
|
|
|
|
tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
|
|
|
|
else
|
|
|
|
tod_diff_f = time_of_day_f - m_last_time_of_day_f;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
|
|
|
m_last_time_of_day_f = time_of_day_f;
|
|
|
|
float time_diff = m_time_of_day_update_timer;
|
2012-03-16 15:34:30 +01:00
|
|
|
m_time_of_day_update_timer = 0;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2012-03-16 15:34:30 +01:00
|
|
|
if(m_time_of_day_set){
|
2014-02-05 00:17:57 +01:00
|
|
|
time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
|
2012-03-16 15:34:30 +01:00
|
|
|
infostream<<"Client: Measured time_of_day speed (old format): "
|
|
|
|
<<time_speed<<" tod_diff_f="<<tod_diff_f
|
|
|
|
<<" time_diff="<<time_diff<<std::endl;
|
|
|
|
}
|
2010-12-29 14:26:47 +01:00
|
|
|
}
|
2012-03-16 15:34:30 +01:00
|
|
|
|
|
|
|
// Update environment
|
|
|
|
m_env.setTimeOfDay(time_of_day);
|
|
|
|
m_env.setTimeOfDaySpeed(time_speed);
|
|
|
|
m_time_of_day_set = true;
|
|
|
|
|
|
|
|
u32 dr = m_env.getDayNightRatio();
|
2014-04-05 15:27:33 +02:00
|
|
|
infostream<<"Client: time_of_day="<<time_of_day
|
2012-03-16 15:34:30 +01:00
|
|
|
<<" time_speed="<<time_speed
|
|
|
|
<<" dr="<<dr<<std::endl;
|
2010-12-20 13:04:31 +01:00
|
|
|
}
|
2010-12-23 21:35:53 +01:00
|
|
|
else if(command == TOCLIENT_CHAT_MESSAGE)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 length
|
|
|
|
wstring message
|
|
|
|
*/
|
|
|
|
u8 buf[6];
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
// Read stuff
|
2014-02-05 00:17:57 +01:00
|
|
|
is.read((char*) buf, 2);
|
2010-12-23 21:35:53 +01:00
|
|
|
u16 len = readU16(buf);
|
|
|
|
|
|
|
|
std::wstring message;
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0; i<len; i++)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
|
|
|
is.read((char*)buf, 2);
|
|
|
|
message += (wchar_t)readU16(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_chat_queue.push_back(message);
|
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
|
|
|
|
{
|
2014-02-05 00:17:57 +01:00
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 count of removed objects
|
|
|
|
for all removed objects {
|
|
|
|
u16 id
|
|
|
|
}
|
|
|
|
u16 count of added objects
|
|
|
|
for all added objects {
|
|
|
|
u16 id
|
|
|
|
u8 type
|
|
|
|
u32 initialization data length
|
|
|
|
string initialization data
|
|
|
|
}
|
|
|
|
*/
|
2011-02-20 23:45:14 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
char buf[6];
|
|
|
|
// Get all data except the command number
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
// Throw them in an istringstream
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2011-02-20 23:45:14 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
// Read removed objects
|
|
|
|
is.read(buf, 2);
|
|
|
|
u16 removed_count = readU16((u8*)buf);
|
|
|
|
for(unsigned int i=0; i<removed_count; i++)
|
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
is.read(buf, 2);
|
2014-02-05 00:17:57 +01:00
|
|
|
u16 id = readU16((u8*)buf);
|
|
|
|
m_env.removeActiveObject(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read added objects
|
|
|
|
is.read(buf, 2);
|
|
|
|
u16 added_count = readU16((u8*)buf);
|
|
|
|
for(unsigned int i=0; i<added_count; i++)
|
|
|
|
{
|
2011-02-20 23:45:14 +01:00
|
|
|
is.read(buf, 2);
|
2014-02-05 00:17:57 +01:00
|
|
|
u16 id = readU16((u8*)buf);
|
|
|
|
is.read(buf, 1);
|
|
|
|
u8 type = readU8((u8*)buf);
|
|
|
|
std::string data = deSerializeLongString(is);
|
|
|
|
// Add it
|
|
|
|
m_env.addActiveObject(id, type, data);
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
|
|
|
|
{
|
2014-02-05 00:17:57 +01:00
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
for all objects
|
|
|
|
{
|
|
|
|
u16 id
|
|
|
|
u16 message length
|
|
|
|
string message
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
char buf[6];
|
|
|
|
// Get all data except the command number
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
// Throw them in an istringstream
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
while(is.eof() == false)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
2014-02-05 00:17:57 +01:00
|
|
|
is.read(buf, 2);
|
|
|
|
u16 id = readU16((u8*)buf);
|
|
|
|
if(is.eof())
|
|
|
|
break;
|
|
|
|
is.read(buf, 2);
|
|
|
|
size_t message_size = readU16((u8*)buf);
|
|
|
|
std::string message;
|
|
|
|
message.reserve(message_size);
|
|
|
|
for(unsigned int i=0; i<message_size; i++)
|
2011-02-20 23:45:14 +01:00
|
|
|
{
|
2014-02-05 00:17:57 +01:00
|
|
|
is.read(buf, 1);
|
|
|
|
message.append(buf, 1);
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
// Pass on to the environment
|
|
|
|
m_env.processActiveObjectMessage(id, message);
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
|
|
|
}
|
2013-02-08 21:54:01 +01:00
|
|
|
else if(command == TOCLIENT_MOVEMENT)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
player->movement_acceleration_default = readF1000(is) * BS;
|
|
|
|
player->movement_acceleration_air = readF1000(is) * BS;
|
|
|
|
player->movement_acceleration_fast = readF1000(is) * BS;
|
|
|
|
player->movement_speed_walk = readF1000(is) * BS;
|
|
|
|
player->movement_speed_crouch = readF1000(is) * BS;
|
|
|
|
player->movement_speed_fast = readF1000(is) * BS;
|
|
|
|
player->movement_speed_climb = readF1000(is) * BS;
|
|
|
|
player->movement_speed_jump = readF1000(is) * BS;
|
|
|
|
player->movement_liquid_fluidity = readF1000(is) * BS;
|
2013-02-08 21:54:01 +01:00
|
|
|
player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
|
2014-02-05 00:17:57 +01:00
|
|
|
player->movement_liquid_sink = readF1000(is) * BS;
|
|
|
|
player->movement_gravity = readF1000(is) * BS;
|
2013-02-08 21:54:01 +01:00
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
else if(command == TOCLIENT_HP)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
|
|
|
u8 oldhp = player->hp;
|
|
|
|
u8 hp = readU8(is);
|
2011-04-21 18:35:17 +02:00
|
|
|
player->hp = hp;
|
2012-01-24 00:00:26 +01:00
|
|
|
|
|
|
|
if(hp < oldhp)
|
|
|
|
{
|
|
|
|
// Add to ClientEvent queue
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_PLAYER_DAMAGE;
|
|
|
|
event.player_damage.amount = oldhp - hp;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2013-07-19 19:50:33 +02:00
|
|
|
else if(command == TOCLIENT_BREATH)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
|
|
|
player->setBreath(readU16(is));
|
2013-07-19 19:50:33 +02:00
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
else if(command == TOCLIENT_MOVE_PLAYER)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
v3f pos = readV3F1000(is);
|
|
|
|
f32 pitch = readF1000(is);
|
|
|
|
f32 yaw = readF1000(is);
|
|
|
|
player->setPosition(pos);
|
|
|
|
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client got TOCLIENT_MOVE_PLAYER"
|
2011-04-21 18:35:17 +02:00
|
|
|
<<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
|
|
|
|
<<" pitch="<<pitch
|
|
|
|
<<" yaw="<<yaw
|
|
|
|
<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
2011-04-21 18:35:17 +02:00
|
|
|
Add to ClientEvent queue.
|
|
|
|
This has to be sent to the main program because otherwise
|
|
|
|
it would just force the pitch and yaw values to whatever
|
|
|
|
the camera points to.
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2011-04-21 18:35:17 +02:00
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_PLAYER_FORCE_MOVE;
|
|
|
|
event.player_force_move.pitch = pitch;
|
|
|
|
event.player_force_move.yaw = yaw;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
|
|
|
|
// Ignore damage for a few seconds, so that the player doesn't
|
|
|
|
// get damage from falling on ground
|
|
|
|
m_ignore_damage_timer = 3.0;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2011-08-11 07:02:57 +02:00
|
|
|
else if(command == TOCLIENT_PLAYERITEM)
|
|
|
|
{
|
2012-03-19 03:04:16 +01:00
|
|
|
infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
|
2011-08-11 07:02:57 +02:00
|
|
|
}
|
2011-10-15 13:46:59 +02:00
|
|
|
else if(command == TOCLIENT_DEATHSCREEN)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
bool set_camera_point_target = readU8(is);
|
|
|
|
v3f camera_point_target = readV3F1000(is);
|
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_DEATHSCREEN;
|
2011-10-15 13:46:59 +02:00
|
|
|
event.deathscreen.set_camera_point_target = set_camera_point_target;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.deathscreen.camera_point_target_x = camera_point_target.X;
|
|
|
|
event.deathscreen.camera_point_target_y = camera_point_target.Y;
|
|
|
|
event.deathscreen.camera_point_target_z = camera_point_target.Z;
|
2011-10-15 13:46:59 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2012-03-25 10:50:29 +02:00
|
|
|
else if(command == TOCLIENT_ANNOUNCE_MEDIA)
|
2012-01-02 12:31:50 +01:00
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2012-03-25 10:50:29 +02:00
|
|
|
int num_files = readU16(is);
|
|
|
|
|
2012-11-30 17:12:32 +01:00
|
|
|
infostream<<"Client: Received media announcement: packet size: "
|
|
|
|
<<datasize<<std::endl;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
if (m_media_downloader == NULL ||
|
|
|
|
m_media_downloader->isStarted()) {
|
|
|
|
const char *problem = m_media_downloader ?
|
|
|
|
"we already saw another announcement" :
|
|
|
|
"all media has been received already";
|
|
|
|
errorstream<<"Client: Received media announcement but "
|
|
|
|
<<problem<<"! "
|
|
|
|
<<" files="<<num_files
|
|
|
|
<<" size="<<datasize<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2012-03-25 13:47:51 +02:00
|
|
|
for(int i=0; i<num_files; i++)
|
|
|
|
{
|
2012-01-02 12:31:50 +01:00
|
|
|
std::string name = deSerializeString(is);
|
2012-03-25 13:47:51 +02:00
|
|
|
std::string sha1_base64 = deSerializeString(is);
|
|
|
|
std::string sha1_raw = base64_decode(sha1_base64);
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
m_media_downloader->addFile(name, sha1_raw);
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
std::vector<std::string> remote_media;
|
2012-12-14 12:30:17 +01:00
|
|
|
try {
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
Strfnd sf(deSerializeString(is));
|
|
|
|
while(!sf.atend()) {
|
|
|
|
std::string baseurl = trim(sf.next(","));
|
|
|
|
if(baseurl != "")
|
|
|
|
m_media_downloader->addRemoteServer(baseurl);
|
|
|
|
}
|
2012-12-14 12:30:17 +01:00
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
catch(SerializationError& e) {
|
2012-12-14 12:30:17 +01:00
|
|
|
// not supported by server or turned off
|
|
|
|
}
|
2012-01-02 12:31:50 +01:00
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
m_media_downloader->step(this);
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
2012-03-25 10:50:29 +02:00
|
|
|
else if(command == TOCLIENT_MEDIA)
|
2011-11-15 10:02:47 +01:00
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
/*
|
|
|
|
u16 command
|
2012-03-25 10:50:29 +02:00
|
|
|
u16 total number of file bunches
|
2011-11-15 22:58:56 +01:00
|
|
|
u16 index of this bunch
|
2012-03-25 10:50:29 +02:00
|
|
|
u32 number of files in this bunch
|
|
|
|
for each file {
|
2011-11-15 10:02:47 +01:00
|
|
|
u16 length of name
|
|
|
|
string name
|
|
|
|
u32 length of data
|
|
|
|
data
|
|
|
|
}
|
|
|
|
*/
|
2011-11-15 22:58:56 +01:00
|
|
|
int num_bunches = readU16(is);
|
|
|
|
int bunch_i = readU16(is);
|
2013-06-13 03:05:47 +02:00
|
|
|
u32 num_files = readU32(is);
|
2012-03-25 10:50:29 +02:00
|
|
|
infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
|
|
|
|
<<num_bunches<<" files="<<num_files
|
2011-11-15 23:20:22 +01:00
|
|
|
<<" size="<<datasize<<std::endl;
|
2013-06-13 03:05:47 +02:00
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
if (num_files == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_media_downloader == NULL ||
|
|
|
|
!m_media_downloader->isStarted()) {
|
|
|
|
const char *problem = m_media_downloader ?
|
|
|
|
"media has not been requested" :
|
|
|
|
"all media has been received already";
|
|
|
|
errorstream<<"Client: Received media but "
|
|
|
|
<<problem<<"! "
|
2013-06-13 03:05:47 +02:00
|
|
|
<<" bunch "<<bunch_i<<"/"<<num_bunches
|
|
|
|
<<" files="<<num_files
|
|
|
|
<<" size="<<datasize<<std::endl;
|
|
|
|
return;
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
}
|
2013-06-13 03:05:47 +02:00
|
|
|
|
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0; i<num_files; i++){
|
2011-11-15 10:02:47 +01:00
|
|
|
std::string name = deSerializeString(is);
|
|
|
|
std::string data = deSerializeLongString(is);
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
m_media_downloader->conventionalTransferDone(
|
|
|
|
name, data, this);
|
2011-11-15 10:02:47 +01:00
|
|
|
}
|
|
|
|
}
|
2011-11-15 22:58:56 +01:00
|
|
|
else if(command == TOCLIENT_TOOLDEF)
|
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
|
2011-11-15 22:58:56 +01:00
|
|
|
}
|
2011-11-15 19:32:56 +01:00
|
|
|
else if(command == TOCLIENT_NODEDEF)
|
|
|
|
{
|
|
|
|
infostream<<"Client: Received node definitions: packet size: "
|
|
|
|
<<datasize<<std::endl;
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
|
|
|
// Decompress node definitions
|
2011-11-15 19:32:56 +01:00
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
|
2012-01-12 06:10:39 +01:00
|
|
|
std::ostringstream tmp_os;
|
|
|
|
decompressZlib(tmp_is, tmp_os);
|
2011-11-15 19:32:56 +01:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Deserialize node definitions
|
|
|
|
std::istringstream tmp_is2(tmp_os.str());
|
|
|
|
m_nodedef->deSerialize(tmp_is2);
|
|
|
|
m_nodedef_received = true;
|
2011-11-15 19:32:56 +01:00
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
else if(command == TOCLIENT_CRAFTITEMDEF)
|
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_ITEMDEF)
|
|
|
|
{
|
|
|
|
infostream<<"Client: Received item definitions: packet size: "
|
2011-11-29 16:15:18 +01:00
|
|
|
<<datasize<<std::endl;
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
|
|
|
// Decompress item definitions
|
2011-11-29 16:15:18 +01:00
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
|
2012-01-12 06:10:39 +01:00
|
|
|
std::ostringstream tmp_os;
|
|
|
|
decompressZlib(tmp_is, tmp_os);
|
|
|
|
|
|
|
|
// Deserialize node definitions
|
|
|
|
std::istringstream tmp_is2(tmp_os.str());
|
|
|
|
m_itemdef->deSerialize(tmp_is2);
|
|
|
|
m_itemdef_received = true;
|
2011-11-29 16:15:18 +01:00
|
|
|
}
|
2012-03-24 18:01:26 +01:00
|
|
|
else if(command == TOCLIENT_PLAY_SOUND)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
s32 server_id = readS32(is);
|
|
|
|
std::string name = deSerializeString(is);
|
|
|
|
float gain = readF1000(is);
|
|
|
|
int type = readU8(is); // 0=local, 1=positional, 2=object
|
|
|
|
v3f pos = readV3F1000(is);
|
|
|
|
u16 object_id = readU16(is);
|
|
|
|
bool loop = readU8(is);
|
|
|
|
// Start playing
|
|
|
|
int client_id = -1;
|
|
|
|
switch(type){
|
|
|
|
case 0: // local
|
2012-04-08 13:35:57 +02:00
|
|
|
client_id = m_sound->playSound(name, loop, gain);
|
2012-03-24 18:01:26 +01:00
|
|
|
break;
|
|
|
|
case 1: // positional
|
2012-04-08 13:35:57 +02:00
|
|
|
client_id = m_sound->playSoundAt(name, loop, gain, pos);
|
2012-03-24 18:01:26 +01:00
|
|
|
break;
|
|
|
|
case 2: { // object
|
|
|
|
ClientActiveObject *cao = m_env.getActiveObject(object_id);
|
|
|
|
if(cao)
|
|
|
|
pos = cao->getPosition();
|
|
|
|
client_id = m_sound->playSoundAt(name, loop, gain, pos);
|
|
|
|
// TODO: Set up sound to move with object
|
|
|
|
break; }
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(client_id != -1){
|
|
|
|
m_sounds_server_to_client[server_id] = client_id;
|
|
|
|
m_sounds_client_to_server[client_id] = server_id;
|
|
|
|
if(object_id != 0)
|
|
|
|
m_sounds_to_objects[client_id] = object_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_STOP_SOUND)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
s32 server_id = readS32(is);
|
|
|
|
std::map<s32, int>::iterator i =
|
|
|
|
m_sounds_server_to_client.find(server_id);
|
|
|
|
if(i != m_sounds_server_to_client.end()){
|
|
|
|
int client_id = i->second;
|
|
|
|
m_sound->stopSound(client_id);
|
|
|
|
}
|
|
|
|
}
|
2012-03-31 15:23:26 +02:00
|
|
|
else if(command == TOCLIENT_PRIVILEGES)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
m_privileges.clear();
|
|
|
|
infostream<<"Client: Privileges updated: ";
|
|
|
|
u16 num_privileges = readU16(is);
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0; i<num_privileges; i++){
|
2012-03-31 15:23:26 +02:00
|
|
|
std::string priv = deSerializeString(is);
|
|
|
|
m_privileges.insert(priv);
|
|
|
|
infostream<<priv<<" ";
|
|
|
|
}
|
|
|
|
infostream<<std::endl;
|
|
|
|
}
|
2012-07-19 13:09:16 +02:00
|
|
|
else if(command == TOCLIENT_INVENTORY_FORMSPEC)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
// Store formspec in LocalPlayer
|
|
|
|
player->inventory_formspec = deSerializeLongString(is);
|
|
|
|
}
|
2012-07-24 19:57:17 +02:00
|
|
|
else if(command == TOCLIENT_DETACHED_INVENTORY)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
std::string name = deSerializeString(is);
|
|
|
|
|
|
|
|
infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
|
|
|
|
|
|
|
|
Inventory *inv = NULL;
|
|
|
|
if(m_detached_inventories.count(name) > 0)
|
|
|
|
inv = m_detached_inventories[name];
|
|
|
|
else{
|
|
|
|
inv = new Inventory(m_itemdef);
|
|
|
|
m_detached_inventories[name] = inv;
|
|
|
|
}
|
|
|
|
inv->deSerialize(is);
|
|
|
|
}
|
2013-01-02 20:45:04 +01:00
|
|
|
else if(command == TOCLIENT_SHOW_FORMSPEC)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
std::string formspec = deSerializeLongString(is);
|
2013-01-03 18:59:28 +01:00
|
|
|
std::string formname = deSerializeString(is);
|
2013-01-02 20:45:04 +01:00
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_SHOW_FORMSPEC;
|
|
|
|
// pointer is required as event is a struct only!
|
|
|
|
// adding a std:string to a struct isn't possible
|
|
|
|
event.show_formspec.formspec = new std::string(formspec);
|
2013-01-03 18:59:28 +01:00
|
|
|
event.show_formspec.formname = new std::string(formname);
|
2013-01-02 20:45:04 +01:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2013-01-23 18:32:02 +01:00
|
|
|
else if(command == TOCLIENT_SPAWN_PARTICLE)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
v3f pos = readV3F1000(is);
|
|
|
|
v3f vel = readV3F1000(is);
|
|
|
|
v3f acc = readV3F1000(is);
|
|
|
|
float expirationtime = readF1000(is);
|
|
|
|
float size = readF1000(is);
|
2013-01-23 18:32:02 +01:00
|
|
|
bool collisiondetection = readU8(is);
|
2014-02-05 00:17:57 +01:00
|
|
|
std::string texture = deSerializeLongString(is);
|
|
|
|
bool vertical = false;
|
2013-04-22 20:35:10 +02:00
|
|
|
try {
|
|
|
|
vertical = readU8(is);
|
|
|
|
} catch (...) {}
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_SPAWN_PARTICLE;
|
|
|
|
event.spawn_particle.pos = new v3f (pos);
|
|
|
|
event.spawn_particle.vel = new v3f (vel);
|
|
|
|
event.spawn_particle.acc = new v3f (acc);
|
|
|
|
event.spawn_particle.expirationtime = expirationtime;
|
|
|
|
event.spawn_particle.size = size;
|
|
|
|
event.spawn_particle.collisiondetection = collisiondetection;
|
|
|
|
event.spawn_particle.vertical = vertical;
|
|
|
|
event.spawn_particle.texture = new std::string(texture);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
u16 amount = readU16(is);
|
|
|
|
float spawntime = readF1000(is);
|
|
|
|
v3f minpos = readV3F1000(is);
|
|
|
|
v3f maxpos = readV3F1000(is);
|
|
|
|
v3f minvel = readV3F1000(is);
|
|
|
|
v3f maxvel = readV3F1000(is);
|
|
|
|
v3f minacc = readV3F1000(is);
|
|
|
|
v3f maxacc = readV3F1000(is);
|
|
|
|
float minexptime = readF1000(is);
|
|
|
|
float maxexptime = readF1000(is);
|
|
|
|
float minsize = readF1000(is);
|
|
|
|
float maxsize = readF1000(is);
|
2013-01-23 18:32:02 +01:00
|
|
|
bool collisiondetection = readU8(is);
|
2014-02-05 00:17:57 +01:00
|
|
|
std::string texture = deSerializeLongString(is);
|
|
|
|
u32 id = readU32(is);
|
2013-04-22 20:35:10 +02:00
|
|
|
bool vertical = false;
|
|
|
|
try {
|
|
|
|
vertical = readU8(is);
|
|
|
|
} catch (...) {}
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_ADD_PARTICLESPAWNER;
|
|
|
|
event.add_particlespawner.amount = amount;
|
|
|
|
event.add_particlespawner.spawntime = spawntime;
|
|
|
|
event.add_particlespawner.minpos = new v3f (minpos);
|
|
|
|
event.add_particlespawner.maxpos = new v3f (maxpos);
|
|
|
|
event.add_particlespawner.minvel = new v3f (minvel);
|
|
|
|
event.add_particlespawner.maxvel = new v3f (maxvel);
|
|
|
|
event.add_particlespawner.minacc = new v3f (minacc);
|
|
|
|
event.add_particlespawner.maxacc = new v3f (maxacc);
|
|
|
|
event.add_particlespawner.minexptime = minexptime;
|
|
|
|
event.add_particlespawner.maxexptime = maxexptime;
|
|
|
|
event.add_particlespawner.minsize = minsize;
|
|
|
|
event.add_particlespawner.maxsize = maxsize;
|
2013-01-23 18:32:02 +01:00
|
|
|
event.add_particlespawner.collisiondetection = collisiondetection;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.add_particlespawner.vertical = vertical;
|
|
|
|
event.add_particlespawner.texture = new std::string(texture);
|
|
|
|
event.add_particlespawner.id = id;
|
2013-01-23 18:32:02 +01:00
|
|
|
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
|
|
|
|
{
|
|
|
|
std::string datastring((char*)&data[2], datasize-2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
u32 id = readU16(is);
|
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_DELETE_PARTICLESPAWNER;
|
2013-01-23 18:32:02 +01:00
|
|
|
event.delete_particlespawner.id = id;
|
|
|
|
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2013-04-11 20:23:38 +02:00
|
|
|
else if(command == TOCLIENT_HUDADD)
|
|
|
|
{
|
2013-04-14 00:20:22 +02:00
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
2013-04-11 20:23:38 +02:00
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
u32 id = readU32(is);
|
|
|
|
u8 type = readU8(is);
|
|
|
|
v2f pos = readV2F1000(is);
|
|
|
|
std::string name = deSerializeString(is);
|
|
|
|
v2f scale = readV2F1000(is);
|
|
|
|
std::string text = deSerializeString(is);
|
|
|
|
u32 number = readU32(is);
|
|
|
|
u32 item = readU32(is);
|
|
|
|
u32 dir = readU32(is);
|
2013-04-22 11:53:55 +02:00
|
|
|
v2f align = readV2F1000(is);
|
2013-04-23 01:47:59 +02:00
|
|
|
v2f offset = readV2F1000(is);
|
2014-01-26 21:31:59 +01:00
|
|
|
v3f world_pos;
|
2014-04-28 23:41:27 +02:00
|
|
|
v2s32 size;
|
2014-01-26 21:31:59 +01:00
|
|
|
try{
|
|
|
|
world_pos = readV3F1000(is);
|
|
|
|
}catch(SerializationError &e) {};
|
2014-04-28 23:41:27 +02:00
|
|
|
try{
|
|
|
|
size = readV2S32(is);
|
|
|
|
} catch(SerializationError &e) {};
|
2013-04-11 20:23:38 +02:00
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_HUDADD;
|
|
|
|
event.hudadd.id = id;
|
|
|
|
event.hudadd.type = type;
|
|
|
|
event.hudadd.pos = new v2f(pos);
|
|
|
|
event.hudadd.name = new std::string(name);
|
|
|
|
event.hudadd.scale = new v2f(scale);
|
|
|
|
event.hudadd.text = new std::string(text);
|
|
|
|
event.hudadd.number = number;
|
|
|
|
event.hudadd.item = item;
|
|
|
|
event.hudadd.dir = dir;
|
|
|
|
event.hudadd.align = new v2f(align);
|
|
|
|
event.hudadd.offset = new v2f(offset);
|
2014-01-26 21:31:59 +01:00
|
|
|
event.hudadd.world_pos = new v3f(world_pos);
|
2014-04-28 23:41:27 +02:00
|
|
|
event.hudadd.size = new v2s32(size);
|
2013-04-11 20:23:38 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_HUDRM)
|
|
|
|
{
|
2013-04-14 00:20:22 +02:00
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
2013-04-11 20:23:38 +02:00
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
u32 id = readU32(is);
|
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_HUDRM;
|
2013-04-11 20:23:38 +02:00
|
|
|
event.hudrm.id = id;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
else if(command == TOCLIENT_HUDCHANGE)
|
2013-12-01 01:52:06 +01:00
|
|
|
{
|
2013-04-14 00:20:22 +02:00
|
|
|
std::string sdata;
|
|
|
|
v2f v2fdata;
|
2014-01-26 21:31:59 +01:00
|
|
|
v3f v3fdata;
|
2013-04-14 00:20:22 +02:00
|
|
|
u32 intdata = 0;
|
2014-04-28 23:41:27 +02:00
|
|
|
v2s32 v2s32data;
|
2013-04-14 00:20:22 +02:00
|
|
|
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
2013-04-11 20:23:38 +02:00
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2013-04-14 00:20:22 +02:00
|
|
|
u32 id = readU32(is);
|
|
|
|
u8 stat = (HudElementStat)readU8(is);
|
|
|
|
|
2013-04-26 01:27:22 +02:00
|
|
|
if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
|
|
|
|
stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
|
2013-04-11 20:23:38 +02:00
|
|
|
v2fdata = readV2F1000(is);
|
2013-04-14 00:20:22 +02:00
|
|
|
else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
|
2013-04-11 20:23:38 +02:00
|
|
|
sdata = deSerializeString(is);
|
2014-01-26 21:31:59 +01:00
|
|
|
else if (stat == HUD_STAT_WORLD_POS)
|
|
|
|
v3fdata = readV3F1000(is);
|
2014-04-28 23:41:27 +02:00
|
|
|
else if (stat == HUD_STAT_SIZE )
|
|
|
|
v2s32data = readV2S32(is);
|
2013-04-14 00:20:22 +02:00
|
|
|
else
|
|
|
|
intdata = readU32(is);
|
|
|
|
|
2013-04-11 20:23:38 +02:00
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_HUDCHANGE;
|
2013-04-14 00:20:22 +02:00
|
|
|
event.hudchange.id = id;
|
|
|
|
event.hudchange.stat = (HudElementStat)stat;
|
2013-04-11 20:23:38 +02:00
|
|
|
event.hudchange.v2fdata = new v2f(v2fdata);
|
2014-01-26 21:31:59 +01:00
|
|
|
event.hudchange.v3fdata = new v3f(v3fdata);
|
2013-04-14 00:20:22 +02:00
|
|
|
event.hudchange.sdata = new std::string(sdata);
|
|
|
|
event.hudchange.data = intdata;
|
2014-04-28 23:41:27 +02:00
|
|
|
event.hudchange.v2s32data = new v2s32(v2s32data);
|
2013-04-11 20:23:38 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2013-04-26 01:27:22 +02:00
|
|
|
else if(command == TOCLIENT_HUD_SET_FLAGS)
|
2013-12-01 01:52:06 +01:00
|
|
|
{
|
2013-04-24 12:52:46 +02:00
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2013-04-26 01:27:22 +02:00
|
|
|
u32 flags = readU32(is);
|
|
|
|
u32 mask = readU32(is);
|
|
|
|
|
|
|
|
player->hud_flags &= ~mask;
|
|
|
|
player->hud_flags |= flags;
|
2013-04-24 12:52:46 +02:00
|
|
|
}
|
2013-05-04 02:08:52 +02:00
|
|
|
else if(command == TOCLIENT_HUD_SET_PARAM)
|
|
|
|
{
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
u16 param = readU16(is);
|
|
|
|
std::string value = deSerializeString(is);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
|
2013-05-04 02:08:52 +02:00
|
|
|
s32 hotbar_itemcount = readS32((u8*) value.c_str());
|
|
|
|
if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
|
|
|
|
player->hud_hotbar_itemcount = hotbar_itemcount;
|
2014-02-05 00:17:57 +01:00
|
|
|
}
|
|
|
|
else if (param == HUD_PARAM_HOTBAR_IMAGE) {
|
2013-09-03 19:51:40 +02:00
|
|
|
((LocalPlayer *) player)->hotbar_image = value;
|
2014-02-05 00:17:57 +01:00
|
|
|
}
|
|
|
|
else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
|
2013-09-03 19:51:40 +02:00
|
|
|
((LocalPlayer *) player)->hotbar_selected_image = value;
|
2013-05-04 02:08:52 +02:00
|
|
|
}
|
|
|
|
}
|
2013-05-02 22:52:50 +02:00
|
|
|
else if(command == TOCLIENT_SET_SKY)
|
|
|
|
{
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
video::SColor *bgcolor = new video::SColor(readARGB8(is));
|
|
|
|
std::string *type = new std::string(deSerializeString(is));
|
|
|
|
u16 count = readU16(is);
|
2013-05-02 22:52:50 +02:00
|
|
|
std::vector<std::string> *params = new std::vector<std::string>;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2013-05-02 22:52:50 +02:00
|
|
|
for(size_t i=0; i<count; i++)
|
|
|
|
params->push_back(deSerializeString(is));
|
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_SET_SKY;
|
2013-05-02 22:52:50 +02:00
|
|
|
event.set_sky.bgcolor = bgcolor;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.set_sky.type = type;
|
|
|
|
event.set_sky.params = params;
|
2013-05-02 22:52:50 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2013-10-18 10:53:19 +02:00
|
|
|
else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
|
|
|
|
{
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
bool do_override = readU8(is);
|
2013-10-18 10:53:19 +02:00
|
|
|
float day_night_ratio_f = (float)readU16(is) / 65536;
|
|
|
|
|
|
|
|
ClientEvent event;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
|
2013-10-18 10:53:19 +02:00
|
|
|
event.override_day_night_ratio.do_override = do_override;
|
2014-02-05 00:17:57 +01:00
|
|
|
event.override_day_night_ratio.ratio_f = day_night_ratio_f;
|
2013-10-18 10:53:19 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2014-01-08 13:47:53 +01:00
|
|
|
else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
|
|
|
|
{
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
2014-04-12 13:50:22 +02:00
|
|
|
player->local_animations[0] = readV2S32(is);
|
|
|
|
player->local_animations[1] = readV2S32(is);
|
|
|
|
player->local_animations[2] = readV2S32(is);
|
|
|
|
player->local_animations[3] = readV2S32(is);
|
2014-01-08 13:47:53 +01:00
|
|
|
player->local_animation_speed = readF1000(is);
|
|
|
|
}
|
2014-04-11 15:32:46 +02:00
|
|
|
else if(command == TOCLIENT_EYE_OFFSET)
|
|
|
|
{
|
|
|
|
std::string datastring((char *)&data[2], datasize - 2);
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
player->eye_offset_first = readV3F1000(is);
|
|
|
|
player->eye_offset_third = readV3F1000(is);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
else
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Client: Ignoring unknown command "
|
2010-11-27 00:02:21 +01:00
|
|
|
<<command<<std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
|
|
|
|
{
|
2011-04-04 01:05:12 +02:00
|
|
|
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
|
2010-11-27 00:02:21 +01:00
|
|
|
m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
|
|
|
|
}
|
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
void Client::interact(u8 action, const PointedThing& pointed)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2014-02-13 20:17:42 +01:00
|
|
|
if(m_state != LC_Ready){
|
2011-11-29 16:15:18 +01:00
|
|
|
infostream<<"Client::interact() "
|
2010-11-27 00:02:21 +01:00
|
|
|
"cancelled (not connected)"
|
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2011-11-29 16:15:18 +01:00
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
[0] u16 command
|
2010-12-18 12:10:37 +01:00
|
|
|
[2] u8 action
|
2011-11-29 16:15:18 +01:00
|
|
|
[3] u16 item
|
|
|
|
[5] u32 length of the next item
|
|
|
|
[9] serialized PointedThing
|
2010-12-18 12:10:37 +01:00
|
|
|
actions:
|
2011-11-29 16:15:18 +01:00
|
|
|
0: start digging (from undersurface) or use
|
|
|
|
1: stop digging (all parameters ignored)
|
|
|
|
2: digging completed
|
|
|
|
3: place block or item (to abovesurface)
|
|
|
|
4: use item
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2011-11-29 16:15:18 +01:00
|
|
|
writeU16(os, TOSERVER_INTERACT);
|
|
|
|
writeU8(os, action);
|
|
|
|
writeU16(os, getPlayerItem());
|
|
|
|
std::ostringstream tmp_os(std::ios::binary);
|
|
|
|
pointed.serialize(tmp_os);
|
|
|
|
os<<serializeLongString(tmp_os.str());
|
2011-10-15 11:17:21 +02:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
2011-10-15 11:17:21 +02:00
|
|
|
|
2011-11-29 16:15:18 +01:00
|
|
|
// Send as reliable
|
2011-04-10 03:15:10 +02:00
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2012-06-01 19:51:15 +02:00
|
|
|
void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
|
|
|
|
const std::map<std::string, std::string> &fields)
|
2011-04-04 04:12:33 +02:00
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
2012-06-01 19:51:15 +02:00
|
|
|
writeU16(os, TOSERVER_NODEMETA_FIELDS);
|
|
|
|
writeV3S16(os, p);
|
|
|
|
os<<serializeString(formname);
|
2014-02-05 00:17:57 +01:00
|
|
|
size_t fields_size = fields.size();
|
|
|
|
assert(fields_size <= 0xFFFF);
|
|
|
|
writeU16(os, (u16) (fields_size & 0xFFFF));
|
2012-06-01 19:51:15 +02:00
|
|
|
for(std::map<std::string, std::string>::const_iterator
|
|
|
|
i = fields.begin(); i != fields.end(); i++){
|
|
|
|
const std::string &name = i->first;
|
|
|
|
const std::string &value = i->second;
|
|
|
|
os<<serializeString(name);
|
|
|
|
os<<serializeLongString(value);
|
|
|
|
}
|
2011-04-04 04:12:33 +02:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2013-12-01 01:52:06 +01:00
|
|
|
void Client::sendInventoryFields(const std::string &formname,
|
2012-07-22 16:10:58 +02:00
|
|
|
const std::map<std::string, std::string> &fields)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOSERVER_INVENTORY_FIELDS);
|
|
|
|
os<<serializeString(formname);
|
2014-02-05 00:17:57 +01:00
|
|
|
size_t fields_size = fields.size();
|
|
|
|
assert(fields_size <= 0xFFFF);
|
|
|
|
writeU16(os, (u16) (fields_size & 0xFFFF));
|
2012-07-22 16:10:58 +02:00
|
|
|
for(std::map<std::string, std::string>::const_iterator
|
|
|
|
i = fields.begin(); i != fields.end(); i++){
|
2014-02-05 00:17:57 +01:00
|
|
|
const std::string &name = i->first;
|
2012-07-22 16:10:58 +02:00
|
|
|
const std::string &value = i->second;
|
|
|
|
os<<serializeString(name);
|
|
|
|
os<<serializeLongString(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2010-12-22 15:30:23 +01:00
|
|
|
void Client::sendInventoryAction(InventoryAction *a)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
u8 buf[12];
|
|
|
|
|
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOSERVER_INVENTORY_ACTION);
|
|
|
|
os.write((char*)buf, 2);
|
|
|
|
|
|
|
|
a->serialize(os);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
void Client::sendChatMessage(const std::wstring &message)
|
|
|
|
{
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
u8 buf[12];
|
|
|
|
|
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOSERVER_CHAT_MESSAGE);
|
|
|
|
os.write((char*)buf, 2);
|
|
|
|
|
|
|
|
// Write length
|
2014-02-05 00:17:57 +01:00
|
|
|
size_t messagesize = message.size();
|
2014-05-11 15:39:38 +02:00
|
|
|
if (messagesize > 0xFFFF) {
|
|
|
|
messagesize = 0xFFFF;
|
|
|
|
}
|
|
|
|
writeU16(buf, (u16) messagesize);
|
2010-12-23 21:35:53 +01:00
|
|
|
os.write((char*)buf, 2);
|
|
|
|
|
|
|
|
// Write string
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0; i<message.size(); i++)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
|
|
|
u16 w = message[i];
|
|
|
|
writeU16(buf, w);
|
|
|
|
os.write((char*)buf, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2014-02-27 21:12:59 +01:00
|
|
|
void Client::sendChangePassword(const std::wstring &oldpassword,
|
|
|
|
const std::wstring &newpassword)
|
2011-05-22 22:09:12 +02:00
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
if(player == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::string playername = player->getName();
|
|
|
|
std::string oldpwd = translatePassword(playername, oldpassword);
|
|
|
|
std::string newpwd = translatePassword(playername, newpassword);
|
|
|
|
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
u8 buf[2+PASSWORD_SIZE*2];
|
|
|
|
/*
|
|
|
|
[0] u16 TOSERVER_PASSWORD
|
|
|
|
[2] u8[28] old password
|
|
|
|
[30] u8[28] new password
|
|
|
|
*/
|
|
|
|
|
|
|
|
writeU16(buf, TOSERVER_PASSWORD);
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
|
2011-05-22 22:09:12 +02:00
|
|
|
{
|
|
|
|
buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
|
|
|
|
buf[30+i] = i<newpwd.length()?newpwd[i]:0;
|
|
|
|
}
|
|
|
|
buf[2+PASSWORD_SIZE-1] = 0;
|
|
|
|
buf[30+PASSWORD_SIZE-1] = 0;
|
|
|
|
os.write((char*)buf, 2+PASSWORD_SIZE*2);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
void Client::sendDamage(u8 damage)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOSERVER_DAMAGE);
|
|
|
|
writeU8(os, damage);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2013-07-19 19:50:33 +02:00
|
|
|
void Client::sendBreath(u16 breath)
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOSERVER_BREATH);
|
|
|
|
writeU16(os, breath);
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2011-10-15 13:46:59 +02:00
|
|
|
void Client::sendRespawn()
|
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
|
|
|
std::ostringstream os(std::ios_base::binary);
|
|
|
|
|
|
|
|
writeU16(os, TOSERVER_RESPAWN);
|
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
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);
|
|
|
|
|
2014-08-09 22:05:09 +02:00
|
|
|
writeU16(os,strlen(minetest_version_hash));
|
|
|
|
os.write(minetest_version_hash,strlen(minetest_version_hash));
|
2014-02-13 20:17:42 +01:00
|
|
|
|
|
|
|
// Make data buffer
|
|
|
|
std::string s = os.str();
|
|
|
|
SharedBuffer<u8> data((u8*)s.c_str(), s.size());
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Client::sendPlayerPos()
|
|
|
|
{
|
2012-12-02 13:59:08 +01:00
|
|
|
LocalPlayer *myplayer = m_env.getLocalPlayer();
|
2010-11-27 00:02:21 +01:00
|
|
|
if(myplayer == NULL)
|
|
|
|
return;
|
2012-12-02 13:59:08 +01:00
|
|
|
|
|
|
|
// Save bandwidth by only updating position when something changed
|
2014-02-05 00:17:57 +01:00
|
|
|
if(myplayer->last_position == myplayer->getPosition() &&
|
|
|
|
myplayer->last_speed == myplayer->getSpeed() &&
|
|
|
|
myplayer->last_pitch == myplayer->getPitch() &&
|
|
|
|
myplayer->last_yaw == myplayer->getYaw() &&
|
2012-12-02 13:59:08 +01:00
|
|
|
myplayer->last_keyPressed == myplayer->keyPressed)
|
|
|
|
return;
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
myplayer->last_position = myplayer->getPosition();
|
|
|
|
myplayer->last_speed = myplayer->getSpeed();
|
|
|
|
myplayer->last_pitch = myplayer->getPitch();
|
|
|
|
myplayer->last_yaw = myplayer->getYaw();
|
2012-12-02 13:59:08 +01:00
|
|
|
myplayer->last_keyPressed = myplayer->keyPressed;
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
u16 our_peer_id;
|
|
|
|
{
|
2011-04-04 01:05:12 +02:00
|
|
|
//JMutexAutoLock lock(m_con_mutex); //bulk comment-out
|
2010-11-27 00:02:21 +01:00
|
|
|
our_peer_id = m_con.GetPeerID();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set peer id if not set already
|
2011-01-15 02:28:19 +01:00
|
|
|
if(myplayer->peer_id == PEER_ID_INEXISTENT)
|
2010-11-27 00:02:21 +01:00
|
|
|
myplayer->peer_id = our_peer_id;
|
|
|
|
// Check that an existing peer_id is the same as the connection's
|
|
|
|
assert(myplayer->peer_id == our_peer_id);
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
v3f pf = myplayer->getPosition();
|
|
|
|
v3f sf = myplayer->getSpeed();
|
|
|
|
s32 pitch = myplayer->getPitch() * 100;
|
|
|
|
s32 yaw = myplayer->getYaw() * 100;
|
|
|
|
u32 keyPressed = myplayer->keyPressed;
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
v3s32 position(pf.X*100, pf.Y*100, pf.Z*100);
|
|
|
|
v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
|
|
|
|
/*
|
|
|
|
Format:
|
|
|
|
[0] u16 command
|
|
|
|
[2] v3s32 position*100
|
|
|
|
[2+12] v3s32 speed*100
|
|
|
|
[2+12+12] s32 pitch*100
|
|
|
|
[2+12+12+4] s32 yaw*100
|
2012-11-22 20:01:31 +01:00
|
|
|
[2+12+12+4+4] u32 keyPressed
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2012-11-22 20:01:31 +01:00
|
|
|
SharedBuffer<u8> data(2+12+12+4+4+4);
|
2010-11-27 00:02:21 +01:00
|
|
|
writeU16(&data[0], TOSERVER_PLAYERPOS);
|
|
|
|
writeV3S32(&data[2], position);
|
|
|
|
writeV3S32(&data[2+12], speed);
|
|
|
|
writeS32(&data[2+12+12], pitch);
|
2013-12-01 01:52:06 +01:00
|
|
|
writeS32(&data[2+12+12+4], yaw);
|
2012-11-22 20:01:31 +01:00
|
|
|
writeU32(&data[2+12+12+4+4], keyPressed);
|
2010-11-27 00:02:21 +01:00
|
|
|
// Send as unreliable
|
|
|
|
Send(0, data, false);
|
|
|
|
}
|
|
|
|
|
2011-08-10 21:43:40 +02:00
|
|
|
void Client::sendPlayerItem(u16 item)
|
|
|
|
{
|
|
|
|
Player *myplayer = m_env.getLocalPlayer();
|
|
|
|
if(myplayer == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
u16 our_peer_id = m_con.GetPeerID();
|
|
|
|
|
|
|
|
// Set peer id if not set already
|
|
|
|
if(myplayer->peer_id == PEER_ID_INEXISTENT)
|
|
|
|
myplayer->peer_id = our_peer_id;
|
|
|
|
// Check that an existing peer_id is the same as the connection's
|
|
|
|
assert(myplayer->peer_id == our_peer_id);
|
|
|
|
|
|
|
|
SharedBuffer<u8> data(2+2);
|
|
|
|
writeU16(&data[0], TOSERVER_PLAYERITEM);
|
|
|
|
writeU16(&data[2], item);
|
|
|
|
|
|
|
|
// Send as reliable
|
|
|
|
Send(0, data, true);
|
|
|
|
}
|
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
void Client::removeNode(v3s16 p)
|
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<v3s16, MapBlock * >::iterator
|
|
|
|
i = modified_blocks.begin();
|
|
|
|
i != modified_blocks.end(); ++i)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2014-11-07 21:52:43 +01:00
|
|
|
addUpdateMeshTask(i->first, false, false);
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
2014-11-07 21:52:43 +01:00
|
|
|
// add urgent task to update the modified node
|
|
|
|
addUpdateMeshTaskForNode(p, false, true);
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
|
|
|
|
2013-11-23 15:35:49 +01:00
|
|
|
void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2014-11-07 21:52:43 +01:00
|
|
|
//TimeTaker timer1("Client::addNode()");
|
2011-02-23 23:19:41 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
2010-12-23 21:35:53 +01:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2011-06-26 01:34:36 +02:00
|
|
|
//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
|
2013-11-23 15:35:49 +01:00
|
|
|
m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e)
|
|
|
|
{}
|
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
for(std::map<v3s16, MapBlock * >::iterator
|
|
|
|
i = modified_blocks.begin();
|
|
|
|
i != modified_blocks.end(); ++i)
|
2010-12-23 21:35:53 +01:00
|
|
|
{
|
2014-11-07 21:52:43 +01:00
|
|
|
addUpdateMeshTask(i->first, false, false);
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Client::setPlayerControl(PlayerControl &control)
|
|
|
|
{
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
player->control = control;
|
|
|
|
}
|
|
|
|
|
2011-08-10 18:31:44 +02:00
|
|
|
void Client::selectPlayerItem(u16 item)
|
|
|
|
{
|
2011-11-29 16:15:18 +01:00
|
|
|
m_playeritem = item;
|
|
|
|
m_inventory_updated = true;
|
2011-08-10 21:43:40 +02:00
|
|
|
sendPlayerItem(item);
|
2011-08-10 18:31:44 +02:00
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Returns true if the inventory of the local player has been
|
|
|
|
// updated from the server. If it is true, it is set to false.
|
|
|
|
bool Client::getLocalInventoryUpdated()
|
|
|
|
{
|
|
|
|
bool updated = m_inventory_updated;
|
|
|
|
m_inventory_updated = false;
|
|
|
|
return updated;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copies the inventory of the local player to parameter
|
|
|
|
void Client::getLocalInventory(Inventory &dst)
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
dst = player->inventory;
|
|
|
|
}
|
|
|
|
|
2011-12-06 14:21:56 +01:00
|
|
|
Inventory* Client::getInventory(const InventoryLocation &loc)
|
|
|
|
{
|
|
|
|
switch(loc.type){
|
|
|
|
case InventoryLocation::UNDEFINED:
|
|
|
|
{}
|
|
|
|
break;
|
2012-01-12 06:10:39 +01:00
|
|
|
case InventoryLocation::CURRENT_PLAYER:
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
return &player->inventory;
|
|
|
|
}
|
|
|
|
break;
|
2011-12-06 14:21:56 +01:00
|
|
|
case InventoryLocation::PLAYER:
|
|
|
|
{
|
|
|
|
Player *player = m_env.getPlayer(loc.name.c_str());
|
|
|
|
if(!player)
|
|
|
|
return NULL;
|
|
|
|
return &player->inventory;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case InventoryLocation::NODEMETA:
|
|
|
|
{
|
|
|
|
NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
|
|
|
|
if(!meta)
|
|
|
|
return NULL;
|
|
|
|
return meta->getInventory();
|
|
|
|
}
|
|
|
|
break;
|
2012-07-24 19:57:17 +02:00
|
|
|
case InventoryLocation::DETACHED:
|
|
|
|
{
|
|
|
|
if(m_detached_inventories.count(loc.name) == 0)
|
|
|
|
return NULL;
|
|
|
|
return m_detached_inventories[loc.name];
|
|
|
|
}
|
|
|
|
break;
|
2011-12-06 14:21:56 +01:00
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2011-04-04 14:13:19 +02:00
|
|
|
void Client::inventoryAction(InventoryAction *a)
|
|
|
|
{
|
2012-01-22 00:49:02 +01:00
|
|
|
/*
|
|
|
|
Send it to the server
|
|
|
|
*/
|
2011-04-04 14:13:19 +02:00
|
|
|
sendInventoryAction(a);
|
2012-01-22 00:49:02 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Predict some local inventory changes
|
|
|
|
*/
|
|
|
|
a->clientApply(this, this);
|
2013-05-11 00:12:14 +02:00
|
|
|
|
|
|
|
// Remove it
|
|
|
|
delete a;
|
2011-04-04 14:13:19 +02:00
|
|
|
}
|
|
|
|
|
2011-04-07 23:47:14 +02:00
|
|
|
ClientActiveObject * Client::getSelectedActiveObject(
|
|
|
|
f32 max_d,
|
|
|
|
v3f from_pos_f_on_map,
|
|
|
|
core::line3d<f32> shootline_on_map
|
|
|
|
)
|
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
std::vector<DistanceSortedActiveObject> objects;
|
2011-04-07 23:47:14 +02:00
|
|
|
|
|
|
|
m_env.getActiveObjects(from_pos_f_on_map, max_d, objects);
|
|
|
|
|
|
|
|
// Sort them.
|
|
|
|
// After this, the closest object is the first in the array.
|
2012-12-20 18:19:49 +01:00
|
|
|
std::sort(objects.begin(), objects.end());
|
2011-04-07 23:47:14 +02:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
for(unsigned int i=0; i<objects.size(); i++)
|
2011-04-07 23:47:14 +02:00
|
|
|
{
|
|
|
|
ClientActiveObject *obj = objects[i].obj;
|
|
|
|
|
|
|
|
core::aabbox3d<f32> *selection_box = obj->getSelectionBox();
|
|
|
|
if(selection_box == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
v3f pos = obj->getPosition();
|
|
|
|
|
|
|
|
core::aabbox3d<f32> offsetted_box(
|
|
|
|
selection_box->MinEdge + pos,
|
|
|
|
selection_box->MaxEdge + pos
|
|
|
|
);
|
|
|
|
|
|
|
|
if(offsetted_box.intersectsWithLine(shootline_on_map))
|
|
|
|
{
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-03-25 19:13:25 +01:00
|
|
|
std::list<std::string> Client::getConnectedPlayerNames()
|
2011-12-03 09:01:14 +01:00
|
|
|
{
|
2013-03-25 19:13:25 +01:00
|
|
|
return m_env.getPlayerNames();
|
2011-12-03 09:01:14 +01:00
|
|
|
}
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
float Client::getAnimationTime()
|
2010-12-18 16:46:00 +01:00
|
|
|
{
|
2012-03-13 18:56:12 +01:00
|
|
|
return m_animation_time;
|
2010-12-18 16:46:00 +01:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
int Client::getCrackLevel()
|
2011-04-21 18:35:17 +02:00
|
|
|
{
|
2012-03-13 18:56:12 +01:00
|
|
|
return m_crack_level;
|
2011-04-21 18:35:17 +02:00
|
|
|
}
|
|
|
|
|
2014-12-01 13:05:36 +01:00
|
|
|
void Client::setHighlighted(v3s16 pos, bool show_highlighted)
|
2014-09-16 12:38:37 +02:00
|
|
|
{
|
2014-12-01 13:05:36 +01:00
|
|
|
m_show_highlighted = show_highlighted;
|
2014-09-16 12:38:37 +02:00
|
|
|
v3s16 old_highlighted_pos = m_highlighted_pos;
|
|
|
|
m_highlighted_pos = pos;
|
|
|
|
addUpdateMeshTaskForNode(old_highlighted_pos, false, true);
|
|
|
|
addUpdateMeshTaskForNode(m_highlighted_pos, false, true);
|
|
|
|
}
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
void Client::setCrack(int level, v3s16 pos)
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
2012-03-13 18:56:12 +01:00
|
|
|
int old_crack_level = m_crack_level;
|
|
|
|
v3s16 old_crack_pos = m_crack_pos;
|
2011-06-26 01:34:36 +02:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
m_crack_level = level;
|
|
|
|
m_crack_pos = pos;
|
2011-06-26 01:34:36 +02:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
|
|
|
|
{
|
|
|
|
// remove old crack
|
|
|
|
addUpdateMeshTaskForNode(old_crack_pos, false, true);
|
|
|
|
}
|
|
|
|
if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
2012-03-13 18:56:12 +01:00
|
|
|
// add new crack
|
|
|
|
addUpdateMeshTaskForNode(pos, false, true);
|
2011-06-26 01:34:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
u16 Client::getHP()
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
return player->hp;
|
2011-06-26 01:34:36 +02:00
|
|
|
}
|
|
|
|
|
2013-06-19 16:30:22 +02:00
|
|
|
u16 Client::getBreath()
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2013-07-19 19:50:33 +02:00
|
|
|
return player->getBreath();
|
2013-06-19 16:30:22 +02:00
|
|
|
}
|
|
|
|
|
2011-12-03 09:01:14 +01:00
|
|
|
bool Client::getChatMessage(std::wstring &message)
|
|
|
|
{
|
|
|
|
if(m_chat_queue.size() == 0)
|
|
|
|
return false;
|
|
|
|
message = m_chat_queue.pop_front();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::typeChatMessage(const std::wstring &message)
|
|
|
|
{
|
|
|
|
// Discard empty line
|
|
|
|
if(message == L"")
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Send to others
|
|
|
|
sendChatMessage(message);
|
|
|
|
|
|
|
|
// Show locally
|
|
|
|
if (message[0] == L'/')
|
|
|
|
{
|
2014-09-21 02:23:55 +02:00
|
|
|
m_chat_queue.push_back((std::wstring)L"issued command: " + message);
|
2011-12-03 09:01:14 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
std::wstring name = narrow_to_wide(player->getName());
|
2014-09-21 02:23:55 +02:00
|
|
|
m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
|
2011-12-03 09:01:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
|
2010-12-19 15:51:45 +01:00
|
|
|
{
|
2011-04-04 01:05:12 +02:00
|
|
|
MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
|
|
|
|
if(b == NULL)
|
|
|
|
return;
|
2014-09-16 12:38:37 +02:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
/*
|
|
|
|
Create a task to update the mesh of the block
|
|
|
|
*/
|
2014-09-16 12:38:37 +02:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
MeshMakeData *data = new MeshMakeData(this);
|
2014-09-16 12:38:37 +02:00
|
|
|
|
2010-12-19 15:51:45 +01:00
|
|
|
{
|
2011-04-04 01:05:12 +02:00
|
|
|
//TimeTaker timer("data fill");
|
2011-06-26 02:14:52 +02:00
|
|
|
// Release: ~0ms
|
|
|
|
// Debug: 1-6ms, avg=2ms
|
2012-03-13 18:56:12 +01:00
|
|
|
data->fill(b);
|
|
|
|
data->setCrack(m_crack_level, m_crack_pos);
|
2014-12-01 13:05:36 +01:00
|
|
|
data->setHighlighted(m_highlighted_pos, m_show_highlighted);
|
2012-03-13 18:56:12 +01:00
|
|
|
data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
|
2010-12-19 15:51:45 +01:00
|
|
|
}
|
2014-09-16 12:38:37 +02:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
// Add task to queue
|
2012-03-13 18:56:12 +01:00
|
|
|
m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
2010-12-19 15:51:45 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
|
2011-04-04 01:05:12 +02:00
|
|
|
{
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + v3s16(0,0,0);
|
|
|
|
//MapBlock *b = m_env.getMap().getBlockNoCreate(p);
|
2012-03-13 18:56:12 +01:00
|
|
|
addUpdateMeshTask(p, ack_to_server, urgent);
|
2010-12-19 15:51:45 +01:00
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
catch(InvalidPositionException &e){}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2011-04-04 01:05:12 +02:00
|
|
|
// Leading edge
|
2013-04-24 04:12:24 +02:00
|
|
|
for (int i=0;i<6;i++)
|
|
|
|
{
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + g_6dirs[i];
|
|
|
|
addUpdateMeshTask(p, false, urgent);
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){}
|
2012-03-13 18:56:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
v3s16 p = nodepos;
|
|
|
|
infostream<<"Client::addUpdateMeshTaskForNode(): "
|
|
|
|
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
|
|
|
|
<<std::endl;
|
|
|
|
}
|
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
v3s16 blockpos = getNodeBlockPos(nodepos);
|
2012-03-13 18:56:12 +01:00
|
|
|
v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
|
|
|
|
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + v3s16(0,0,0);
|
|
|
|
addUpdateMeshTask(p, ack_to_server, urgent);
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
// Leading edge
|
|
|
|
if(nodepos.X == blockpos_relative.X){
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + v3s16(-1,0,0);
|
|
|
|
addUpdateMeshTask(p, false, urgent);
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){}
|
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
if(nodepos.Y == blockpos_relative.Y){
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + v3s16(0,-1,0);
|
|
|
|
addUpdateMeshTask(p, false, urgent);
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){}
|
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2012-03-13 18:56:12 +01:00
|
|
|
if(nodepos.Z == blockpos_relative.Z){
|
|
|
|
try{
|
|
|
|
v3s16 p = blockpos + v3s16(0,0,-1);
|
|
|
|
addUpdateMeshTask(p, false, urgent);
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){}
|
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
2010-12-19 15:51:45 +01:00
|
|
|
|
2011-04-21 18:35:17 +02:00
|
|
|
ClientEvent Client::getClientEvent()
|
|
|
|
{
|
|
|
|
if(m_client_event_queue.size() == 0)
|
|
|
|
{
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_NONE;
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
return m_client_event_queue.pop_front();
|
|
|
|
}
|
|
|
|
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
float Client::mediaReceiveProgress()
|
|
|
|
{
|
|
|
|
if (m_media_downloader)
|
|
|
|
return m_media_downloader->getProgress();
|
|
|
|
else
|
|
|
|
return 1.0; // downloader only exists when not yet done
|
|
|
|
}
|
|
|
|
|
2013-05-11 16:02:41 +02:00
|
|
|
void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
|
2012-01-12 06:10:39 +01:00
|
|
|
{
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"Client::afterContentReceived() started"<<std::endl;
|
2012-01-12 06:10:39 +01:00
|
|
|
assert(m_itemdef_received);
|
|
|
|
assert(m_nodedef_received);
|
Rewrite client media download and support hash-based remote download
Move most of the media-related code in client.cpp into a new class
ClientMediaDownloader (clientmedia.cpp, clientmedia.h). Among other
things, this class does the following things:
- Download [remote_server][sha1] instead of [remote_server][name]. This
is to support servers that provide the same file name with different
contents.
- Initially fetch [remote_server]index.mth. This file should follow the
Minetest Hashset format (currently version 1) and contain a list of SHA1
hashes that exist on the server.
- The list of needed SHA1s is uploaded (via HTTP POST) when index.mth is
requested, so servers can optionally narrow down the list to the needs
of the client.
- If index.mth is missing (HTTP response code 404), we enter compat mode,
fetching [remote_server][name] as before this commit.
- remote_server can now contain multiple servers, separated by commas.
The downloader code attempts to split requests between the different
servers, as permitted by each server's index.mth. If one server claims
to have a file but actually doesn't (or something fails), we ask a
different server that also claims to have it.
- As before, when none of the remote servers provide a particular
file, we download it via the conventional method, i.e. using
the minetest protocol: TOSERVER_REQUEST_MEDIA / TOCLIENT_MEDIA.
- Bugfix: Every downloaded file's SHA1 is now verified against the SHA1
announced by the minetest server (before loading it and inserting it
into the file cache).
- Bugfix: Only send TOSERVER_RECEIVED_MEDIA when we actually have all
media. This should fix #863.
2013-08-29 05:22:18 +02:00
|
|
|
assert(mediaReceived());
|
2012-05-19 16:11:41 +02:00
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Rebuild inherited images and recreate textures
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"- Rebuilding images and textures"<<std::endl;
|
2012-01-12 06:10:39 +01:00
|
|
|
m_tsrc->rebuildImagesAndTextures();
|
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
// Rebuild shaders
|
2013-07-05 04:24:05 +02:00
|
|
|
infostream<<"- Rebuilding shaders"<<std::endl;
|
2012-03-19 02:59:12 +01:00
|
|
|
m_shsrc->rebuildShaders();
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Update node aliases
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"- Updating node aliases"<<std::endl;
|
2012-01-12 06:10:39 +01:00
|
|
|
m_nodedef->updateAliases(m_itemdef);
|
|
|
|
|
2014-05-14 23:19:31 +02:00
|
|
|
// Update node textures and assign shaders to each tile
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"- Updating node textures"<<std::endl;
|
2014-10-15 04:13:53 +02:00
|
|
|
m_nodedef->updateTextures(this);
|
2012-01-12 06:10:39 +01:00
|
|
|
|
2012-12-01 22:54:15 +01:00
|
|
|
// Preload item textures and meshes if configured to
|
|
|
|
if(g_settings->getBool("preload_item_visuals"))
|
|
|
|
{
|
|
|
|
verbosestream<<"Updating item textures and meshes"<<std::endl;
|
2013-05-11 16:02:41 +02:00
|
|
|
wchar_t* text = wgettext("Item textures...");
|
2014-11-23 13:40:43 +01:00
|
|
|
draw_load_screen(text, device, guienv, 0, 0);
|
2012-12-01 22:54:15 +01:00
|
|
|
std::set<std::string> names = m_itemdef->getAll();
|
2013-05-11 16:02:41 +02:00
|
|
|
size_t size = names.size();
|
|
|
|
size_t count = 0;
|
|
|
|
int percent = 0;
|
2012-12-01 22:54:15 +01:00
|
|
|
for(std::set<std::string>::const_iterator
|
|
|
|
i = names.begin(); i != names.end(); ++i){
|
|
|
|
// Asking for these caches the result
|
|
|
|
m_itemdef->getInventoryTexture(*i, this);
|
|
|
|
m_itemdef->getWieldMesh(*i, this);
|
2013-05-11 16:02:41 +02:00
|
|
|
count++;
|
|
|
|
percent = count*100/size;
|
|
|
|
if (count%50 == 0) // only update every 50 item
|
2014-11-23 13:40:43 +01:00
|
|
|
draw_load_screen(text, device, guienv, 0, percent);
|
2012-12-01 22:54:15 +01:00
|
|
|
}
|
2013-05-11 16:02:41 +02:00
|
|
|
delete[] text;
|
2012-12-01 22:54:15 +01:00
|
|
|
}
|
|
|
|
|
2012-01-12 06:10:39 +01:00
|
|
|
// Start mesh update thread after setting up content definitions
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"- Starting mesh update thread"<<std::endl;
|
2012-01-12 06:10:39 +01:00
|
|
|
m_mesh_update_thread.Start();
|
2012-05-19 16:11:41 +02:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
m_state = LC_Ready;
|
|
|
|
sendReady();
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"Client::afterContentReceived() done"<<std::endl;
|
2012-01-12 06:10:39 +01:00
|
|
|
}
|
|
|
|
|
2011-10-17 16:18:50 +02:00
|
|
|
float Client::getRTT(void)
|
|
|
|
{
|
2014-02-13 20:17:42 +01:00
|
|
|
return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
|
2011-10-17 16:18:50 +02:00
|
|
|
}
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2014-04-26 01:15:46 +02:00
|
|
|
float Client::getCurRate(void)
|
|
|
|
{
|
|
|
|
return ( m_con.getLocalStat(con::CUR_INC_RATE) +
|
|
|
|
m_con.getLocalStat(con::CUR_DL_RATE));
|
|
|
|
}
|
|
|
|
|
|
|
|
float Client::getAvgRate(void)
|
|
|
|
{
|
|
|
|
return ( m_con.getLocalStat(con::AVG_INC_RATE) +
|
|
|
|
m_con.getLocalStat(con::AVG_DL_RATE));
|
|
|
|
}
|
|
|
|
|
2014-09-21 02:23:55 +02:00
|
|
|
void Client::makeScreenshot(IrrlichtDevice *device)
|
|
|
|
{
|
|
|
|
irr::video::IVideoDriver *driver = device->getVideoDriver();
|
|
|
|
irr::video::IImage* const raw_image = driver->createScreenShot();
|
|
|
|
if (raw_image) {
|
2014-11-23 13:40:43 +01:00
|
|
|
irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
|
2014-09-21 02:23:55 +02:00
|
|
|
raw_image->getDimension());
|
|
|
|
|
|
|
|
if (image) {
|
|
|
|
raw_image->copyTo(image);
|
|
|
|
irr::c8 filename[256];
|
|
|
|
snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
|
|
|
|
g_settings->get("screenshot_path").c_str(),
|
|
|
|
device->getTimer()->getRealTime());
|
|
|
|
std::stringstream sstr;
|
|
|
|
if (driver->writeImageToFile(image, filename)) {
|
|
|
|
sstr << "Saved screenshot to '" << filename << "'";
|
|
|
|
} else {
|
|
|
|
sstr << "Failed to save screenshot '" << filename << "'";
|
|
|
|
}
|
|
|
|
m_chat_queue.push_back(narrow_to_wide(sstr.str()));
|
|
|
|
infostream << sstr << std::endl;
|
|
|
|
image->drop();
|
|
|
|
}
|
|
|
|
raw_image->drop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-14 20:41:30 +01:00
|
|
|
// IGameDef interface
|
|
|
|
// Under envlock
|
2012-01-12 06:10:39 +01:00
|
|
|
IItemDefManager* Client::getItemDefManager()
|
2011-11-14 20:41:30 +01:00
|
|
|
{
|
2012-01-12 06:10:39 +01:00
|
|
|
return m_itemdef;
|
2011-11-14 20:41:30 +01:00
|
|
|
}
|
|
|
|
INodeDefManager* Client::getNodeDefManager()
|
|
|
|
{
|
|
|
|
return m_nodedef;
|
|
|
|
}
|
2011-11-17 01:28:46 +01:00
|
|
|
ICraftDefManager* Client::getCraftDefManager()
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
//return m_craftdef;
|
|
|
|
}
|
2011-11-14 20:41:30 +01:00
|
|
|
ITextureSource* Client::getTextureSource()
|
|
|
|
{
|
|
|
|
return m_tsrc;
|
|
|
|
}
|
2012-03-19 02:59:12 +01:00
|
|
|
IShaderSource* Client::getShaderSource()
|
|
|
|
{
|
|
|
|
return m_shsrc;
|
|
|
|
}
|
2014-10-21 05:12:15 +02:00
|
|
|
scene::ISceneManager* Client::getSceneManager()
|
|
|
|
{
|
|
|
|
return m_device->getSceneManager();
|
|
|
|
}
|
2011-11-16 12:03:28 +01:00
|
|
|
u16 Client::allocateUnknownNodeId(const std::string &name)
|
|
|
|
{
|
|
|
|
errorstream<<"Client::allocateUnknownNodeId(): "
|
|
|
|
<<"Client cannot allocate node IDs"<<std::endl;
|
|
|
|
assert(0);
|
|
|
|
return CONTENT_IGNORE;
|
|
|
|
}
|
2012-03-23 11:05:17 +01:00
|
|
|
ISoundManager* Client::getSoundManager()
|
|
|
|
{
|
2012-03-23 14:29:30 +01:00
|
|
|
return m_sound;
|
2012-03-23 11:05:17 +01:00
|
|
|
}
|
2012-03-23 19:23:03 +01:00
|
|
|
MtEventManager* Client::getEventManager()
|
|
|
|
{
|
|
|
|
return m_event;
|
|
|
|
}
|
2011-11-14 20:41:30 +01:00
|
|
|
|
2014-01-06 12:24:06 +01:00
|
|
|
scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
|
|
|
|
{
|
|
|
|
std::map<std::string, std::string>::const_iterator i =
|
|
|
|
m_mesh_data.find(filename);
|
|
|
|
if(i == m_mesh_data.end()){
|
|
|
|
errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
|
|
|
|
<<std::endl;
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
const std::string &data = i->second;
|
2014-01-06 12:24:06 +01:00
|
|
|
scene::ISceneManager *smgr = m_device->getSceneManager();
|
|
|
|
|
|
|
|
// Create the mesh, remove it from cache and return it
|
|
|
|
// This allows unique vertex colors and other properties for each instance
|
|
|
|
Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
|
|
|
|
io::IFileSystem *irrfs = m_device->getFileSystem();
|
2014-02-05 00:17:57 +01:00
|
|
|
io::IReadFile *rfile = irrfs->createMemoryReadFile(
|
2014-01-06 12:24:06 +01:00
|
|
|
*data_rw, data_rw.getSize(), filename.c_str());
|
|
|
|
assert(rfile);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2014-01-06 12:24:06 +01:00
|
|
|
scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
|
|
|
|
rfile->drop();
|
|
|
|
// NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
|
|
|
|
// of uniquely named instances and re-use them
|
|
|
|
mesh->grab();
|
|
|
|
smgr->getMeshCache()->removeMesh(mesh);
|
|
|
|
return mesh;
|
|
|
|
}
|
|
|
|
|