#include <IFileSystem.h>
#include "sha1.h"
#include "base64.h"
+#include "clientmap.h"
static std::string getTextureCacheDir()
{
- return porting::path_userdata + DIR_DELIM + "cache" + DIR_DELIM + "texture";
+ return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "textures";
}
struct TextureRequest
{
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;
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_itemdef_received(false),
- m_nodedef_received(false)
+ m_nodedef_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;
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)
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.
*/
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)
{
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;
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
}
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))
+ {
+ // remove old crack
+ addUpdateMeshTaskForNode(old_crack_pos, false, true);
+ }
+ if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
{
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+ // 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'/')
{
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+ m_chat_queue.push_back(
+ (std::wstring)L"issued command: "+message);
+ }
+ else
+ {
+ 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()