#include "log.h"
#include "nodemetadata.h"
#include "nodedef.h"
-#include "tooldef.h"
-#include "craftitemdef.h"
+#include "itemdef.h"
#include <IFileSystem.h>
+#include "sha1.h"
+#include "base64.h"
+#include "clientmap.h"
+
+static std::string getTextureCacheDir()
+{
+ return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
+}
+
+struct TextureRequest
+{
+ std::string name;
+
+ TextureRequest(const std::string &name_=""):
+ name(name_)
+ {}
+};
/*
QueuedMeshUpdate
{
JMutexAutoLock lock(m_mutex);
- core::list<QueuedMeshUpdate*>::Iterator i;
- for(i=m_queue.begin(); i!=m_queue.end(); i++)
+ for(std::vector<QueuedMeshUpdate*>::iterator
+ i = m_queue.begin();
+ i != m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
delete q;
/*
peer_id=0 adds with nobody to send to
*/
-void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
+void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
{
DSTACK(__FUNCTION_NAME);
JMutexAutoLock lock(m_mutex);
+ if(urgent)
+ m_urgents.insert(p);
+
/*
Find if block is already in queue.
If it is, update the data and quit.
*/
- core::list<QueuedMeshUpdate*>::Iterator i;
- for(i=m_queue.begin(); i!=m_queue.end(); i++)
+ for(std::vector<QueuedMeshUpdate*>::iterator
+ i = m_queue.begin();
+ i != m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
if(q->p == p)
{
JMutexAutoLock lock(m_mutex);
- core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
- if(i == m_queue.end())
- return NULL;
- QueuedMeshUpdate *q = *i;
- m_queue.erase(i);
- return q;
+ 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;
}
/*
ScopeProfiler sp(g_profiler, "Client: Mesh making");
- scene::SMesh *mesh_new = NULL;
- mesh_new = makeMapBlockMesh(q->data, m_gamedef);
+ MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
+ if(mesh_new->getMesh()->getMeshBufferCount() == 0)
+ {
+ delete mesh_new;
+ mesh_new = NULL;
+ }
MeshUpdateResult r;
r.p = q->p;
std::string password,
MapDrawControl &control,
IWritableTextureSource *tsrc,
- IWritableToolDefManager *tooldef,
- IWritableNodeDefManager *nodedef,
- IWritableCraftItemDefManager *craftitemdef
+ IWritableItemDefManager *itemdef,
+ IWritableNodeDefManager *nodedef
):
m_tsrc(tsrc),
- m_tooldef(tooldef),
+ m_itemdef(itemdef),
m_nodedef(nodedef),
- m_craftitemdef(craftitemdef),
m_mesh_update_thread(this),
m_env(
new ClientMap(this, this, control,
m_server_ser_ver(SER_FMT_VER_INVALID),
m_playeritem(0),
m_inventory_updated(false),
- m_time_of_day(0),
+ m_inventory_from_server(NULL),
+ m_inventory_from_server_age(0.0),
+ m_animation_time(0),
+ m_crack_level(-1),
+ m_crack_pos(0,0,0),
m_map_seed(0),
m_password(password),
m_access_denied(false),
m_texture_receive_progress(0),
m_textures_received(false),
- m_tooldef_received(false),
+ m_itemdef_received(false),
m_nodedef_received(false),
- m_craftitemdef_received(false)
+ m_time_of_day_set(false),
+ m_last_time_of_day_f(-1),
+ m_time_of_day_update_timer(0)
{
m_packetcounter_timer = 0.0;
//m_delete_unused_sectors_timer = 0.0;
else
infostream<<"Not building texture atlas."<<std::endl;
- // Update node textures
- m_nodedef->updateTextures(m_tsrc);
-
- // Start threads after setting up content definitions
- m_mesh_update_thread.Start();
-
/*
Add local player
*/
player->updateName(playername);
m_env.addPlayer(player);
-
- // Initialize player in the inventory context
- m_inventory_context.current_player = player;
}
}
m_mesh_update_thread.setRun(false);
while(m_mesh_update_thread.IsRunning())
sleep_ms(100);
+
+ delete m_inventory_from_server;
}
void Client::connect(Address address)
else
m_ignore_damage_timer = 0.0;
+ m_animation_time += dtime;
+ if(m_animation_time > 60.0)
+ m_animation_time -= 60.0;
+
+ m_time_of_day_update_timer += dtime;
+
//infostream<<"Client steps "<<dtime<<std::endl;
{
MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
if(block)
{
- block->replaceMesh(r.mesh);
+ //JMutexAutoLock lock(block->mesh_mutex);
+
+ // 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;
}
if(r.ack_block_to_server)
{
}
}
}
+
+ /*
+ 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;
+ }
+ }
}
// Virtual methods from con::PeerHandler
//TimeTaker t1("TOCLIENT_REMOVENODE");
- // This will clear the cracking animation after digging
- ((ClientMap&)m_env.getMap()).clearTempMod(p);
-
removeNode(p);
}
else if(command == TOCLIENT_ADDNODE)
Update an existing block
*/
//infostream<<"Updating"<<std::endl;
- block->deSerialize(istr, ser_version);
+ block->deSerialize(istr, ser_version, false);
}
else
{
*/
//infostream<<"Creating new"<<std::endl;
block = new MapBlock(&m_env.getMap(), p, this);
- block->deSerialize(istr, ser_version);
+ block->deSerialize(istr, ser_version, false);
sector->insertBlock(block);
}
m_con.Send(PEER_ID_SERVER, 1, reply, true);
#endif
- /*
- Update Mesh of this block and blocks at x-, y- and z-.
- Environment should not be locked as it interlocks with the
- main thread, from which is will want to retrieve textures.
- */
-
- //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
/*
Add it to mesh update queue and set it to be acknowledged after update.
*/
//t4.stop();
//TimeTaker t1("inventory.deSerialize()", m_device);
- player->inventory.deSerialize(is, this);
+ player->inventory.deSerialize(is);
//t1.stop();
m_inventory_updated = true;
+ delete m_inventory_from_server;
+ m_inventory_from_server = new Inventory(player->inventory);
+ m_inventory_from_server_age = 0.0;
+
//infostream<<"Client got player inventory:"<<std::endl;
//player->inventory.print(infostream);
}
u16 time_of_day = readU16(&data[2]);
time_of_day = time_of_day % 24000;
//infostream<<"Client: time_of_day="<<time_of_day<<std::endl;
-
- /*
- time_of_day:
- 0 = midnight
- 12000 = midday
- */
- {
- m_env.setTimeOfDay(time_of_day);
-
- u32 dr = m_env.getDayNightRatio();
-
- infostream<<"Client: time_of_day="<<time_of_day
- <<", dr="<<dr
- <<std::endl;
+ float time_speed = 0;
+ if(datasize >= 2 + 2 + 4){
+ time_speed = readF1000(&data[4]);
+ } 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;
+ if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
+ tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
+ else
+ tod_diff_f = time_of_day_f - m_last_time_of_day_f;
+ 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;
+ 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;
+ }
}
+
+ // Update environment
+ m_env.setTimeOfDay(time_of_day);
+ m_env.setTimeOfDaySpeed(time_speed);
+ m_time_of_day_set = true;
+ u32 dr = m_env.getDayNightRatio();
+ verbosestream<<"Client: time_of_day="<<time_of_day
+ <<" time_speed="<<time_speed
+ <<" dr="<<dr<<std::endl;
}
else if(command == TOCLIENT_CHAT_MESSAGE)
{
std::istringstream is(datastring, std::ios_base::binary);
Player *player = m_env.getLocalPlayer();
assert(player != NULL);
+ u8 oldhp = player->hp;
u8 hp = readU8(is);
player->hp = hp;
+
+ if(hp < oldhp)
+ {
+ // Add to ClientEvent queue
+ ClientEvent event;
+ event.type = CE_PLAYER_DAMAGE;
+ event.player_damage.amount = oldhp - hp;
+ m_client_event_queue.push_back(event);
+ }
}
else if(command == TOCLIENT_MOVE_PLAYER)
{
} else {
InventoryList *inv = player->inventory.getList("main");
std::string itemstring(deSerializeString(is));
- if (itemstring.empty()) {
- inv->deleteItem(0);
- infostream
- <<"Client: empty player item for peer "
- << peer_id << std::endl;
- } else {
- std::istringstream iss(itemstring);
- delete inv->changeItem(0,
- InventoryItem::deSerialize(iss, this));
- infostream<<"Client: player item for peer " << peer_id << ": ";
- player->getWieldItem()->serialize(infostream);
- infostream<<std::endl;
+ ItemStack item;
+ item.deSerialize(itemstring, m_itemdef);
+ inv->changeItem(0, item);
+ if(itemstring.empty())
+ {
+ infostream<<"Client: empty player item for peer "
+ <<peer_id<<std::endl;
+ }
+ else
+ {
+ infostream<<"Client: player item for peer "
+ <<peer_id<<": "<<itemstring<<std::endl;
}
}
}
event.deathscreen.camera_point_target_z = camera_point_target.Z;
m_client_event_queue.push_back(event);
}
- else if(command == TOCLIENT_TEXTURES)
+ else if(command == TOCLIENT_ANNOUNCE_TEXTURES)
{
io::IFileSystem *irrfs = m_device->getFileSystem();
video::IVideoDriver *vdrv = m_device->getVideoDriver();
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
- // Stop threads while updating content definitions
- m_mesh_update_thread.setRun(false);
- // Process the remaining TextureSource queue to let MeshUpdateThread
- // get it's remaining textures and thus let it stop
- while(m_mesh_update_thread.IsRunning()){
- m_tsrc->processQueue();
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ assert(!m_mesh_update_thread.IsRunning());
+
+ int num_textures = readU16(is);
+
+ core::list<TextureRequest> texture_requests;
+
+ for(int i=0; i<num_textures; i++){
+
+ bool texture_found = false;
+
+ //read texture from cache
+ std::string name = deSerializeString(is);
+ std::string sha1_texture = deSerializeString(is);
+
+ // if name contains illegal characters, ignore the texture
+ if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
+ errorstream<<"Client: ignoring illegal texture name "
+ <<"sent by server: \""<<name<<"\""<<std::endl;
+ continue;
+ }
+
+ std::string tpath = getTextureCacheDir() + DIR_DELIM + name;
+ // Read data
+ std::ifstream fis(tpath.c_str(), std::ios_base::binary);
+
+
+ if(fis.good() == false){
+ infostream<<"Client::Texture not found in cache: "
+ <<name << " expected it at: "<<tpath<<std::endl;
+ }
+ else
+ {
+ std::ostringstream tmp_os(std::ios_base::binary);
+ bool bad = false;
+ for(;;){
+ char buf[1024];
+ fis.read(buf, 1024);
+ std::streamsize len = fis.gcount();
+ tmp_os.write(buf, len);
+ if(fis.eof())
+ break;
+ if(!fis.good()){
+ bad = true;
+ break;
+ }
+ }
+ if(bad){
+ infostream<<"Client: Failed to read texture from cache\""
+ <<name<<"\""<<std::endl;
+ }
+ else {
+
+ SHA1 sha1;
+ sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
+
+ unsigned char *digest = sha1.getDigest();
+
+ std::string digest_string = base64_encode(digest, 20);
+
+ if (digest_string == sha1_texture) {
+ // Silly irrlicht's const-incorrectness
+ Buffer<char> data_rw(tmp_os.str().c_str(), tmp_os.str().size());
+
+ // Create an irrlicht memory file
+ io::IReadFile *rfile = irrfs->createMemoryReadFile(
+ *data_rw, tmp_os.str().size(), "_tempreadfile");
+ assert(rfile);
+ // Read image
+ video::IImage *img = vdrv->createImageFromFile(rfile);
+ if(!img){
+ infostream<<"Client: Cannot create image from data of "
+ <<"received texture \""<<name<<"\""<<std::endl;
+ rfile->drop();
+ }
+ else {
+ m_tsrc->insertSourceImage(name, img);
+ img->drop();
+ rfile->drop();
+
+ texture_found = true;
+ }
+ }
+ else {
+ infostream<<"Client::Texture cached sha1 hash not matching server hash: "
+ <<name << ": server ->"<<sha1_texture <<" client -> "<<digest_string<<std::endl;
+ }
+
+ free(digest);
+ }
+ }
+
+ //add texture request
+ if (!texture_found) {
+ infostream<<"Client: Adding texture to request list: \""
+ <<name<<"\""<<std::endl;
+ texture_requests.push_back(TextureRequest(name));
+ }
+
}
-
+
+ ClientEvent event;
+ event.type = CE_TEXTURES_UPDATED;
+ m_client_event_queue.push_back(event);
+
+
+ //send Texture request
+ /*
+ u16 command
+ u16 number of textures requested
+ for each texture {
+ u16 length of name
+ string name
+ }
+ */
+ std::ostringstream os(std::ios_base::binary);
+ u8 buf[12];
+
+
+ // Write command
+ writeU16(buf, TOSERVER_REQUEST_TEXTURES);
+ os.write((char*)buf, 2);
+
+ writeU16(buf,texture_requests.size());
+ os.write((char*)buf, 2);
+
+
+ for(core::list<TextureRequest>::Iterator i = texture_requests.begin();
+ i != texture_requests.end(); i++) {
+ os<<serializeString(i->name);
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ Send(0, data, true);
+ infostream<<"Client: Sending request list to server " <<std::endl;
+ }
+ else if(command == TOCLIENT_TEXTURES)
+ {
+ io::IFileSystem *irrfs = m_device->getFileSystem();
+ video::IVideoDriver *vdrv = m_device->getVideoDriver();
+
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ assert(!m_mesh_update_thread.IsRunning());
+
/*
u16 command
u16 total number of texture bunches
for(int i=0; i<num_textures; i++){
std::string name = deSerializeString(is);
std::string data = deSerializeLongString(is);
+
+ // if name contains illegal characters, ignore the texture
+ if(!string_allowed(name, TEXTURENAME_ALLOWED_CHARS)){
+ errorstream<<"Client: ignoring illegal texture name "
+ <<"sent by server: \""<<name<<"\""<<std::endl;
+ continue;
+ }
+
// Silly irrlicht's const-incorrectness
Buffer<char> data_rw(data.c_str(), data.size());
// Create an irrlicht memory file
rfile->drop();
continue;
}
+
+ fs::CreateAllDirs(getTextureCacheDir());
+
+ std::string filename = getTextureCacheDir() + DIR_DELIM + name;
+ std::ofstream outfile(filename.c_str(), std::ios_base::binary | std::ios_base::trunc);
+
+ if (outfile.good()) {
+ outfile.write(data.c_str(),data.length());
+ outfile.close();
+ }
+ else {
+ errorstream<<"Client: Unable to open cached texture file "<< filename <<std::endl;
+ }
+
m_tsrc->insertSourceImage(name, img);
img->drop();
rfile->drop();
}
-
- if(m_nodedef_received && m_textures_received){
- // Rebuild inherited images and recreate textures
- m_tsrc->rebuildImagesAndTextures();
-
- // Update texture atlas
- if(g_settings->getBool("enable_texture_atlas"))
- m_tsrc->buildMainAtlas(this);
-
- // Update node textures
- m_nodedef->updateTextures(m_tsrc);
- }
-
- // Resume threads
- m_mesh_update_thread.setRun(true);
- m_mesh_update_thread.Start();
ClientEvent event;
event.type = CE_TEXTURES_UPDATED;
}
else if(command == TOCLIENT_TOOLDEF)
{
- infostream<<"Client: Received tool definitions: packet size: "
- <<datasize<<std::endl;
-
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- m_tooldef_received = true;
-
- // Stop threads while updating content definitions
- m_mesh_update_thread.setRun(false);
- // Process the remaining TextureSource queue to let MeshUpdateThread
- // get it's remaining textures and thus let it stop
- while(m_mesh_update_thread.IsRunning()){
- m_tsrc->processQueue();
- }
-
- std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- m_tooldef->deSerialize(tmp_is);
-
- // Resume threads
- m_mesh_update_thread.setRun(true);
- m_mesh_update_thread.Start();
+ infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
}
else if(command == TOCLIENT_NODEDEF)
{
infostream<<"Client: Received node definitions: packet size: "
<<datasize<<std::endl;
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ assert(!m_mesh_update_thread.IsRunning());
+
+ // Decompress node definitions
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
- m_nodedef_received = true;
-
- // Stop threads while updating content definitions
- m_mesh_update_thread.stop();
-
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- m_nodedef->deSerialize(tmp_is, this);
-
- if(m_textures_received){
- // Update texture atlas
- if(g_settings->getBool("enable_texture_atlas"))
- m_tsrc->buildMainAtlas(this);
-
- // Update node textures
- m_nodedef->updateTextures(m_tsrc);
- }
+ std::ostringstream tmp_os;
+ decompressZlib(tmp_is, tmp_os);
- // Resume threads
- m_mesh_update_thread.setRun(true);
- m_mesh_update_thread.Start();
+ // Deserialize node definitions
+ std::istringstream tmp_is2(tmp_os.str());
+ m_nodedef->deSerialize(tmp_is2);
+ m_nodedef_received = true;
}
else if(command == TOCLIENT_CRAFTITEMDEF)
{
- infostream<<"Client: Received CraftItem definitions: packet size: "
+ infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
+ }
+ else if(command == TOCLIENT_ITEMDEF)
+ {
+ infostream<<"Client: Received item definitions: packet size: "
<<datasize<<std::endl;
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ assert(!m_mesh_update_thread.IsRunning());
+
+ // Decompress item definitions
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
-
- m_craftitemdef_received = true;
-
- // Stop threads while updating content definitions
- m_mesh_update_thread.setRun(false);
- // Process the remaining TextureSource queue to let MeshUpdateThread
- // get it's remaining textures and thus let it stop
- while(m_mesh_update_thread.IsRunning()){
- m_tsrc->processQueue();
- }
-
std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- m_craftitemdef->deSerialize(tmp_is);
-
- // Resume threads
- m_mesh_update_thread.setRun(true);
- m_mesh_update_thread.Start();
+ 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;
}
else
{
void Client::removeNode(v3s16 p)
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-
core::map<v3s16, MapBlock*> modified_blocks;
try
{
}
+ // add urgent task to update the modified node
+ addUpdateMeshTaskForNode(p, false, true);
+
for(core::map<v3s16, MapBlock * >::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
- //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
addUpdateMeshTaskWithEdge(p);
}
}
void Client::addNode(v3s16 p, MapNode n)
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-
TimeTaker timer1("Client::addNode()");
core::map<v3s16, MapBlock*> modified_blocks;
try
{
//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
- std::string st = std::string("");
- m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st);
+ m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
}
catch(InvalidPositionException &e)
{}
- //TimeTaker timer2("Client::addNode(): updateMeshes");
-
for(core::map<v3s16, MapBlock * >::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
- //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
addUpdateMeshTaskWithEdge(p);
}
}
-void Client::updateCamera(v3f pos, v3f dir, f32 fov)
-{
- m_env.getClientMap().updateCamera(pos, dir, fov);
-}
-
-void Client::renderPostFx()
-{
- m_env.getClientMap().renderPostFx();
-}
-
-MapNode Client::getNode(v3s16 p)
-{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- return m_env.getMap().getNode(p);
-}
-
-NodeMetadata* Client::getNodeMetadata(v3s16 p)
-{
- return m_env.getMap().getNodeMetadata(p);
-}
-
-LocalPlayer* Client::getLocalPlayer()
-{
- return m_env.getLocalPlayer();
-}
-
void Client::setPlayerControl(PlayerControl &control)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
m_playeritem = item;
m_inventory_updated = true;
-
- LocalPlayer *player = m_env.getLocalPlayer();
- assert(player != NULL);
- player->wieldItem(item);
-
sendPlayerItem(item);
}
dst = player->inventory;
}
-InventoryContext *Client::getInventoryContext()
-{
- return &m_inventory_context;
-}
-
Inventory* Client::getInventory(const InventoryLocation &loc)
{
switch(loc.type){
case InventoryLocation::UNDEFINED:
{}
break;
+ case InventoryLocation::CURRENT_PLAYER:
+ {
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ return &player->inventory;
+ }
+ break;
case InventoryLocation::PLAYER:
{
Player *player = m_env.getPlayer(loc.name.c_str());
}
return NULL;
}
-#if 0
-Inventory* Client::getInventory(InventoryContext *c, std::string id)
-{
- if(id == "current_player")
- {
- assert(c->current_player);
- return &(c->current_player->inventory);
- }
-
- Strfnd fn(id);
- std::string id0 = fn.next(":");
-
- if(id0 == "nodemeta")
- {
- v3s16 p;
- p.X = stoi(fn.next(","));
- p.Y = stoi(fn.next(","));
- p.Z = stoi(fn.next(","));
- NodeMetadata* meta = getNodeMetadata(p);
- if(meta)
- return meta->getInventory();
- infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
- <<"no metadata found"<<std::endl;
- return NULL;
- }
-
- infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
- return NULL;
-}
-#endif
void Client::inventoryAction(InventoryAction *a)
{
+ /*
+ Send it to the server
+ */
sendInventoryAction(a);
+
+ /*
+ Predict some local inventory changes
+ */
+ a->clientApply(this, this);
}
ClientActiveObject * Client::getSelectedActiveObject(
//<<", m_opt_not_found_history.size()="<<m_opt_not_found_history.size()
<<std::endl;*/
}
-
-u32 Client::getDayNightRatio()
+
+core::list<std::wstring> Client::getConnectedPlayerNames()
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- return m_env.getDayNightRatio();
+ core::list<Player*> players = m_env.getPlayers(true);
+ core::list<std::wstring> playerNames;
+ for(core::list<Player*>::Iterator
+ i = players.begin();
+ i != players.end(); i++)
+ {
+ Player *player = *i;
+ playerNames.push_back(narrow_to_wide(player->getName()));
+ }
+ return playerNames;
}
-u16 Client::getHP()
+float Client::getAnimationTime()
{
- Player *player = m_env.getLocalPlayer();
- assert(player != NULL);
- return player->hp;
+ return m_animation_time;
}
-void Client::setTempMod(v3s16 p, NodeMod mod)
+int Client::getCrackLevel()
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
+ return m_crack_level;
+}
- core::map<v3s16, MapBlock*> affected_blocks;
- ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
- &affected_blocks);
+void Client::setCrack(int level, v3s16 pos)
+{
+ int old_crack_level = m_crack_level;
+ v3s16 old_crack_pos = m_crack_pos;
- for(core::map<v3s16, MapBlock*>::Iterator
- i = affected_blocks.getIterator();
- i.atEnd() == false; i++)
+ m_crack_level = level;
+ m_crack_pos = pos;
+
+ if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
{
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+ // remove old crack
+ addUpdateMeshTaskForNode(old_crack_pos, false, true);
+ }
+ if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
+ {
+ // add new crack
+ addUpdateMeshTaskForNode(pos, false, true);
}
}
-void Client::clearTempMod(v3s16 p)
+u16 Client::getHP()
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ return player->hp;
+}
- core::map<v3s16, MapBlock*> affected_blocks;
- ((ClientMap&)m_env.getMap()).clearTempMod(p,
- &affected_blocks);
+bool Client::getChatMessage(std::wstring &message)
+{
+ if(m_chat_queue.size() == 0)
+ return false;
+ message = m_chat_queue.pop_front();
+ return true;
+}
- for(core::map<v3s16, MapBlock*>::Iterator
- i = affected_blocks.getIterator();
- i.atEnd() == false; i++)
+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'/')
+ {
+ m_chat_queue.push_back(
+ (std::wstring)L"issued command: "+message);
+ }
+ else
{
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+ LocalPlayer *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ std::wstring name = narrow_to_wide(player->getName());
+ m_chat_queue.push_back(
+ (std::wstring)L"<"+name+L"> "+message);
}
}
-void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
+void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
{
/*infostream<<"Client::addUpdateMeshTask(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+ <<" ack_to_server="<<ack_to_server
+ <<" urgent="<<urgent
<<std::endl;*/
MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
Create a task to update the mesh of the block
*/
- MeshMakeData *data = new MeshMakeData;
+ MeshMakeData *data = new MeshMakeData(this);
{
//TimeTaker timer("data fill");
// Release: ~0ms
// Debug: 1-6ms, avg=2ms
- data->fill(getDayNightRatio(), b);
+ data->fill(b);
+ data->setCrack(m_crack_level, m_crack_pos);
+ data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
}
// Debug wait
//while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
// Add task to queue
- m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
+ m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
/*infostream<<"Mesh update input queue size is "
<<m_mesh_update_thread.m_queue_in.size()
<<std::endl;*/
-
-#if 0
- // Temporary test: make mesh directly in here
- {
- //TimeTaker timer("make mesh");
- // 10ms
- scene::SMesh *mesh_new = NULL;
- mesh_new = makeMapBlockMesh(data);
- b->replaceMesh(mesh_new);
- delete data;
- }
-#endif
-
- /*
- Mark mesh as non-expired at this point so that it can already
- be marked as expired again if the data changes
- */
- b->setMeshExpired(false);
}
-void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
+void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
{
/*{
v3s16 p = blockpos;
try{
v3s16 p = blockpos + v3s16(0,0,0);
//MapBlock *b = m_env.getMap().getBlockNoCreate(p);
- addUpdateMeshTask(p, ack_to_server);
+ addUpdateMeshTask(p, ack_to_server, urgent);
}
catch(InvalidPositionException &e){}
// Leading edge
try{
v3s16 p = blockpos + v3s16(-1,0,0);
- addUpdateMeshTask(p);
+ addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,-1,0);
- addUpdateMeshTask(p);
+ addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,-1);
- addUpdateMeshTask(p);
+ addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
}
+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;
+ }
+
+ v3s16 blockpos = getNodeBlockPos(nodepos);
+ v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
+
+ try{
+ v3s16 p = blockpos + v3s16(0,0,0);
+ addUpdateMeshTask(p, ack_to_server, urgent);
+ }
+ catch(InvalidPositionException &e){}
+ // Leading edge
+ if(nodepos.X == blockpos_relative.X){
+ try{
+ v3s16 p = blockpos + v3s16(-1,0,0);
+ addUpdateMeshTask(p, false, urgent);
+ }
+ catch(InvalidPositionException &e){}
+ }
+ if(nodepos.Y == blockpos_relative.Y){
+ try{
+ v3s16 p = blockpos + v3s16(0,-1,0);
+ addUpdateMeshTask(p, false, urgent);
+ }
+ catch(InvalidPositionException &e){}
+ }
+ if(nodepos.Z == blockpos_relative.Z){
+ try{
+ v3s16 p = blockpos + v3s16(0,0,-1);
+ addUpdateMeshTask(p, false, urgent);
+ }
+ catch(InvalidPositionException &e){}
+ }
+}
+
ClientEvent Client::getClientEvent()
{
if(m_client_event_queue.size() == 0)
return m_client_event_queue.pop_front();
}
+void Client::afterContentReceived()
+{
+ assert(m_itemdef_received);
+ assert(m_nodedef_received);
+ assert(m_textures_received);
+
+ // Rebuild inherited images and recreate textures
+ m_tsrc->rebuildImagesAndTextures();
+
+ // Update texture atlas
+ if(g_settings->getBool("enable_texture_atlas"))
+ m_tsrc->buildMainAtlas(this);
+
+ // Update node aliases
+ m_nodedef->updateAliases(m_itemdef);
+
+ // Update node textures
+ m_nodedef->updateTextures(m_tsrc);
+
+ // Update item textures and meshes
+ m_itemdef->updateTexturesAndMeshes(this);
+
+ // Start mesh update thread after setting up content definitions
+ m_mesh_update_thread.Start();
+}
+
float Client::getRTT(void)
{
try{
// IGameDef interface
// Under envlock
-IToolDefManager* Client::getToolDefManager()
+IItemDefManager* Client::getItemDefManager()
{
- return m_tooldef;
+ return m_itemdef;
}
INodeDefManager* Client::getNodeDefManager()
{
return NULL;
//return m_craftdef;
}
-ICraftItemDefManager* Client::getCraftItemDefManager()
-{
- return m_craftitemdef;
-}
ITextureSource* Client::getTextureSource()
{
return m_tsrc;