ThreadStarted();
DSTACK(__FUNCTION_NAME);
-
-#if CATCH_UNHANDLED_EXCEPTIONS
- try
+
+ BEGIN_DEBUG_EXCEPTION_HANDLER
+
+ while(getRun())
{
-#endif
- while(getRun())
- {
- m_client->asyncStep();
+ m_client->asyncStep();
- //m_client->updateSomeExpiredMeshes();
+ //m_client->updateSomeExpiredMeshes();
- bool was = m_client->AsyncProcessData();
+ bool was = m_client->AsyncProcessData();
- if(was == false)
- sleep_ms(10);
- }
-#if CATCH_UNHANDLED_EXCEPTIONS
+ if(was == false)
+ sleep_ms(10);
}
- /*
- This is what has to be done in threads to get suitable debug info
- */
- catch(std::exception &e)
- {
- dstream<<std::endl<<DTIME<<"An unhandled exception occurred: "
- <<e.what()<<std::endl;
- assert(0);
- }
-#endif
+
+ END_DEBUG_EXCEPTION_HANDLER
return NULL;
}
Client::Client(
IrrlichtDevice *device,
const char *playername,
- JMutex &range_mutex,
- s16 &viewing_range_nodes,
- bool &viewing_range_all):
+ MapDrawControl &control):
m_thread(this),
- m_env(new ClientMap(this,
- range_mutex,
- viewing_range_nodes,
- viewing_range_all,
+ m_env(new ClientMap(this, control,
device->getSceneManager()->getRootSceneNode(),
device->getSceneManager(), 666),
dout_client),
Client::~Client()
{
+ {
+ JMutexAutoLock conlock(m_con_mutex);
+ m_con.Disconnect();
+ }
+
m_thread.setRun(false);
while(m_thread.IsRunning())
sleep_ms(100);
if(dtime > 2.0)
dtime = 2.0;
- /*
- Day/night
- */
- {
- s32 d = 8;
- s32 t = (((m_time_of_day.get() + 24000/d/2)%24000)/(24000/d));
- s32 dn = 0;
- if(t == d/4 || t == (d-d/4))
- dn = 1;
- else if(t < d/4 || t > (d-d/4))
- dn = 2;
- else
- dn = 0;
-
- u32 dr = 1000;
- if(dn == 0)
- dr = 1000;
- if(dn == 1)
- dr = 600;
- if(dn == 2)
- dr = 300;
-
- if(dr != m_env.getDayNightRatio())
- {
- //dstream<<"dr="<<dr<<std::endl;
- m_env.setDayNightRatio(dr);
- m_env.expireMeshes(true);
- }
- }
-
//dstream<<"Client steps "<<dtime<<std::endl;
"InvalidIncomingDataException: what()="
<<e.what()<<std::endl;
}
- //TODO: Testing
- //break;
}
}
// making some copypasta
{}
- if(command == TOCLIENT_PLAYERPOS)
+ if(command == TOCLIENT_REMOVENODE)
+ {
+ if(datasize < 8)
+ return;
+ v3s16 p;
+ p.X = readS16(&data[2]);
+ p.Y = readS16(&data[4]);
+ p.Z = readS16(&data[6]);
+
+ //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
+
+ // This will clear the cracking animation after digging
+ ((ClientMap&)m_env.getMap()).clearTempMod(p);
+
+ removeNode(p);
+ }
+ else if(command == TOCLIENT_ADDNODE)
+ {
+ if(datasize < 8 + MapNode::serializedLength(ser_version))
+ return;
+
+ v3s16 p;
+ p.X = readS16(&data[2]);
+ p.Y = readS16(&data[4]);
+ p.Z = readS16(&data[6]);
+
+ //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
+
+ MapNode n;
+ n.deSerialize(&data[8], ser_version);
+
+ addNode(p, n);
+ }
+ else if(command == TOCLIENT_PLAYERPOS)
{
dstream<<"WARNING: Received deprecated TOCLIENT_PLAYERPOS"
<<std::endl;
our_peer_id = m_con.GetPeerID();
}
// Cancel if we don't have a peer id
- if(our_peer_id == PEER_ID_NEW){
+ if(our_peer_id == PEER_ID_INEXISTENT){
dout_client<<DTIME<<"TOCLIENT_PLAYERPOS cancelled: "
"we have no peer id"
<<std::endl;
our_peer_id = m_con.GetPeerID();
}
// Cancel if we don't have a peer id
- if(our_peer_id == PEER_ID_NEW){
+ if(our_peer_id == PEER_ID_INEXISTENT){
dout_client<<DTIME<<"TOCLIENT_PLAYERINFO cancelled: "
"we have no peer id"
<<std::endl;
time = time % 24000;
m_time_of_day.set(time);
//dstream<<"Client: time="<<time<<std::endl;
+
+ /*
+ Day/night
+
+ time_of_day:
+ 0 = midnight
+ 12000 = midday
+ */
+ {
+ const s32 daylength = 8;
+ const s32 nightlength = 2;
+ const s32 daytimelength = 4;
+ s32 d = daylength;
+ s32 t = (((m_time_of_day.get())%24000)/(24000/d));
+ u32 dr;
+ if(t < nightlength/2 || t >= d - nightlength/2)
+ dr = 350;
+ else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2)
+ dr = 1000;
+ else
+ dr = 750;
+
+ dstream<<"time_of_day="<<m_time_of_day.get()
+ <<", t="<<t
+ <<", dr="<<dr
+ <<std::endl;
+
+ if(dr != m_env.getDayNightRatio())
+ {
+ //dstream<<"dr="<<dr<<std::endl;
+ dout_client<<DTIME<<"Client: changing day-night ratio"<<std::endl;
+ m_env.setDayNightRatio(dr);
+ m_env.expireMeshes(true);
+ }
+ }
+
+ }
+ else if(command == TOCLIENT_CHAT_MESSAGE)
+ {
+ /*
+ u16 command
+ u16 length
+ wstring message
+ */
+ u8 buf[6];
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ // Read stuff
+ is.read((char*)buf, 2);
+ u16 len = readU16(buf);
+
+ std::wstring message;
+ for(u16 i=0; i<len; i++)
+ {
+ is.read((char*)buf, 2);
+ message += (wchar_t)readU16(buf);
+ }
+
+ /*dstream<<"Client received chat message: "
+ <<wide_to_narrow(message)<<std::endl;*/
+
+ m_chat_queue.push_back(message);
}
// Default to queueing it (for slow commands)
else
/*
Returns true if there was something in queue
*/
-bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
+bool Client::AsyncProcessPacket()
{
DSTACK(__FUNCTION_NAME);
ToClientCommand command = (ToClientCommand)readU16(&data[0]);
- if(command == TOCLIENT_REMOVENODE)
- {
- if(datasize < 8)
- return true;
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
-
- //TimeTaker t1("TOCLIENT_REMOVENODE", g_device);
-
- core::map<v3s16, MapBlock*> modified_blocks;
-
- try
- {
- JMutexAutoLock envlock(m_env_mutex);
- //TimeTaker t("removeNodeAndUpdate", m_device);
- m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
- }
- catch(InvalidPositionException &e)
- {
- }
-
- for(core::map<v3s16, MapBlock * >::Iterator
- i = modified_blocks.getIterator();
- i.atEnd() == false; i++)
- {
- v3s16 p = i.getNode()->getKey();
- //m_env.getMap().updateMeshes(p);
- mesh_updater.add(p);
- }
- }
- else if(command == TOCLIENT_ADDNODE)
- {
- if(datasize < 8 + MapNode::serializedLength(ser_version))
- return true;
-
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
-
- //TimeTaker t1("TOCLIENT_ADDNODE", g_device);
-
- MapNode n;
- n.deSerialize(&data[8], ser_version);
-
- core::map<v3s16, MapBlock*> modified_blocks;
-
- try
- {
- JMutexAutoLock envlock(m_env_mutex);
- m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
- }
- catch(InvalidPositionException &e)
- {}
-
- for(core::map<v3s16, MapBlock * >::Iterator
- i = modified_blocks.getIterator();
- i.atEnd() == false; i++)
- {
- v3s16 p = i.getNode()->getKey();
- //m_env.getMap().updateMeshes(p);
- mesh_updater.add(p);
- }
- }
- else if(command == TOCLIENT_BLOCKDATA)
+ if(command == TOCLIENT_BLOCKDATA)
{
// Ignore too small packet
if(datasize < 8)
}
} //envlock
-
- // Old version has zero lighting, update it.
- if(ser_version == 0 || ser_version == 1)
- {
- derr_client<<"Client: Block in old format: "
- "Calculating lighting"<<std::endl;
- core::map<v3s16, MapBlock*> blocks_changed;
- blocks_changed.insert(block->getPos(), block);
- core::map<v3s16, MapBlock*> modified_blocks;
- m_env.getMap().updateLighting(blocks_changed, modified_blocks);
- }
-
- /*
- Update Mesh of this block and blocks at x-, y- and z-
- */
-
- //m_env.getMap().updateMeshes(block->getPos());
- mesh_updater.add(block->getPos());
-
/*
Acknowledge block.
*/
// Send as reliable
m_con.Send(PEER_ID_SERVER, 1, reply, true);
-#if 0
/*
- Remove from history
+ 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.
*/
- {
- JMutexAutoLock lock(m_fetchblock_mutex);
-
- if(m_fetchblock_history.find(p) != NULL)
- {
- m_fetchblock_history.remove(p);
- }
- else
- {
- /*
- 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], p);
- // Send as reliable
- m_con.Send(PEER_ID_SERVER, 1, reply, true);
- }
- }
-#endif
+
+ m_env.getMap().updateMeshes(block->getPos(), getDayNightRatio());
}
else
{
{
for(;;)
{
- // We want to update the meshes as soon as a single packet has
- // been processed
- LazyMeshUpdater mesh_updater(&m_env);
- bool r = AsyncProcessPacket(mesh_updater);
+ bool r = AsyncProcessPacket();
if(r == false)
break;
}
return false;
-
- /*LazyMeshUpdater mesh_updater(&m_env);
- for(;;)
- {
- bool r = AsyncProcessPacket(mesh_updater);
- if(r == false)
- break;
- }
- return false;*/
-
}
void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
}
-#if 0
-void Client::fetchBlock(v3s16 p, u8 flags)
-{
- if(connectedAndInitialized() == false)
- throw ClientNotReadyException
- ("ClientNotReadyException: connectedAndInitialized() == false");
-
- /*dstream<<"Client::fetchBlock(): Sending GETBLOCK for ("
- <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-
- JMutexAutoLock conlock(m_con_mutex);
-
- SharedBuffer<u8> data(9);
- writeU16(&data[0], TOSERVER_GETBLOCK);
- writeS16(&data[2], p.X);
- writeS16(&data[4], p.Y);
- writeS16(&data[6], p.Z);
- writeU8(&data[8], flags);
- m_con.Send(PEER_ID_SERVER, 1, data, true);
-}
-
-/*
- Calls fetchBlock() on some nearby missing blocks.
-
- Returns when any of various network load indicators go over limit.
-
- Does nearly the same thing as the old updateChangedVisibleArea()
-*/
-void Client::fetchBlocks()
-{
- if(connectedAndInitialized() == false)
- throw ClientNotReadyException
- ("ClientNotReadyException: connectedAndInitialized() == false");
-}
-#endif
-
-bool Client::isFetchingBlocks()
-{
- JMutexAutoLock conlock(m_con_mutex);
- con::Peer *peer = m_con.GetPeerNoEx(PEER_ID_SERVER);
- // Not really fetching but can't fetch more.
- if(peer == NULL) return true;
-
- con::Channel *channel = &(peer->channels[1]);
- /*
- NOTE: Channel 0 should always be used for fetching blocks,
- and for nothing else.
- */
- if(channel->incoming_reliables.size() > 0)
- return true;
- if(channel->outgoing_reliables.size() > 0)
- return true;
- return false;
-}
-
IncomingPacket Client::getPacket()
{
JMutexAutoLock lock(m_incoming_queue_mutex);
return packet;
}
-#if 0
-void Client::removeNode(v3s16 nodepos)
-{
- if(connectedAndInitialized() == false){
- dout_client<<DTIME<<"Client::removeNode() cancelled (not connected)"
- <<std::endl;
- return;
- }
-
- // Test that the position exists
- try{
- JMutexAutoLock envlock(m_env_mutex);
- m_env.getMap().getNode(nodepos);
- }
- catch(InvalidPositionException &e)
- {
- dout_client<<DTIME<<"Client::removeNode() cancelled (doesn't exist)"
- <<std::endl;
- return;
- }
-
- SharedBuffer<u8> data(8);
- writeU16(&data[0], TOSERVER_REMOVENODE);
- writeS16(&data[2], nodepos.X);
- writeS16(&data[4], nodepos.Y);
- writeS16(&data[6], nodepos.Z);
- Send(0, data, true);
-}
-
-void Client::addNodeFromInventory(v3s16 nodepos, u16 i)
-{
- if(connectedAndInitialized() == false){
- dout_client<<DTIME<<"Client::addNodeFromInventory() "
- "cancelled (not connected)"
- <<std::endl;
- return;
- }
-
- // Test that the position exists
- try{
- JMutexAutoLock envlock(m_env_mutex);
- m_env.getMap().getNode(nodepos);
- }
- catch(InvalidPositionException &e)
- {
- dout_client<<DTIME<<"Client::addNode() cancelled (doesn't exist)"
- <<std::endl;
- return;
- }
-
- //u8 ser_version = m_server_ser_ver;
-
- // SUGGESTION: The validity of the operation could be checked here too
-
- u8 datasize = 2 + 6 + 2;
- SharedBuffer<u8> data(datasize);
- writeU16(&data[0], TOSERVER_ADDNODE_FROM_INVENTORY);
- writeS16(&data[2], nodepos.X);
- writeS16(&data[4], nodepos.Y);
- writeS16(&data[6], nodepos.Z);
- writeU16(&data[8], i);
- Send(0, data, true);
-}
-#endif
-
void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
v3s16 nodepos_oversurface, u16 item)
{
}
/*
- [0] u16 command
+ [0] u16 command=TOSERVER_CLICK_OBJECT
[2] u8 button (0=left, 1=right)
[3] v3s16 block
[9] s16 id
Send(0, data, true);
}
+void Client::sendChatMessage(const std::wstring &message)
+{
+ std::ostringstream os(std::ios_base::binary);
+ u8 buf[12];
+
+ // Write command
+ writeU16(buf, TOSERVER_CHAT_MESSAGE);
+ os.write((char*)buf, 2);
+
+ // Write length
+ writeU16(buf, message.size());
+ os.write((char*)buf, 2);
+
+ // Write string
+ for(u32 i=0; i<message.size(); i++)
+ {
+ u16 w = message[i];
+ writeU16(buf, w);
+ os.write((char*)buf, 2);
+ }
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ Send(0, data, true);
+}
+
void Client::sendPlayerPos()
{
JMutexAutoLock envlock(m_env_mutex);
}
// Set peer id if not set already
- if(myplayer->peer_id == PEER_ID_NEW)
+ 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);
Send(0, data, false);
}
+void Client::removeNode(v3s16 p)
+{
+ JMutexAutoLock envlock(m_env_mutex);
+
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ try
+ {
+ //TimeTaker t("removeNodeAndUpdate", m_device);
+ m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
+ }
+ catch(InvalidPositionException &e)
+ {
+ }
+
+ for(core::map<v3s16, MapBlock * >::Iterator
+ i = modified_blocks.getIterator();
+ i.atEnd() == false; i++)
+ {
+ v3s16 p = i.getNode()->getKey();
+ m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
+ }
+}
+void Client::addNode(v3s16 p, MapNode n)
+{
+ JMutexAutoLock envlock(m_env_mutex);
+
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ try
+ {
+ m_env.getMap().addNodeAndUpdate(p, n, modified_blocks);
+ }
+ catch(InvalidPositionException &e)
+ {}
+
+ for(core::map<v3s16, MapBlock * >::Iterator
+ i = modified_blocks.getIterator();
+ i.atEnd() == false; i++)
+ {
+ v3s16 p = i.getNode()->getKey();
+ m_env.getMap().updateMeshes(p, m_env.getDayNightRatio());
+ }
+}
+
void Client::updateCamera(v3f pos, v3f dir)
{
m_env.getMap().updateCamera(pos, dir);