# Makefile for Irrlicht Examples\r
# It's usually sufficient to change just the target name and source file list\r
# and be sure that CXX is set to a valid compiler\r
-TARGET = test\r
SOURCE_FILES = guiTextInputMenu.cpp guiInventoryMenu.cpp irrlichtwrapper.cpp guiPauseMenu.cpp defaultsettings.cpp mapnode.cpp tile.cpp voxel.cpp mapblockobject.cpp inventory.cpp debug.cpp serialization.cpp light.cpp filesys.cpp connection.cpp environment.cpp client.cpp server.cpp socket.cpp mapblock.cpp mapsector.cpp heightmap.cpp map.cpp player.cpp utility.cpp main.cpp test.cpp\r
-SOURCES = $(addprefix src/, $(SOURCE_FILES))\r
-BUILD_DIR = build\r
-OBJECTS = $(addprefix $(BUILD_DIR)/, $(SOURCE_FILES:.cpp=.o))\r
+\r
+DEBUG_TARGET = debugtest\r
+DEBUG_SOURCES = $(addprefix src/, $(SOURCE_FILES))\r
+DEBUG_BUILD_DIR = debugbuild\r
+DEBUG_OBJECTS = $(addprefix $(DEBUG_BUILD_DIR)/, $(SOURCE_FILES:.cpp=.o))\r
\r
FAST_TARGET = fasttest\r
FAST_SOURCES = $(addprefix src/, $(SOURCE_FILES))\r
#CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe\r
#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe\r
#CXXFLAGS = -O1 -ffast-math -Wall -g\r
-CXXFLAGS = -Wall -g -O0\r
+CXXFLAGS = -Wall -g -O1\r
\r
-all: fast_linux\r
+all: fast\r
\r
ifeq ($(HOSTTYPE), x86_64)\r
LIBSELECT=64\r
endif\r
\r
-all_linux fast_linux: LDFLAGS = -L/usr/X11R6/lib$(LIBSELECT) -L$(IRRLICHTPATH)/lib/Linux -L$(JTHREADPATH)/src/.libs -lIrrlicht -lGL -lXxf86vm -lXext -lX11 -ljthread -lz\r
-all_linux fast_linux: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src\r
-fast_linux server_linux: CXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686\r
-server_linux: LDFLAGS = -L$(JTHREADPATH)/src/.libs -ljthread -lz -lpthread\r
-server_linux: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DSERVER\r
-all_linux fast_linux clean_linux: SYSTEM=Linux\r
+debug fast: LDFLAGS = -L/usr/X11R6/lib$(LIBSELECT) -L$(IRRLICHTPATH)/lib/Linux -L$(JTHREADPATH)/src/.libs -lIrrlicht -lGL -lXxf86vm -lXext -lX11 -ljthread -lz\r
+debug: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DDEBUG\r
+fast: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DUNITTEST_DISABLE\r
+fast server: CXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686\r
+server: LDFLAGS = -L$(JTHREADPATH)/src/.libs -ljthread -lz -lpthread\r
+server: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DSERVER -DUNITTEST_DISABLE\r
+debug fast clean_debug: SYSTEM=Linux\r
\r
-DESTPATH = bin/$(TARGET)\r
+DEBUG_DESTPATH = bin/$(DEBUG_TARGET)\r
FAST_DESTPATH = bin/$(FAST_TARGET)\r
SERVER_DESTPATH = bin/$(SERVER_TARGET)\r
\r
# Build commands\r
\r
-all_linux: $(BUILD_DIR) $(DESTPATH)\r
-fast_linux: $(FAST_BUILD_DIR) $(FAST_DESTPATH)\r
-server_linux: $(SERVER_BUILD_DIR) $(SERVER_DESTPATH)\r
+debug: $(DEBUG_BUILD_DIR) $(DEBUG_DESTPATH)\r
+fast: $(FAST_BUILD_DIR) $(FAST_DESTPATH)\r
+server: $(SERVER_BUILD_DIR) $(SERVER_DESTPATH)\r
\r
-$(BUILD_DIR):\r
- mkdir -p $(BUILD_DIR)\r
+$(DEBUG_BUILD_DIR):\r
+ mkdir -p $(DEBUG_BUILD_DIR)\r
$(FAST_BUILD_DIR):\r
mkdir -p $(FAST_BUILD_DIR)\r
$(SERVER_BUILD_DIR):\r
mkdir -p $(SERVER_BUILD_DIR)\r
\r
-$(DESTPATH): $(OBJECTS)\r
- $(CXX) -o $@ $(OBJECTS) $(LDFLAGS)\r
+$(DEBUG_DESTPATH): $(DEBUG_OBJECTS)\r
+ $(CXX) -o $@ $(DEBUG_OBJECTS) $(LDFLAGS)\r
\r
$(FAST_DESTPATH): $(FAST_OBJECTS)\r
- $(CXX) -o $@ $(FAST_OBJECTS) $(LDFLAGS) -DUNITTEST_DISABLE\r
+ $(CXX) -o $@ $(FAST_OBJECTS) $(LDFLAGS)\r
\r
$(SERVER_DESTPATH): $(SERVER_OBJECTS)\r
- $(CXX) -o $@ $(SERVER_OBJECTS) $(LDFLAGS) -DSERVER -DUNITTEST_DISABLE\r
+ $(CXX) -o $@ $(SERVER_OBJECTS) $(LDFLAGS)\r
\r
-$(BUILD_DIR)/%.o: src/%.cpp\r
+$(DEBUG_BUILD_DIR)/%.o: src/%.cpp\r
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)\r
\r
$(FAST_BUILD_DIR)/%.o: src/%.cpp\r
$(SERVER_BUILD_DIR)/%.o: src/%.cpp\r
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)\r
\r
-clean: clean_linux clean_fast_linux clean_server_linux\r
+clean: clean_debug clean_fast clean_server\r
\r
-clean_linux:\r
- @$(RM) $(OBJECTS) $(DESTPATH)\r
+clean_debug:\r
+ @$(RM) $(DEBUG_OBJECTS) $(DEBUG_DESTPATH)\r
\r
-clean_fast_linux:\r
+clean_fast:\r
@$(RM) $(FAST_OBJECTS) $(FAST_DESTPATH)\r
\r
-clean_server_linux:\r
+clean_server:\r
@$(RM) $(SERVER_OBJECTS) $(SERVER_DESTPATH)\r
\r
-.PHONY: all all_win32 clean clean_linux clean_win32 clean_fast_linux clean_server_linux\r
+.PHONY: all all_win32 clean clean_debug clean_win32 clean_fast clean_server\r
RelativePath=".\src\guiPauseMenu.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\src\guiTextInputMenu.cpp"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\src\heightmap.cpp"\r
>\r
"InvalidIncomingDataException: what()="
<<e.what()<<std::endl;
}
- //TODO: Testing
- //break;
}
}
m_time_of_day.set(time);
//dstream<<"Client: time="<<time<<std::endl;
}
+ 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
{
// This will clear the cracking animation after digging
((ClientMap&)m_env.getMap()).clearTempMod(p);
- 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);
- }
+ removeNode(p);
}
else if(command == TOCLIENT_ADDNODE)
{
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);
- }
+ addNode(p, n);
}
else if(command == TOCLIENT_BLOCKDATA)
{
}
} //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
{
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)
{
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);
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);
void sendSignText(v3s16 blockpos, s16 id, std::string text);
void sendInventoryAction(InventoryAction *a);
+ void sendChatMessage(const std::wstring &message);
+
+ // locks envlock
+ void removeNode(v3s16 p);
+ // locks envlock
+ void addNode(v3s16 p, MapNode n);
void updateCamera(v3f pos, v3f dir);
// Returns InvalidPositionException if not found
//f32 getGroundHeight(v2s16 p);
- // Returns InvalidPositionException if not found
- //bool isNodeUnderground(v3s16 p);
- // Note: The players should not be exposed outside
- // Return value is valid until client is destroyed
- //Player * getLocalPlayer();
- // Return value is valid until step()
- //core::list<Player*> getPlayers();
v3f getPlayerPosition();
void setPlayerControl(PlayerControl &control);
bool getLocalInventoryUpdated();
// Copies the inventory of the local player to parameter
void getLocalInventory(Inventory &dst);
- // TODO: Functions for sending inventory editing commands to
- // server
// Gets closest object pointed by the shootline
// Returns NULL if not found
return 0.0;
return peer->avg_rtt;
}
-
+
+ bool getChatMessage(std::wstring &message)
+ {
+ if(m_chat_queue.size() == 0)
+ return false;
+ message = m_chat_queue.pop_front();
+ return true;
+ }
+
+ void addChatMessage(const std::wstring &message)
+ {
+ JMutexAutoLock envlock(m_env_mutex);
+ 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);
+ }
+
private:
// Virtual methods from con::PeerHandler
void sendPlayerPos();
// This sends the player's current name etc to the server
void sendPlayerInfo();
-
+
float m_packetcounter_timer;
float m_delete_unused_sectors_timer;
float m_connection_reinit_timer;
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
//s32 m_daynight_i;
//u32 m_daynight_ratio;
+
+ Queue<std::wstring> m_chat_queue;
};
#endif // !SERVER
u16 command
u16 time (0-23999)
*/
+
+ TOCLIENT_CHAT_MESSAGE = 0x30,
+ /*
+ u16 command
+ u16 length
+ wstring message
+ */
+
};
enum ToServerCommand
0: start digging (from undersurface)
1: place block (to abovesurface)
2: stop digging (all parameters ignored)
+ 3: digging completed
*/
TOSERVER_RELEASE = 0x29, // Not used
/*
See InventoryAction in inventory.h
*/
+
+ TOSERVER_CHAT_MESSAGE = 0x32,
+ /*
+ u16 command
+ u16 length
+ wstring message
+ */
+
};
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
// Time after building, during which the following limit
// is in use
-#define FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING 2.0
+//#define FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING 2.0
// This many blocks are sent when player is building
#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 0
// Override for the previous one when distance of block
// Whether to catch all std::exceptions.
// Assert will be called on such an event.
-#define CATCH_UNHANDLED_EXCEPTIONS 1
+#ifdef DEBUG
+ #define CATCH_UNHANDLED_EXCEPTIONS 0
+#else
+ #define CATCH_UNHANDLED_EXCEPTIONS 1
+#endif
/*
Collecting active blocks is stopped after object data
#define WATER_LEVEL (0)
// Length of cracking animation in count of images
-#define CRACK_ANIMATION_LENGTH 4
+#define CRACK_ANIMATION_LENGTH 5
#endif
g_settings.setDefault("time_speed", "96");
g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
g_settings.setDefault("server_map_save_interval", "60");
+ g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0");
}
L"- R: Toggle viewing all loaded chunks\n"\r
L"- I: Inventory menu\n"\r
L"- ESC: This menu\n"\r
+ L"- T: Chat\n"\r
L"\n"\r
L"To generate a new map, remove the map directory.\n";\r
Environment->addStaticText(text, rect, false, true, this, 258);\r
GUITextInputMenu::~GUITextInputMenu()
{
removeChildren();
+ if(m_dest)
+ delete m_dest;
}
void GUITextInputMenu::removeChildren()
break;
}
}
+ if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
+ {
+ switch(event.GUIEvent.Caller->getID())
+ {
+ case 256:
+ acceptInput();
+ quitMenu();
+ break;
+ }
+ }
}
return Parent ? Parent->OnEvent(event) : false;
// Throw a request in
m_get_texture_queue.add(spec, 0, 0, &result_queue);
- dstream<<"Waiting for texture "<<spec.name<<std::endl;
-
- // Wait result
- GetResult<TextureSpec, video::ITexture*, u8, u8>
- result = result_queue.pop_front(1000);
+ dstream<<"Waiting for texture from main thread: "
+ <<spec.name<<std::endl;
- // Check that at least something worked OK
- assert(result.key.name == spec.name);
-
- t = result.item;
+ try
+ {
+ // Wait result for a second
+ GetResult<TextureSpec, video::ITexture*, u8, u8>
+ result = result_queue.pop_front(1000);
+
+ // Check that at least something worked OK
+ assert(result.key.name == spec.name);
+
+ t = result.item;
+ }
+ catch(ItemNotFoundException &e)
+ {
+ dstream<<"Waiting for texture timed out."<<std::endl;
+ t = NULL;
+ }
}
// Add to cache and return
TODO: Draw big amounts of torches better (that is, throw them in the\r
same meshbuffer (can the meshcollector class be used?))\r
\r
+TODO: Check if the usage of Client::isFetchingBlocks() in\r
+ updateViewingRange() actually does something\r
+\r
Doing now:\r
======================================================================\r
\r
u16 g_selected_item = 0;\r
\r
IrrlichtDevice *g_device = NULL;\r
+Client *g_client = NULL;\r
\r
/*\r
GUI Stuff\r
return g_irrlicht->getTime();\r
}\r
\r
+/*\r
+ Text input system\r
+*/\r
+\r
+struct TextDestSign : public TextDest\r
+{\r
+ TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
+ {\r
+ m_blockpos = blockpos;\r
+ m_id = id;\r
+ m_client = client;\r
+ }\r
+ void gotText(std::wstring text)\r
+ {\r
+ std::string ntext = wide_to_narrow(text);\r
+ dstream<<"Changing text of a sign object: "\r
+ <<ntext<<std::endl;\r
+ m_client->sendSignText(m_blockpos, m_id, ntext);\r
+ }\r
+\r
+ v3s16 m_blockpos;\r
+ s16 m_id;\r
+ Client *m_client;\r
+};\r
+\r
+struct TextDestChat : public TextDest\r
+{\r
+ TextDestChat(Client *client)\r
+ {\r
+ m_client = client;\r
+ }\r
+ void gotText(std::wstring text)\r
+ {\r
+ m_client->sendChatMessage(text);\r
+ m_client->addChatMessage(text);\r
+ }\r
+\r
+ Client *m_client;\r
+};\r
+\r
class MyEventReceiver : public IEventReceiver\r
{\r
public:\r
&g_active_menu_count))->drop();\r
return true;\r
}\r
+ if(event.KeyInput.Key == irr::KEY_KEY_T)\r
+ {\r
+ TextDest *dest = new TextDestChat(g_client);\r
+\r
+ (new GUITextInputMenu(guienv, guiroot, -1,\r
+ &g_active_menu_count, dest,\r
+ L""))->drop();\r
+ }\r
}\r
\r
// Material selection\r
s32 m_selection;\r
};\r
\r
-/*\r
- Text input system\r
-*/\r
-\r
-struct TextDestSign : public TextDest\r
-{\r
- TextDestSign(v3s16 blockpos, s16 id, Client *client)\r
- {\r
- m_blockpos = blockpos;\r
- m_id = id;\r
- m_client = client;\r
- }\r
- void gotText(std::wstring text)\r
- {\r
- std::string ntext = wide_to_narrow(text);\r
- dstream<<"Changing text of a sign object: "\r
- <<ntext<<std::endl;\r
- m_client->sendSignText(m_blockpos, m_id, ntext);\r
- }\r
-\r
- v3s16 m_blockpos;\r
- s16 m_id;\r
- Client *m_client;\r
-};\r
-\r
int main(int argc, char *argv[])\r
{\r
/*\r
gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");\r
if(font)\r
skin->setFont(font);\r
+ \r
+ u32 text_height = font->getDimension(L"Hello, world!").Height;\r
+ dstream<<"text_height="<<text_height<<std::endl;\r
+\r
//skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));\r
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));\r
//skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));\r
\r
const wchar_t *text = L"Loading and connecting...";\r
core::vector2d<s32> center(screenW/2, screenH/2);\r
- core::dimension2d<u32> textd = font->getDimension(text);\r
- std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;\r
- // Have to add a bit to disable the text from word wrapping\r
- //core::vector2d<s32> textsize(textd.Width+4, textd.Height);\r
- core::vector2d<s32> textsize(300, textd.Height);\r
+ core::vector2d<s32> textsize(300, text_height);\r
core::rect<s32> textrect(center - textsize/2, center + textsize/2);\r
\r
gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(\r
g_range_mutex,\r
g_viewing_range_nodes,\r
g_viewing_range_all);\r
+ \r
+ g_client = &client;\r
\r
Address connect_address(0,0,0,0, port);\r
try{\r
core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),\r
false, false);\r
\r
+ // Chat text\r
+ gui::IGUIStaticText *chat_guitext = guienv->addStaticText(\r
+ L"Chat here\nOther line\nOther line\nOther line\nOther line",\r
+ core::rect<s32>(70, 60, 795, 150),\r
+ false, true);\r
+ core::list<std::wstring> chat_lines;\r
+ //chat_lines.push_back(L"Minetest-c55 up and running!");\r
+ \r
/*\r
Some statistics are collected in these\r
*/\r
if(g_input->getLeftClicked() ||\r
(g_input->getLeftState() && nodepos != nodepos_old))\r
{\r
- std::cout<<DTIME<<"Ground left-clicked"<<std::endl;\r
+ dstream<<DTIME<<"Started digging"<<std::endl;\r
client.groundAction(0, nodepos, neighbourpos, g_selected_item);\r
}\r
if(g_input->getLeftClicked())\r
}\r
if(g_input->getLeftState())\r
{\r
- float dig_time_complete = 0.5;\r
MapNode n = client.getNode(nodepos);\r
+\r
+ // TODO: Get this from some table that is sent by server\r
+ float dig_time_complete = 0.5;\r
if(n.d == CONTENT_STONE)\r
dig_time_complete = 1.5;\r
\r
- float dig_time_complete0 = dig_time_complete+client.getAvgRtt()*2;\r
- if(dig_time_complete0 < 0.0)\r
- dig_time_complete0 = 0.0;\r
-\r
dig_index = (u16)((float)CRACK_ANIMATION_LENGTH\r
- * dig_time/dig_time_complete0);\r
+ * dig_time/dig_time_complete);\r
\r
- if(dig_time > 0.125 && dig_index < CRACK_ANIMATION_LENGTH)\r
+ if(dig_index < CRACK_ANIMATION_LENGTH)\r
{\r
//dstream<<"dig_index="<<dig_index<<std::endl;\r
client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));\r
}\r
+ else\r
+ {\r
+ dstream<<DTIME<<"Digging completed"<<std::endl;\r
+ client.groundAction(3, nodepos, neighbourpos, g_selected_item);\r
+ client.clearTempMod(nodepos);\r
+ client.removeNode(nodepos);\r
+ }\r
\r
dig_time += dtime;\r
}\r
\r
if(g_input->getLeftReleased())\r
{\r
- std::cout<<DTIME<<"Left released"<<std::endl;\r
+ std::cout<<DTIME<<"Left button released (stopped digging)"\r
+ <<std::endl;\r
client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);\r
}\r
if(g_input->getRightReleased())\r
}\r
\r
{\r
- /*wchar_t temptext[100];\r
- swprintf(temptext, 100,\r
- SWPRINTF_CHARSTRING,\r
- infotext.substr(0,99).c_str()\r
- );\r
-\r
- guitext_info->setText(temptext);*/\r
-\r
guitext_info->setText(infotext.c_str());\r
}\r
+ \r
+ /*\r
+ Get chat messages from client\r
+ */\r
+ {\r
+ // Get messages\r
+ std::wstring message;\r
+ while(client.getChatMessage(message))\r
+ {\r
+ chat_lines.push_back(message);\r
+ if(chat_lines.size() > 5)\r
+ {\r
+ core::list<std::wstring>::Iterator\r
+ i = chat_lines.begin();\r
+ chat_lines.erase(i);\r
+ }\r
+ }\r
+ // Append them to form the whole static text and throw\r
+ // it to the gui element\r
+ std::wstring whole;\r
+ for(core::list<std::wstring>::Iterator\r
+ i = chat_lines.begin();\r
+ i != chat_lines.end(); i++)\r
+ {\r
+ whole += (*i) + L'\n';\r
+ }\r
+ chat_guitext->setText(whole.c_str());\r
+ // Update gui element size and position\r
+ core::rect<s32> rect(\r
+ 10,\r
+ screensize.Y - 10 - text_height*chat_lines.size(),\r
+ screensize.X - 10,\r
+ screensize.Y - 10\r
+ );\r
+ chat_guitext->setRelativePosition(rect);\r
+ }\r
\r
/*\r
Inventory\r
{
SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
m_time_from_building.m_value += dtime;
- if(m_time_from_building.m_value
- < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
+ /*if(m_time_from_building.m_value
+ < FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)*/
+ if(m_time_from_building.m_value < g_settings.getFloat(
+ "full_block_send_enable_min_time_from_building"))
{
maximum_simultaneous_block_sends
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
NOTE: Some of this could be moved to RemoteClient
*/
-
+#if 0
{
JMutexAutoLock envlock(m_env_mutex);
JMutexAutoLock conlock(m_con_mutex);
client->m_dig_time_remaining -= dtime;
if(client->m_dig_time_remaining > 0)
+ {
+ client->m_time_from_building.set(0.0);
continue;
+ }
v3s16 p_under = client->m_dig_position;
v.blitBack(modified_blocks);
}
}
+#endif
// Send object positions
{
m_time_of_day.get());
m_con.Send(peer->id, 0, data, true);
}
+
+ // Send information about joining in chat
+ {
+ std::wstring name = L"unknown";
+ Player *player = m_env.getPlayer(peer_id);
+ if(player != NULL)
+ name = narrow_to_wide(player->getName());
+
+ std::wstring message;
+ message += L"*** ";
+ message += name;
+ message += L" joined game";
+ BroadcastChatMessage(message);
+ }
return;
}
*/
if(action == 0)
{
+ /*
+ NOTE: This can be used in the future to check if
+ somebody is cheating, by checking the timing.
+ */
+#if 0
u8 content;
try
// Reset build time counter
getClient(peer->id)->m_time_from_building.set(0.0);
-
+#endif
} // action == 0
/*
*/
else if(action == 2)
{
+#if 0
RemoteClient *client = getClient(peer->id);
JMutexAutoLock digmutex(client->m_dig_mutex);
client->m_dig_tool_item = -1;
+#endif
}
+ /*
+ 3: Digging completed
+ */
+ if(action == 3)
+ {
+ // Mandatory parameter; actually used for nothing
+ core::map<v3s16, MapBlock*> modified_blocks;
+
+ u8 material;
+
+ try
+ {
+ // Get material at position
+ material = m_env.getMap().getNode(p_under).d;
+ // If it's not diggable, do nothing
+ if(content_diggable(material) == false)
+ {
+ derr_server<<"Server: Not finishing digging: Node not diggable"
+ <<std::endl;
+ return;
+ }
+ }
+ catch(InvalidPositionException &e)
+ {
+ derr_server<<"Server: Not finishing digging: Node not found"
+ <<std::endl;
+ return;
+ }
+
+ //TODO: Send to only other clients
+
+ /*
+ Send the removal to all other clients
+ */
+
+ // Create packet
+ u32 replysize = 8;
+ SharedBuffer<u8> reply(replysize);
+ writeU16(&reply[0], TOCLIENT_REMOVENODE);
+ writeS16(&reply[2], p_under.X);
+ writeS16(&reply[4], p_under.Y);
+ writeS16(&reply[6], p_under.Z);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(peer_id == client->peer_id)
+ continue;
+
+ // Send as reliable
+ m_con.Send(client->peer_id, 0, reply, true);
+ }
+
+ /*
+ Update and send inventory
+ */
+
+ if(g_settings.getBool("creative_mode") == false)
+ {
+ // Add to inventory and send inventory
+ InventoryItem *item = new MaterialItem(material, 1);
+ player->inventory.addItem("main", item);
+ SendInventory(player->peer_id);
+ }
+
+ /*
+ Remove the node
+ (this takes some time so it is done after the quick stuff)
+ */
+ m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
+
+ /*
+ Update water
+ */
+
+ // Update water pressure around modification
+ // This also adds it to m_flow_active_nodes if appropriate
+
+ MapVoxelManipulator v(&m_env.getMap());
+ v.m_disable_water_climb =
+ g_settings.getBool("disable_water_climb");
+
+ VoxelArea area(p_under-v3s16(1,1,1), p_under+v3s16(1,1,1));
+
+ try
+ {
+ v.updateAreaWaterPressure(area, m_flow_active_nodes);
+ }
+ catch(ProcessingLimitException &e)
+ {
+ dstream<<"Processing limit reached (1)"<<std::endl;
+ }
+
+ v.blitBack(modified_blocks);
+ }
+
/*
1: place block
*/
}
} // action == 1
+
/*
Catch invalid actions
*/
<<std::endl;
}
}
+ else if(command == TOSERVER_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<<"CHAT: "<<wide_to_narrow(message)<<std::endl;
+
+ /*
+ Send the message to all other clients
+ */
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ // Don't send if it's the same one
+ if(peer_id == client->peer_id)
+ continue;
+
+ // Get player name of this client
+ std::wstring name = L"unknown";
+ Player *player = m_env.getPlayer(client->peer_id);
+ if(player != NULL)
+ name = narrow_to_wide(player->getName());
+
+ SendChatMessage(client->peer_id,
+ std::wstring(L"<")+name+L"> "+message);
+ }
+ }
else
{
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
{
DSTACK(__FUNCTION_NAME);
- //JMutexAutoLock envlock(m_env_mutex);
-
Player* player = m_env.getPlayer(peer_id);
/*
writeU16(&data[0], TOCLIENT_INVENTORY);
memcpy(&data[2], s.c_str(), s.size());
- //JMutexAutoLock conlock(m_con_mutex);
+ // Send as reliable
+ m_con.Send(peer_id, 0, data, true);
+}
+void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ std::ostringstream os(std::ios_base::binary);
+ u8 buf[12];
+
+ // Write command
+ writeU16(buf, TOCLIENT_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
m_con.Send(peer_id, 0, data, true);
}
+void Server::BroadcastChatMessage(const std::wstring &message)
+{
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++)
+ {
+ // Get client and check that it is valid
+ RemoteClient *client = i.getNode()->getValue();
+ assert(client->peer_id == i.getNode()->getKey());
+ if(client->serialization_version == SER_FMT_VER_INVALID)
+ continue;
+
+ SendChatMessage(client->peer_id, message);
+ }
+}
+
void Server::SendBlocks(float dtime)
{
DSTACK(__FUNCTION_NAME);
m_blocks_sent_mutex.Init();
m_blocks_sending_mutex.Init();
- m_dig_mutex.Init();
+ /*m_dig_mutex.Init();
m_dig_time_remaining = 0;
- m_dig_tool_item = -1;
+ m_dig_tool_item = -1;*/
}
~RemoteClient()
{
// Time from last placing or removing blocks
MutexedVariable<float> m_time_from_building;
- JMutex m_dig_mutex;
+ /*JMutex m_dig_mutex;
float m_dig_time_remaining;
// -1 = not digging
s16 m_dig_tool_item;
- v3s16 m_dig_position;
+ v3s16 m_dig_position;*/
private:
/*
void SendObjectData(float dtime);
void SendPlayerInfos();
void SendInventory(u16 peer_id);
+ void SendChatMessage(u16 peer_id, const std::wstring &message);
+ void BroadcastChatMessage(const std::wstring &message);
+
// Sends blocks to clients
void SendBlocks(float dtime);