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"
|
2015-01-12 17:01:41 +01:00
|
|
|
#include "network/clientopcodes.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
|
|
|
|
*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
/*
|
|
|
|
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);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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,
|
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
|
|
|
),
|
2015-01-05 18:34:59 +01:00
|
|
|
m_particle_manager(&m_env),
|
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-12-06 15:37:37 +01:00
|
|
|
m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
|
2015-02-11 07:02:16 +01:00
|
|
|
m_cache_enable_shaders = g_settings->getBool("enable_shaders");
|
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();
|
2015-01-30 15:33:23 +01:00
|
|
|
delete localserver;
|
2014-10-09 14:02:02 +02:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2015-01-30 15:33:23 +01:00
|
|
|
void Client::connect(Address address,
|
|
|
|
const std::string &address_name,
|
|
|
|
bool is_local_server)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
DSTACK(__FUNCTION_NAME);
|
2015-01-30 15:33:23 +01:00
|
|
|
|
|
|
|
initLocalMapSaving(address, address_name, is_local_server);
|
|
|
|
|
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;
|
2015-01-04 22:32:31 +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;
|
2015-01-04 22:32:31 +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
|
|
|
*/
|
2015-01-04 22:32:31 +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");
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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);*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// 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;*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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());
|
2015-01-04 22:32:31 +01: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
|
|
|
|
*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
/*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;*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
/*
|
|
|
|
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())
|
|
|
|
{
|
2014-12-12 15:55:40 +01:00
|
|
|
if(sendlist.empty())
|
2011-06-26 23:27:17 +02:00
|
|
|
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);
|
2015-01-04 22:32:31 +01: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;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2012-03-24 18:01:26 +01:00
|
|
|
/*
|
|
|
|
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
|
2014-12-12 15:55:40 +01:00
|
|
|
if(!removed_server_ids.empty())
|
2012-03-24 18:01:26 +01:00
|
|
|
{
|
|
|
|
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());
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2012-03-25 13:47:51 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-30 15:33:23 +01:00
|
|
|
void Client::initLocalMapSaving(const Address &address,
|
|
|
|
const std::string &hostname,
|
|
|
|
bool is_local_server)
|
|
|
|
{
|
|
|
|
localdb = NULL;
|
|
|
|
|
|
|
|
if (!g_settings->getBool("enable_local_map_saving") || is_local_server)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const std::string world_path = porting::path_user
|
|
|
|
+ DIR_DELIM + "worlds"
|
|
|
|
+ DIR_DELIM + "server_"
|
|
|
|
+ hostname + "_" + to_string(address.getPort());
|
|
|
|
|
|
|
|
SubgameSpec gamespec;
|
|
|
|
|
|
|
|
if (!getWorldExists(world_path)) {
|
|
|
|
gamespec = findSubgame(g_settings->get("default_game"));
|
|
|
|
if (!gamespec.isValid())
|
|
|
|
gamespec = findSubgame("minimal");
|
|
|
|
} else {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Deprecated(ToClientPacket* pkt)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2015-01-12 17:01:41 +01:00
|
|
|
infostream << "Got deprecated command "
|
|
|
|
<< toClientCommandTable[pkt->getCommand()].name << " from peer "
|
|
|
|
<< pkt->getPeerId() << "!" << std::endl;
|
|
|
|
}
|
2010-11-27 16:18:34 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Init(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
if(pkt->getSize() < 1)
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
u8 deployed;
|
|
|
|
*pkt >> deployed;
|
2010-11-27 16:18:34 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
infostream << "Client: TOCLIENT_INIT received with "
|
|
|
|
"deployed=" << ((int)deployed & 0xff) << std::endl;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(!ser_ver_supported(deployed)) {
|
|
|
|
infostream << "Client: TOCLIENT_INIT: Server sent "
|
|
|
|
<< "unsupported ser_fmt_ver"<< std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_server_ser_ver = deployed;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Get player position
|
|
|
|
v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
|
|
|
|
if(pkt->getSize() >= 1 + 6) {
|
|
|
|
*pkt >> playerpos_s16;
|
|
|
|
}
|
|
|
|
v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Set player position
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
player->setPosition(playerpos_f);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(pkt->getSize() >= 1 + 6 + 8) {
|
|
|
|
// Get map seed
|
|
|
|
*pkt >> m_map_seed;
|
|
|
|
infostream << "Client: received map seed: " << m_map_seed << std::endl;
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(pkt->getSize() >= 1 + 6 + 8 + 4) {
|
|
|
|
*pkt >> m_recommended_send_interval;
|
|
|
|
infostream << "Client: received recommended send interval "
|
|
|
|
<< m_recommended_send_interval<<std::endl;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +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);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_state = LC_Init;
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_AccessDenied(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
m_access_denied_reason = L"Unknown";
|
|
|
|
if(pkt->getSize() >= 2) {
|
|
|
|
*pkt >> m_access_denied_reason;
|
|
|
|
}
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_RemoveNode(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
if(pkt->getSize() < 6)
|
|
|
|
return;
|
2012-11-26 21:31:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
v3s16 p;
|
|
|
|
*pkt >> p.X;
|
|
|
|
*pkt >> p.Y;
|
|
|
|
*pkt >> p.Z;
|
|
|
|
removeNode(p);
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_AddNode(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
if(pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
|
|
|
|
return;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
v3s16 p;
|
|
|
|
*pkt >> p.X;
|
|
|
|
*pkt >> p.Y;
|
|
|
|
*pkt >> p.Z;
|
2014-02-13 20:17:42 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
MapNode n;
|
|
|
|
n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
|
|
|
|
|
|
|
|
bool remove_metadata = true;
|
|
|
|
u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
|
|
|
|
if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
|
|
|
|
remove_metadata = false;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2011-05-20 21:28:03 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
addNode(p, n, remove_metadata);
|
|
|
|
}
|
|
|
|
void Client::handleCommand_BlockData(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
// Ignore too small packet
|
|
|
|
if(pkt->getSize() < 6)
|
2011-05-20 21:28:03 +02:00
|
|
|
return;
|
2015-01-12 17:01:41 +01:00
|
|
|
|
|
|
|
v3s16 p;
|
|
|
|
*pkt >> p.X;
|
|
|
|
*pkt >> p.Y;
|
|
|
|
*pkt >> p.Z;
|
|
|
|
|
|
|
|
std::string datastring(pkt->getString(6), pkt->getSize() - 6);
|
|
|
|
std::istringstream istr(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
MapSector *sector;
|
|
|
|
MapBlock *block;
|
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
block->deSerialize(istr, m_server_ser_ver, false);
|
|
|
|
block->deSerializeNetworkSpecific(istr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
Create a new block
|
|
|
|
*/
|
|
|
|
block = new MapBlock(&m_env.getMap(), p, this);
|
|
|
|
block->deSerialize(istr, m_server_ser_ver, false);
|
|
|
|
block->deSerializeNetworkSpecific(istr);
|
|
|
|
sector->insertBlock(block);
|
2011-05-20 21:28:03 +02:00
|
|
|
}
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if (localdb != NULL) {
|
|
|
|
((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2014-02-05 00:17:57 +01:00
|
|
|
/*
|
2015-01-12 17:01:41 +01:00
|
|
|
Add it to mesh update queue and set it to be acknowledged after update.
|
2014-02-05 00:17:57 +01:00
|
|
|
*/
|
2015-01-12 17:01:41 +01:00
|
|
|
addUpdateMeshTaskWithEdge(p, true);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Inventory(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
if(pkt->getSize() < 1)
|
|
|
|
return;
|
2010-12-24 02:08:05 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2010-12-24 02:08:05 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
player->inventory.deSerialize(is);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_inventory_updated = true;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
delete m_inventory_from_server;
|
|
|
|
m_inventory_from_server = new Inventory(player->inventory);
|
|
|
|
m_inventory_from_server_age = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_TimeOfDay(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
if(pkt->getSize() < 2)
|
|
|
|
return;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
u16 time_of_day;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> time_of_day;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
time_of_day = time_of_day % 24000;
|
|
|
|
float time_speed = 0;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(pkt->getSize() >= 2 + 4) {
|
|
|
|
*pkt >> time_speed;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// 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;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +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;
|
2011-06-26 02:14:52 +02:00
|
|
|
else
|
2015-01-12 17:01:41 +01:00
|
|
|
tod_diff_f = time_of_day_f - m_last_time_of_day_f;
|
2011-04-04 01:05:12 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_last_time_of_day_f = time_of_day_f;
|
|
|
|
float time_diff = m_time_of_day_update_timer;
|
|
|
|
m_time_of_day_update_timer = 0;
|
2014-10-09 14:02:02 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(m_time_of_day_set){
|
|
|
|
time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
|
|
|
|
infostream << "Client: Measured time_of_day speed (old format): "
|
|
|
|
<< time_speed << " tod_diff_f=" << tod_diff_f
|
|
|
|
<< " time_diff=" << time_diff << std::endl;
|
|
|
|
}
|
2011-04-04 01:05:12 +02:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Update environment
|
|
|
|
m_env.setTimeOfDay(time_of_day);
|
|
|
|
m_env.setTimeOfDaySpeed(time_speed);
|
|
|
|
m_time_of_day_set = true;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
u32 dr = m_env.getDayNightRatio();
|
|
|
|
infostream << "Client: time_of_day=" << time_of_day
|
|
|
|
<< " time_speed=" << time_speed
|
|
|
|
<< " dr=" << dr << std::endl;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_ChatMessage(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 length
|
|
|
|
wstring message
|
|
|
|
*/
|
|
|
|
u16 len, read_wchar;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> len;
|
2012-01-22 00:49:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
std::wstring message;
|
|
|
|
for(unsigned int i=0; i<len; i++) {
|
|
|
|
*pkt >> read_wchar;
|
|
|
|
message += (wchar_t)read_wchar;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_chat_queue.push_back(message);
|
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_ActiveObjectRemoveAdd(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 count of removed objects
|
|
|
|
for all removed objects {
|
|
|
|
u16 id
|
2014-02-05 00:17:57 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
u16 count of added objects
|
|
|
|
for all added objects {
|
|
|
|
u16 id
|
|
|
|
u8 type
|
|
|
|
u32 initialization data length
|
|
|
|
string initialization data
|
2010-12-29 14:26:47 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
*/
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Read removed objects
|
|
|
|
u8 type;
|
|
|
|
u16 removed_count, added_count, id;
|
2012-03-16 15:34:30 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> removed_count;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
for(u16 i=0; i<removed_count; i++) {
|
|
|
|
*pkt >> id;
|
|
|
|
m_env.removeActiveObject(id);
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Read added objects
|
|
|
|
*pkt >> added_count;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
for(u16 i=0; i<added_count; i++) {
|
|
|
|
*pkt >> id >> type;
|
|
|
|
m_env.addActiveObject(id, type, pkt->readLongString());
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
}
|
2011-02-20 23:45:14 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_ActiveObjectMessages(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
for all objects
|
2014-02-05 00:17:57 +01:00
|
|
|
{
|
2015-01-12 17:01:41 +01:00
|
|
|
u16 id
|
|
|
|
u16 message length
|
|
|
|
string message
|
2014-02-05 00:17:57 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
*/
|
|
|
|
char buf[6];
|
|
|
|
// Get all data except the command number
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
// Throw them in an istringstream
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
while(is.eof() == false) {
|
|
|
|
is.read(buf, 2);
|
|
|
|
u16 id = readU16((u8*)buf);
|
|
|
|
if(is.eof())
|
|
|
|
break;
|
2014-02-05 00:17:57 +01:00
|
|
|
is.read(buf, 2);
|
2015-01-12 17:01:41 +01:00
|
|
|
size_t message_size = readU16((u8*)buf);
|
|
|
|
std::string message;
|
|
|
|
message.reserve(message_size);
|
|
|
|
for(unsigned int i=0; i<message_size; i++)
|
2014-02-05 00:17:57 +01:00
|
|
|
{
|
|
|
|
is.read(buf, 1);
|
2015-01-12 17:01:41 +01:00
|
|
|
message.append(buf, 1);
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
// Pass on to the environment
|
|
|
|
m_env.processActiveObjectMessage(id, message);
|
2011-02-20 23:45:14 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
}
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Movement(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
|
2012-01-24 00:00:26 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
|
|
|
|
>> lf >> lfs >> ls >> g;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
player->movement_acceleration_default = mad * BS;
|
|
|
|
player->movement_acceleration_air = maa * BS;
|
|
|
|
player->movement_acceleration_fast = maf * BS;
|
|
|
|
player->movement_speed_walk = msw * BS;
|
|
|
|
player->movement_speed_crouch = mscr * BS;
|
|
|
|
player->movement_speed_fast = msf * BS;
|
|
|
|
player->movement_speed_climb = mscl * BS;
|
|
|
|
player->movement_speed_jump = msj * BS;
|
|
|
|
player->movement_liquid_fluidity = lf * BS;
|
|
|
|
player->movement_liquid_fluidity_smooth = lfs * BS;
|
|
|
|
player->movement_liquid_sink = ls * BS;
|
|
|
|
player->movement_gravity = g * BS;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_HP(ToClientPacket* pkt)
|
|
|
|
{
|
2011-04-21 18:35:17 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
u8 oldhp = player->hp;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
u8 hp;
|
|
|
|
*pkt >> hp;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
player->hp = hp;
|
|
|
|
|
|
|
|
if(hp < oldhp) {
|
|
|
|
// Add to ClientEvent queue
|
2011-10-15 13:46:59 +02:00
|
|
|
ClientEvent event;
|
2015-01-12 17:01:41 +01:00
|
|
|
event.type = CE_PLAYER_DAMAGE;
|
|
|
|
event.player_damage.amount = oldhp - hp;
|
2011-10-15 13:46:59 +02:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2015-01-12 17:01:41 +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
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Breath(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
u16 breath;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> breath;
|
2012-01-02 12:31:50 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
player->setBreath(breath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_MovePlayer(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
v3f pos;
|
|
|
|
f32 pitch, yaw;
|
|
|
|
|
|
|
|
*pkt >> pos >> pitch >> yaw;
|
|
|
|
|
|
|
|
player->setPosition(pos);
|
|
|
|
|
|
|
|
infostream << "Client got TOCLIENT_MOVE_PLAYER"
|
|
|
|
<< " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
|
|
|
|
<< " pitch=" << pitch
|
|
|
|
<< " yaw=" << yaw
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_PlayerItem(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_DeathScreen(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
bool set_camera_point_target;
|
|
|
|
v3f camera_point_target;
|
|
|
|
|
|
|
|
*pkt >> set_camera_point_target;
|
|
|
|
*pkt >> camera_point_target;
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_DEATHSCREEN;
|
|
|
|
event.deathscreen.set_camera_point_target = set_camera_point_target;
|
|
|
|
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;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_AnnounceMedia(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u16 num_files;
|
|
|
|
|
|
|
|
*pkt >> num_files;
|
|
|
|
|
|
|
|
infostream << "Client: Received media announcement: packet size: "
|
|
|
|
<< pkt->getSize() << std::endl;
|
|
|
|
|
|
|
|
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=" << pkt->getSize() << std::endl;
|
|
|
|
return;
|
2012-01-02 12:31:50 +01:00
|
|
|
}
|
2011-11-15 10:02:47 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
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
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
for(int i=0; i<num_files; i++) {
|
|
|
|
std::string name, sha1_base64;
|
2013-06-13 03:05:47 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> name >> sha1_base64;
|
|
|
|
|
|
|
|
std::string sha1_raw = base64_decode(sha1_base64);
|
|
|
|
m_media_downloader->addFile(name, sha1_raw);
|
|
|
|
}
|
2013-06-13 03:05:47 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
std::vector<std::string> remote_media;
|
|
|
|
try {
|
|
|
|
std::string str;
|
|
|
|
|
|
|
|
*pkt >> str;
|
|
|
|
|
|
|
|
Strfnd sf(str);
|
|
|
|
while(!sf.atend()) {
|
|
|
|
std::string baseurl = trim(sf.next(","));
|
|
|
|
if(baseurl != "")
|
|
|
|
m_media_downloader->addRemoteServer(baseurl);
|
2011-11-15 10:02:47 +01:00
|
|
|
}
|
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
catch(SerializationError& e) {
|
|
|
|
// not supported by server or turned off
|
2011-11-15 22:58:56 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
|
|
|
|
m_media_downloader->step(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_Media(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
u16 command
|
|
|
|
u16 total number of file bunches
|
|
|
|
u16 index of this bunch
|
|
|
|
u32 number of files in this bunch
|
|
|
|
for each file {
|
|
|
|
u16 length of name
|
|
|
|
string name
|
|
|
|
u32 length of data
|
|
|
|
data
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
u16 num_bunches;
|
|
|
|
u16 bunch_i;
|
|
|
|
u32 num_files;
|
|
|
|
|
|
|
|
*pkt >> num_bunches >> bunch_i >> num_files;
|
|
|
|
|
|
|
|
infostream << "Client: Received files: bunch " << bunch_i << "/"
|
|
|
|
<< num_bunches << " files=" << num_files
|
|
|
|
<< " size=" << pkt->getSize() << std::endl;
|
|
|
|
|
|
|
|
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 << "! "
|
|
|
|
<< " bunch " << bunch_i << "/" << num_bunches
|
|
|
|
<< " files=" << num_files
|
|
|
|
<< " size=" << pkt->getSize() << std::endl;
|
|
|
|
return;
|
2012-01-12 06:10:39 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
|
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
|
|
|
for(unsigned int i=0; i<num_files; i++) {
|
|
|
|
std::string name;
|
|
|
|
|
|
|
|
*pkt >> name;
|
|
|
|
|
|
|
|
std::string data = pkt->readLongString();
|
|
|
|
|
|
|
|
m_media_downloader->conventionalTransferDone(
|
|
|
|
name, data, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_ToolDef(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_NodeDef(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
infostream << "Client: Received node definitions: packet size: "
|
|
|
|
<< pkt->getSize() << std::endl;
|
|
|
|
|
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
|
|
|
// Decompress node definitions
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
|
|
|
|
std::ostringstream tmp_os;
|
|
|
|
decompressZlib(tmp_is, tmp_os);
|
|
|
|
|
|
|
|
// Deserialize node definitions
|
|
|
|
std::istringstream tmp_is2(tmp_os.str());
|
|
|
|
m_nodedef->deSerialize(tmp_is2);
|
|
|
|
m_nodedef_received = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_CraftItemDef(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_ItemDef(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
infostream << "Client: Received item definitions: packet size: "
|
|
|
|
<< pkt->getSize() << std::endl;
|
|
|
|
|
|
|
|
// Mesh update thread must be stopped while
|
|
|
|
// updating content definitions
|
|
|
|
assert(!m_mesh_update_thread.IsRunning());
|
|
|
|
|
|
|
|
// Decompress item definitions
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_PlaySound(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
s32 server_id;
|
|
|
|
std::string name;
|
|
|
|
float gain;
|
|
|
|
u8 type; // 0=local, 1=positional, 2=object
|
|
|
|
v3f pos;
|
|
|
|
u16 object_id;
|
|
|
|
bool loop;
|
|
|
|
|
|
|
|
*pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
|
|
|
|
|
|
|
|
// Start playing
|
|
|
|
int client_id = -1;
|
|
|
|
switch(type) {
|
2012-03-24 18:01:26 +01:00
|
|
|
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;
|
2015-01-12 17:01:41 +01:00
|
|
|
case 2:
|
|
|
|
{ // object
|
2012-03-24 18:01:26 +01:00
|
|
|
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;
|
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
default:
|
|
|
|
break;
|
2012-03-24 18:01:26 +01:00
|
|
|
}
|
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
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;
|
2012-03-31 15:23:26 +02:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
}
|
2012-07-19 13:09:16 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_StopSound(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
s32 server_id;
|
2012-07-24 19:57:17 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> server_id;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
std::map<s32, int>::iterator i =
|
|
|
|
m_sounds_server_to_client.find(server_id);
|
2012-07-24 19:57:17 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
if(i != m_sounds_server_to_client.end()) {
|
|
|
|
int client_id = i->second;
|
|
|
|
m_sound->stopSound(client_id);
|
2012-07-24 19:57:17 +02:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
}
|
2013-01-02 20:45:04 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_Privileges(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
m_privileges.clear();
|
|
|
|
infostream << "Client: Privileges updated: ";
|
|
|
|
u16 num_privileges;
|
2013-01-02 20:45:04 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> num_privileges;
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
for(unsigned int i=0; i<num_privileges; i++) {
|
|
|
|
std::string priv;
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> priv;
|
|
|
|
|
|
|
|
m_privileges.insert(priv);
|
|
|
|
infostream << priv << " ";
|
2013-01-23 18:32:02 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
infostream << std::endl;
|
|
|
|
}
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_InventoryFormSpec(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Store formspec in LocalPlayer
|
|
|
|
player->inventory_formspec = pkt->readLongString();
|
|
|
|
}
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_DetachedInventory(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_ShowFormSpec(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string formspec = pkt->readLongString();
|
|
|
|
std::string formname;
|
2013-01-23 18:32:02 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> formname;
|
2013-04-11 20:23:38 +02:00
|
|
|
|
2015-01-12 17:01:41 +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);
|
|
|
|
event.show_formspec.formname = new std::string(formname);
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2013-04-11 20:23:38 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_SpawnParticle(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2013-04-11 20:23:38 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
v3f pos = readV3F1000(is);
|
|
|
|
v3f vel = readV3F1000(is);
|
|
|
|
v3f acc = readV3F1000(is);
|
|
|
|
float expirationtime = readF1000(is);
|
|
|
|
float size = readF1000(is);
|
|
|
|
bool collisiondetection = readU8(is);
|
|
|
|
std::string texture = deSerializeLongString(is);
|
|
|
|
bool vertical = false;
|
|
|
|
try {
|
|
|
|
vertical = readU8(is);
|
|
|
|
} catch (...) {}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
ClientEvent event;
|
|
|
|
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-04-24 12:52:46 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_AddParticleSpawner(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u16 amount;
|
|
|
|
float spawntime;
|
|
|
|
v3f minpos;
|
|
|
|
v3f maxpos;
|
|
|
|
v3f minvel;
|
|
|
|
v3f maxvel;
|
|
|
|
v3f minacc;
|
|
|
|
v3f maxacc;
|
|
|
|
float minexptime;
|
|
|
|
float maxexptime;
|
|
|
|
float minsize;
|
|
|
|
float maxsize;
|
|
|
|
bool collisiondetection;
|
|
|
|
u32 id;
|
2013-05-04 02:08:52 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
|
|
|
|
>> minacc >> maxacc >> minexptime >> maxexptime >> minsize
|
|
|
|
>> maxsize >> collisiondetection;
|
2013-05-04 02:08:52 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
std::string texture = pkt->readLongString();
|
2013-05-02 22:52:50 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
*pkt >> id;
|
2014-02-05 00:17:57 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
bool vertical = false;
|
|
|
|
try {
|
|
|
|
*pkt >> vertical;
|
|
|
|
} catch (...) {}
|
2013-05-02 22:52:50 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
ClientEvent event;
|
|
|
|
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;
|
|
|
|
event.add_particlespawner.collisiondetection = collisiondetection;
|
|
|
|
event.add_particlespawner.vertical = vertical;
|
|
|
|
event.add_particlespawner.texture = new std::string(texture);
|
|
|
|
event.add_particlespawner.id = id;
|
|
|
|
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_DeleteParticleSpawner(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u16 id;
|
|
|
|
|
|
|
|
*pkt >> id;
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_DELETE_PARTICLESPAWNER;
|
|
|
|
event.delete_particlespawner.id = id;
|
|
|
|
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_HudAdd(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
|
|
|
|
|
|
|
u32 id;
|
|
|
|
u8 type;
|
|
|
|
v2f pos;
|
|
|
|
std::string name;
|
|
|
|
v2f scale;
|
|
|
|
std::string text;
|
|
|
|
u32 number;
|
|
|
|
u32 item;
|
|
|
|
u32 dir;
|
|
|
|
v2f align;
|
|
|
|
v2f offset;
|
|
|
|
v3f world_pos;
|
|
|
|
v2s32 size;
|
|
|
|
|
|
|
|
*pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
|
|
|
|
>> dir >> align >> offset;
|
|
|
|
try {
|
|
|
|
*pkt >> world_pos;
|
|
|
|
}
|
|
|
|
catch(SerializationError &e) {};
|
|
|
|
|
|
|
|
try {
|
|
|
|
*pkt >> size;
|
|
|
|
} catch(SerializationError &e) {};
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
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);
|
|
|
|
event.hudadd.world_pos = new v3f(world_pos);
|
|
|
|
event.hudadd.size = new v2s32(size);
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_HudRemove(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u32 id;
|
|
|
|
|
|
|
|
*pkt >> id;
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_HUDRM;
|
|
|
|
event.hudrm.id = id;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_HudChange(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string sdata;
|
|
|
|
v2f v2fdata;
|
|
|
|
v3f v3fdata;
|
|
|
|
u32 intdata = 0;
|
|
|
|
v2s32 v2s32data;
|
|
|
|
u32 id;
|
|
|
|
u8 stat;
|
|
|
|
|
|
|
|
*pkt >> id >> stat;
|
|
|
|
|
|
|
|
if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
|
|
|
|
stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
|
|
|
|
*pkt >> v2fdata;
|
|
|
|
else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
|
|
|
|
*pkt >> sdata;
|
|
|
|
else if (stat == HUD_STAT_WORLD_POS)
|
|
|
|
*pkt >> v3fdata;
|
|
|
|
else if (stat == HUD_STAT_SIZE )
|
|
|
|
*pkt >> v2s32data;
|
|
|
|
else
|
|
|
|
*pkt >> intdata;
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_HUDCHANGE;
|
|
|
|
event.hudchange.id = id;
|
|
|
|
event.hudchange.stat = (HudElementStat)stat;
|
|
|
|
event.hudchange.v2fdata = new v2f(v2fdata);
|
|
|
|
event.hudchange.v3fdata = new v3f(v3fdata);
|
|
|
|
event.hudchange.sdata = new std::string(sdata);
|
|
|
|
event.hudchange.data = intdata;
|
|
|
|
event.hudchange.v2s32data = new v2s32(v2s32data);
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_HudSetFlags(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u32 flags, mask;
|
|
|
|
|
|
|
|
*pkt >> flags >> mask;
|
|
|
|
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
player->hud_flags &= ~mask;
|
|
|
|
player->hud_flags |= flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_HudSetParam(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
u16 param; std::string value;
|
|
|
|
|
|
|
|
*pkt >> param >> value;
|
|
|
|
|
|
|
|
Player *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
|
|
|
|
s32 hotbar_itemcount = readS32((u8*) value.c_str());
|
|
|
|
if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
|
|
|
|
player->hud_hotbar_itemcount = hotbar_itemcount;
|
2013-05-02 22:52:50 +02:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
else if (param == HUD_PARAM_HOTBAR_IMAGE) {
|
|
|
|
((LocalPlayer *) player)->hotbar_image = value;
|
|
|
|
}
|
|
|
|
else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
|
|
|
|
((LocalPlayer *) player)->hotbar_selected_image = value;
|
|
|
|
}
|
|
|
|
}
|
2013-10-18 10:53:19 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
void Client::handleCommand_HudSetSky(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
std::string datastring(pkt->getString(0), pkt->getSize());
|
|
|
|
std::istringstream is(datastring, std::ios_base::binary);
|
2013-10-18 10:53:19 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
video::SColor *bgcolor = new video::SColor(readARGB8(is));
|
|
|
|
std::string *type = new std::string(deSerializeString(is));
|
|
|
|
u16 count = readU16(is);
|
|
|
|
std::vector<std::string> *params = new std::vector<std::string>;
|
|
|
|
|
|
|
|
for(size_t i=0; i<count; i++)
|
|
|
|
params->push_back(deSerializeString(is));
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_SET_SKY;
|
|
|
|
event.set_sky.bgcolor = bgcolor;
|
|
|
|
event.set_sky.type = type;
|
|
|
|
event.set_sky.params = params;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_OverrideDayNightRatio(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
bool do_override;
|
|
|
|
u16 day_night_ratio_u;
|
|
|
|
|
|
|
|
*pkt >> do_override >> day_night_ratio_u;
|
|
|
|
|
|
|
|
float day_night_ratio_f = (float)day_night_ratio_u / 65536;
|
|
|
|
|
|
|
|
ClientEvent event;
|
|
|
|
event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
|
|
|
|
event.override_day_night_ratio.do_override = do_override;
|
|
|
|
event.override_day_night_ratio.ratio_f = day_night_ratio_f;
|
|
|
|
m_client_event_queue.push_back(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_LocalPlayerAnimations(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
*pkt >> player->local_animations[0];
|
|
|
|
*pkt >> player->local_animations[1];
|
|
|
|
*pkt >> player->local_animations[2];
|
|
|
|
*pkt >> player->local_animations[3];
|
|
|
|
*pkt >> player->local_animation_speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::handleCommand_EyeOffset(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
LocalPlayer *player = m_env.getLocalPlayer();
|
|
|
|
assert(player != NULL);
|
|
|
|
|
|
|
|
*pkt >> player->eye_offset_first >> player->eye_offset_third;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Client::handleCommand(ToClientPacket* pkt)
|
|
|
|
{
|
|
|
|
const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
|
|
|
|
(this->*opHandle.handler)(pkt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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);
|
|
|
|
|
|
|
|
// Ignore packets that don't even fit a command
|
|
|
|
if(datasize < 2) {
|
|
|
|
m_packetcounter.add(60000);
|
|
|
|
return;
|
2013-10-18 10:53:19 +02:00
|
|
|
}
|
2014-01-08 13:47:53 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
ToClientPacket* pkt = new ToClientPacket(data, datasize, sender_peer_id);
|
|
|
|
|
|
|
|
ToClientCommand command = pkt->getCommand();
|
2014-01-08 13:47:53 +01:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
//infostream<<"Client: received command="<<command<<std::endl;
|
|
|
|
m_packetcounter.add((u16)command);
|
|
|
|
|
|
|
|
/*
|
|
|
|
If this check is removed, be sure to change the queue
|
|
|
|
system to know the ids
|
|
|
|
*/
|
|
|
|
if(sender_peer_id != PEER_ID_SERVER) {
|
|
|
|
infostream << "Client::ProcessData(): Discarding data not "
|
|
|
|
"coming from server: peer_id=" << sender_peer_id
|
|
|
|
<< std::endl;
|
|
|
|
delete pkt;
|
|
|
|
return;
|
2014-01-08 13:47:53 +01:00
|
|
|
}
|
2014-04-11 15:32:46 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
// Command must be handled into ToClientCommandHandler
|
|
|
|
if (command >= TOCLIENT_NUM_MSG_TYPES) {
|
|
|
|
infostream << "Client: Ignoring unknown command "
|
|
|
|
<< command << std::endl;
|
|
|
|
}
|
2014-04-11 15:32:46 +02:00
|
|
|
|
2015-01-12 17:01:41 +01:00
|
|
|
/*
|
|
|
|
* Those packets are handled before m_server_ser_ver is set, it's normal
|
|
|
|
* But we must use the new ToClientConnectionState in the future,
|
|
|
|
* as a byte mask
|
|
|
|
*/
|
|
|
|
if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
|
|
|
|
handleCommand(pkt);
|
|
|
|
delete pkt;
|
|
|
|
return;
|
2014-04-11 15:32:46 +02:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
|
|
|
|
if(m_server_ser_ver == SER_FMT_VER_INVALID) {
|
|
|
|
infostream << "Client: Server serialization"
|
|
|
|
" format invalid or not initialized."
|
|
|
|
" Skipping incoming command=" << command << std::endl;
|
|
|
|
delete pkt;
|
|
|
|
return;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2015-01-12 17:01:41 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Handle runtime commands
|
|
|
|
*/
|
|
|
|
|
|
|
|
handleCommand(pkt);
|
|
|
|
delete pkt;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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];
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-22 15:30:23 +01:00
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOSERVER_INVENTORY_ACTION);
|
|
|
|
os.write((char*)buf, 2);
|
|
|
|
|
|
|
|
a->serialize(os);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-22 15:30:23 +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
|
|
|
|
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];
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
// Write command
|
|
|
|
writeU16(buf, TOSERVER_CHAT_MESSAGE);
|
|
|
|
os.write((char*)buf, 2);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
// 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);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-23 21:35:53 +01:00
|
|
|
// 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);
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-12-23 21:35:53 +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);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// 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);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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-12-08 02:34:29 +01:00
|
|
|
addUpdateMeshTaskWithEdge(i->first, 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)
|
|
|
|
{}
|
2015-01-04 22:32:31 +01:00
|
|
|
|
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-12-08 02:34:29 +01:00
|
|
|
addUpdateMeshTaskWithEdge(i->first, false, true);
|
2010-12-23 21:35:53 +01:00
|
|
|
}
|
|
|
|
}
|
2015-01-04 22:32:31 +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);
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-04-07 23:47:14 +02:00
|
|
|
// 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;
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2011-04-07 23:47:14 +02:00
|
|
|
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
|
|
|
|
2015-02-11 07:02:16 +01:00
|
|
|
MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
|
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);
|
2014-12-06 15:37:37 +01:00
|
|
|
data->setSmoothLighting(m_cache_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{
|
2014-12-08 02:34:29 +01:00
|
|
|
addUpdateMeshTask(blockpos, 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{
|
2014-12-08 02:34:29 +01:00
|
|
|
addUpdateMeshTask(blockpos, 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());
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2015-02-01 23:59:23 +01:00
|
|
|
const wchar_t* text = wgettext("Loading textures...");
|
2014-08-23 12:16:46 +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;
|
2014-08-23 12:16:46 +02:00
|
|
|
draw_load_screen(text,device, guienv, 0, 70);
|
2012-01-12 06:10:39 +01:00
|
|
|
m_tsrc->rebuildImagesAndTextures();
|
2014-08-23 12:16:46 +02:00
|
|
|
delete[] text;
|
2012-01-12 06:10:39 +01:00
|
|
|
|
2012-03-19 02:59:12 +01:00
|
|
|
// Rebuild shaders
|
2013-07-05 04:24:05 +02:00
|
|
|
infostream<<"- Rebuilding shaders"<<std::endl;
|
2014-08-23 12:16:46 +02:00
|
|
|
text = wgettext("Rebuilding shaders...");
|
|
|
|
draw_load_screen(text, device, guienv, 0, 75);
|
2012-03-19 02:59:12 +01:00
|
|
|
m_shsrc->rebuildShaders();
|
2014-08-23 12:16:46 +02:00
|
|
|
delete[] text;
|
2012-03-19 02:59:12 +01:00
|
|
|
|
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;
|
2014-08-23 12:16:46 +02:00
|
|
|
text = wgettext("Initializing nodes...");
|
|
|
|
draw_load_screen(text, device, guienv, 0, 80);
|
2012-01-12 06:10:39 +01:00
|
|
|
m_nodedef->updateAliases(m_itemdef);
|
2015-01-04 22:32:31 +01:00
|
|
|
m_nodedef->setNodeRegistrationStatus(true);
|
|
|
|
m_nodedef->runNodeResolverCallbacks();
|
2014-08-23 12:16:46 +02:00
|
|
|
delete[] text;
|
2012-01-12 06:10:39 +01:00
|
|
|
|
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;
|
2014-08-23 12:16:46 +02:00
|
|
|
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
|
2014-08-23 12:16:46 +02:00
|
|
|
i = names.begin(); i != names.end(); ++i)
|
|
|
|
{
|
2012-12-01 22:54:15 +01:00
|
|
|
// 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++;
|
2014-08-23 12:16:46 +02:00
|
|
|
percent = (count * 100 / size * 0.2) + 80;
|
|
|
|
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();
|
2015-01-04 22:32:31 +01:00
|
|
|
|
2014-02-13 20:17:42 +01:00
|
|
|
m_state = LC_Ready;
|
|
|
|
sendReady();
|
2014-08-23 12:16:46 +02:00
|
|
|
text = wgettext("Done!");
|
|
|
|
draw_load_screen(text, device, guienv, 0, 100);
|
2012-11-30 15:19:19 +01:00
|
|
|
infostream<<"Client::afterContentReceived() done"<<std::endl;
|
2014-08-23 12:16:46 +02:00
|
|
|
delete[] text;
|
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());
|
2014-12-12 22:31:29 +01:00
|
|
|
std::ostringstream sstr;
|
2014-09-21 02:23:55 +02:00
|
|
|
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()));
|
2014-12-12 22:31:29 +01:00
|
|
|
infostream << sstr.str() << std::endl;
|
2014-09-21 02:23:55 +02:00
|
|
|
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
|
|
|
|
2015-01-05 18:34:59 +01:00
|
|
|
ParticleManager* Client::getParticleManager()
|
|
|
|
{
|
|
|
|
return &m_particle_manager;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|