forked from Mirrorlandia_minetest/minetest
changes to handing of digging (non backwards-compatible i guess)
This commit is contained in:
parent
61b5a35384
commit
10b06419ab
60
Makefile
60
Makefile
@ -1,11 +1,12 @@
|
|||||||
# Makefile for Irrlicht Examples
|
# Makefile for Irrlicht Examples
|
||||||
# It's usually sufficient to change just the target name and source file list
|
# It's usually sufficient to change just the target name and source file list
|
||||||
# and be sure that CXX is set to a valid compiler
|
# and be sure that CXX is set to a valid compiler
|
||||||
TARGET = test
|
|
||||||
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
|
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
|
||||||
SOURCES = $(addprefix src/, $(SOURCE_FILES))
|
|
||||||
BUILD_DIR = build
|
DEBUG_TARGET = debugtest
|
||||||
OBJECTS = $(addprefix $(BUILD_DIR)/, $(SOURCE_FILES:.cpp=.o))
|
DEBUG_SOURCES = $(addprefix src/, $(SOURCE_FILES))
|
||||||
|
DEBUG_BUILD_DIR = debugbuild
|
||||||
|
DEBUG_OBJECTS = $(addprefix $(DEBUG_BUILD_DIR)/, $(SOURCE_FILES:.cpp=.o))
|
||||||
|
|
||||||
FAST_TARGET = fasttest
|
FAST_TARGET = fasttest
|
||||||
FAST_SOURCES = $(addprefix src/, $(SOURCE_FILES))
|
FAST_SOURCES = $(addprefix src/, $(SOURCE_FILES))
|
||||||
@ -24,48 +25,49 @@ JTHREADPATH = ../jthread/jthread-1.2.1
|
|||||||
#CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe
|
#CXXFLAGS = -O2 -ffast-math -Wall -fomit-frame-pointer -pipe
|
||||||
#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe
|
#CXXFLAGS = -O2 -ffast-math -Wall -g -pipe
|
||||||
#CXXFLAGS = -O1 -ffast-math -Wall -g
|
#CXXFLAGS = -O1 -ffast-math -Wall -g
|
||||||
CXXFLAGS = -Wall -g -O0
|
CXXFLAGS = -Wall -g -O1
|
||||||
|
|
||||||
all: fast_linux
|
all: fast
|
||||||
|
|
||||||
ifeq ($(HOSTTYPE), x86_64)
|
ifeq ($(HOSTTYPE), x86_64)
|
||||||
LIBSELECT=64
|
LIBSELECT=64
|
||||||
endif
|
endif
|
||||||
|
|
||||||
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
|
debug fast: LDFLAGS = -L/usr/X11R6/lib$(LIBSELECT) -L$(IRRLICHTPATH)/lib/Linux -L$(JTHREADPATH)/src/.libs -lIrrlicht -lGL -lXxf86vm -lXext -lX11 -ljthread -lz
|
||||||
all_linux fast_linux: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src
|
debug: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DDEBUG
|
||||||
fast_linux server_linux: CXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686
|
fast: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DUNITTEST_DISABLE
|
||||||
server_linux: LDFLAGS = -L$(JTHREADPATH)/src/.libs -ljthread -lz -lpthread
|
fast server: CXXFLAGS = -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops -mtune=i686
|
||||||
server_linux: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DSERVER
|
server: LDFLAGS = -L$(JTHREADPATH)/src/.libs -ljthread -lz -lpthread
|
||||||
all_linux fast_linux clean_linux: SYSTEM=Linux
|
server: CPPFLAGS = -I$(IRRLICHTPATH)/include -I/usr/X11R6/include -I$(JTHREADPATH)/src -DSERVER -DUNITTEST_DISABLE
|
||||||
|
debug fast clean_debug: SYSTEM=Linux
|
||||||
|
|
||||||
DESTPATH = bin/$(TARGET)
|
DEBUG_DESTPATH = bin/$(DEBUG_TARGET)
|
||||||
FAST_DESTPATH = bin/$(FAST_TARGET)
|
FAST_DESTPATH = bin/$(FAST_TARGET)
|
||||||
SERVER_DESTPATH = bin/$(SERVER_TARGET)
|
SERVER_DESTPATH = bin/$(SERVER_TARGET)
|
||||||
|
|
||||||
# Build commands
|
# Build commands
|
||||||
|
|
||||||
all_linux: $(BUILD_DIR) $(DESTPATH)
|
debug: $(DEBUG_BUILD_DIR) $(DEBUG_DESTPATH)
|
||||||
fast_linux: $(FAST_BUILD_DIR) $(FAST_DESTPATH)
|
fast: $(FAST_BUILD_DIR) $(FAST_DESTPATH)
|
||||||
server_linux: $(SERVER_BUILD_DIR) $(SERVER_DESTPATH)
|
server: $(SERVER_BUILD_DIR) $(SERVER_DESTPATH)
|
||||||
|
|
||||||
$(BUILD_DIR):
|
$(DEBUG_BUILD_DIR):
|
||||||
mkdir -p $(BUILD_DIR)
|
mkdir -p $(DEBUG_BUILD_DIR)
|
||||||
$(FAST_BUILD_DIR):
|
$(FAST_BUILD_DIR):
|
||||||
mkdir -p $(FAST_BUILD_DIR)
|
mkdir -p $(FAST_BUILD_DIR)
|
||||||
$(SERVER_BUILD_DIR):
|
$(SERVER_BUILD_DIR):
|
||||||
mkdir -p $(SERVER_BUILD_DIR)
|
mkdir -p $(SERVER_BUILD_DIR)
|
||||||
|
|
||||||
$(DESTPATH): $(OBJECTS)
|
$(DEBUG_DESTPATH): $(DEBUG_OBJECTS)
|
||||||
$(CXX) -o $@ $(OBJECTS) $(LDFLAGS)
|
$(CXX) -o $@ $(DEBUG_OBJECTS) $(LDFLAGS)
|
||||||
|
|
||||||
$(FAST_DESTPATH): $(FAST_OBJECTS)
|
$(FAST_DESTPATH): $(FAST_OBJECTS)
|
||||||
$(CXX) -o $@ $(FAST_OBJECTS) $(LDFLAGS) -DUNITTEST_DISABLE
|
$(CXX) -o $@ $(FAST_OBJECTS) $(LDFLAGS)
|
||||||
|
|
||||||
$(SERVER_DESTPATH): $(SERVER_OBJECTS)
|
$(SERVER_DESTPATH): $(SERVER_OBJECTS)
|
||||||
$(CXX) -o $@ $(SERVER_OBJECTS) $(LDFLAGS) -DSERVER -DUNITTEST_DISABLE
|
$(CXX) -o $@ $(SERVER_OBJECTS) $(LDFLAGS)
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: src/%.cpp
|
$(DEBUG_BUILD_DIR)/%.o: src/%.cpp
|
||||||
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)
|
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
$(FAST_BUILD_DIR)/%.o: src/%.cpp
|
$(FAST_BUILD_DIR)/%.o: src/%.cpp
|
||||||
@ -74,15 +76,15 @@ $(FAST_BUILD_DIR)/%.o: src/%.cpp
|
|||||||
$(SERVER_BUILD_DIR)/%.o: src/%.cpp
|
$(SERVER_BUILD_DIR)/%.o: src/%.cpp
|
||||||
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)
|
$(CXX) -c -o $@ $< $(CPPFLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
clean: clean_linux clean_fast_linux clean_server_linux
|
clean: clean_debug clean_fast clean_server
|
||||||
|
|
||||||
clean_linux:
|
clean_debug:
|
||||||
@$(RM) $(OBJECTS) $(DESTPATH)
|
@$(RM) $(DEBUG_OBJECTS) $(DEBUG_DESTPATH)
|
||||||
|
|
||||||
clean_fast_linux:
|
clean_fast:
|
||||||
@$(RM) $(FAST_OBJECTS) $(FAST_DESTPATH)
|
@$(RM) $(FAST_OBJECTS) $(FAST_DESTPATH)
|
||||||
|
|
||||||
clean_server_linux:
|
clean_server:
|
||||||
@$(RM) $(SERVER_OBJECTS) $(SERVER_DESTPATH)
|
@$(RM) $(SERVER_OBJECTS) $(SERVER_DESTPATH)
|
||||||
|
|
||||||
.PHONY: all all_win32 clean clean_linux clean_win32 clean_fast_linux clean_server_linux
|
.PHONY: all all_win32 clean clean_debug clean_win32 clean_fast clean_server
|
||||||
|
BIN
data/crack.png
BIN
data/crack.png
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.1 KiB |
@ -206,6 +206,10 @@
|
|||||||
RelativePath=".\src\guiPauseMenu.cpp"
|
RelativePath=".\src\guiPauseMenu.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\src\guiTextInputMenu.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\src\heightmap.cpp"
|
RelativePath=".\src\heightmap.cpp"
|
||||||
>
|
>
|
||||||
|
260
src/client.cpp
260
src/client.cpp
@ -447,8 +447,6 @@ void Client::ReceiveAll()
|
|||||||
"InvalidIncomingDataException: what()="
|
"InvalidIncomingDataException: what()="
|
||||||
<<e.what()<<std::endl;
|
<<e.what()<<std::endl;
|
||||||
}
|
}
|
||||||
//TODO: Testing
|
|
||||||
//break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -985,6 +983,33 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
|
|||||||
m_time_of_day.set(time);
|
m_time_of_day.set(time);
|
||||||
//dstream<<"Client: time="<<time<<std::endl;
|
//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)
|
// Default to queueing it (for slow commands)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1042,26 +1067,7 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
|
|||||||
// This will clear the cracking animation after digging
|
// This will clear the cracking animation after digging
|
||||||
((ClientMap&)m_env.getMap()).clearTempMod(p);
|
((ClientMap&)m_env.getMap()).clearTempMod(p);
|
||||||
|
|
||||||
core::map<v3s16, MapBlock*> modified_blocks;
|
removeNode(p);
|
||||||
|
|
||||||
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)
|
else if(command == TOCLIENT_ADDNODE)
|
||||||
{
|
{
|
||||||
@ -1078,24 +1084,7 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
|
|||||||
MapNode n;
|
MapNode n;
|
||||||
n.deSerialize(&data[8], ser_version);
|
n.deSerialize(&data[8], ser_version);
|
||||||
|
|
||||||
core::map<v3s16, MapBlock*> modified_blocks;
|
addNode(p, n);
|
||||||
|
|
||||||
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)
|
else if(command == TOCLIENT_BLOCKDATA)
|
||||||
{
|
{
|
||||||
@ -1190,25 +1179,6 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
|
|||||||
}
|
}
|
||||||
} //envlock
|
} //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.
|
Acknowledge block.
|
||||||
*/
|
*/
|
||||||
@ -1227,39 +1197,13 @@ bool Client::AsyncProcessPacket(LazyMeshUpdater &mesh_updater)
|
|||||||
// Send as reliable
|
// Send as reliable
|
||||||
m_con.Send(PEER_ID_SERVER, 1, reply, true);
|
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_env.getMap().updateMeshes(block->getPos(), getDayNightRatio());
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1383,71 +1327,6 @@ IncomingPacket Client::getPacket()
|
|||||||
return packet;
|
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,
|
void Client::groundAction(u8 action, v3s16 nodepos_undersurface,
|
||||||
v3s16 nodepos_oversurface, u16 item)
|
v3s16 nodepos_oversurface, u16 item)
|
||||||
{
|
{
|
||||||
@ -1563,6 +1442,34 @@ void Client::sendInventoryAction(InventoryAction *a)
|
|||||||
Send(0, data, true);
|
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()
|
void Client::sendPlayerPos()
|
||||||
{
|
{
|
||||||
JMutexAutoLock envlock(m_env_mutex);
|
JMutexAutoLock envlock(m_env_mutex);
|
||||||
@ -1610,6 +1517,51 @@ void Client::sendPlayerPos()
|
|||||||
Send(0, data, false);
|
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)
|
void Client::updateCamera(v3f pos, v3f dir)
|
||||||
{
|
{
|
||||||
|
35
src/client.h
35
src/client.h
@ -191,6 +191,12 @@ public:
|
|||||||
|
|
||||||
void sendSignText(v3s16 blockpos, s16 id, std::string text);
|
void sendSignText(v3s16 blockpos, s16 id, std::string text);
|
||||||
void sendInventoryAction(InventoryAction *a);
|
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);
|
void updateCamera(v3f pos, v3f dir);
|
||||||
|
|
||||||
@ -201,14 +207,7 @@ public:
|
|||||||
|
|
||||||
// Returns InvalidPositionException if not found
|
// Returns InvalidPositionException if not found
|
||||||
//f32 getGroundHeight(v2s16 p);
|
//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();
|
v3f getPlayerPosition();
|
||||||
|
|
||||||
void setPlayerControl(PlayerControl &control);
|
void setPlayerControl(PlayerControl &control);
|
||||||
@ -218,8 +217,6 @@ public:
|
|||||||
bool getLocalInventoryUpdated();
|
bool getLocalInventoryUpdated();
|
||||||
// Copies the inventory of the local player to parameter
|
// Copies the inventory of the local player to parameter
|
||||||
void getLocalInventory(Inventory &dst);
|
void getLocalInventory(Inventory &dst);
|
||||||
// TODO: Functions for sending inventory editing commands to
|
|
||||||
// server
|
|
||||||
|
|
||||||
// Gets closest object pointed by the shootline
|
// Gets closest object pointed by the shootline
|
||||||
// Returns NULL if not found
|
// Returns NULL if not found
|
||||||
@ -261,6 +258,24 @@ public:
|
|||||||
return peer->avg_rtt;
|
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:
|
private:
|
||||||
|
|
||||||
// Virtual methods from con::PeerHandler
|
// Virtual methods from con::PeerHandler
|
||||||
@ -321,6 +336,8 @@ private:
|
|||||||
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
|
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
|
||||||
//s32 m_daynight_i;
|
//s32 m_daynight_i;
|
||||||
//u32 m_daynight_ratio;
|
//u32 m_daynight_ratio;
|
||||||
|
|
||||||
|
Queue<std::wstring> m_chat_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !SERVER
|
#endif // !SERVER
|
||||||
|
@ -98,6 +98,14 @@ enum ToClientCommand
|
|||||||
u16 command
|
u16 command
|
||||||
u16 time (0-23999)
|
u16 time (0-23999)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
TOCLIENT_CHAT_MESSAGE = 0x30,
|
||||||
|
/*
|
||||||
|
u16 command
|
||||||
|
u16 length
|
||||||
|
wstring message
|
||||||
|
*/
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ToServerCommand
|
enum ToServerCommand
|
||||||
@ -179,6 +187,7 @@ enum ToServerCommand
|
|||||||
0: start digging (from undersurface)
|
0: start digging (from undersurface)
|
||||||
1: place block (to abovesurface)
|
1: place block (to abovesurface)
|
||||||
2: stop digging (all parameters ignored)
|
2: stop digging (all parameters ignored)
|
||||||
|
3: digging completed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TOSERVER_RELEASE = 0x29, // Not used
|
TOSERVER_RELEASE = 0x29, // Not used
|
||||||
@ -196,6 +205,14 @@ enum ToServerCommand
|
|||||||
/*
|
/*
|
||||||
See InventoryAction in inventory.h
|
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)
|
inline SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time)
|
||||||
|
@ -47,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
// Time after building, during which the following limit
|
// Time after building, during which the following limit
|
||||||
// is in use
|
// 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
|
// This many blocks are sent when player is building
|
||||||
#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 0
|
#define LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS 0
|
||||||
// Override for the previous one when distance of block
|
// Override for the previous one when distance of block
|
||||||
@ -69,7 +69,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
|
|
||||||
// Whether to catch all std::exceptions.
|
// Whether to catch all std::exceptions.
|
||||||
// Assert will be called on such an event.
|
// Assert will be called on such an event.
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define CATCH_UNHANDLED_EXCEPTIONS 0
|
||||||
|
#else
|
||||||
#define CATCH_UNHANDLED_EXCEPTIONS 1
|
#define CATCH_UNHANDLED_EXCEPTIONS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Collecting active blocks is stopped after object data
|
Collecting active blocks is stopped after object data
|
||||||
@ -80,7 +84,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#define WATER_LEVEL (0)
|
#define WATER_LEVEL (0)
|
||||||
|
|
||||||
// Length of cracking animation in count of images
|
// Length of cracking animation in count of images
|
||||||
#define CRACK_ANIMATION_LENGTH 4
|
#define CRACK_ANIMATION_LENGTH 5
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -60,5 +60,6 @@ void set_default_settings()
|
|||||||
g_settings.setDefault("time_speed", "96");
|
g_settings.setDefault("time_speed", "96");
|
||||||
g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
|
g_settings.setDefault("server_unload_unused_sectors_timeout", "60");
|
||||||
g_settings.setDefault("server_map_save_interval", "60");
|
g_settings.setDefault("server_map_save_interval", "60");
|
||||||
|
g_settings.setDefault("full_block_send_enable_min_time_from_building", "2.0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ void GUIPauseMenu::regenerateGui(v2u32 screensize)
|
|||||||
L"- R: Toggle viewing all loaded chunks\n"
|
L"- R: Toggle viewing all loaded chunks\n"
|
||||||
L"- I: Inventory menu\n"
|
L"- I: Inventory menu\n"
|
||||||
L"- ESC: This menu\n"
|
L"- ESC: This menu\n"
|
||||||
|
L"- T: Chat\n"
|
||||||
L"\n"
|
L"\n"
|
||||||
L"To generate a new map, remove the map directory.\n";
|
L"To generate a new map, remove the map directory.\n";
|
||||||
Environment->addStaticText(text, rect, false, true, this, 258);
|
Environment->addStaticText(text, rect, false, true, this, 258);
|
||||||
|
@ -37,6 +37,8 @@ GUITextInputMenu::GUITextInputMenu(gui::IGUIEnvironment* env,
|
|||||||
GUITextInputMenu::~GUITextInputMenu()
|
GUITextInputMenu::~GUITextInputMenu()
|
||||||
{
|
{
|
||||||
removeChildren();
|
removeChildren();
|
||||||
|
if(m_dest)
|
||||||
|
delete m_dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GUITextInputMenu::removeChildren()
|
void GUITextInputMenu::removeChildren()
|
||||||
@ -173,6 +175,16 @@ bool GUITextInputMenu::OnEvent(const SEvent& event)
|
|||||||
break;
|
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;
|
return Parent ? Parent->OnEvent(event) : false;
|
||||||
|
@ -51,9 +51,12 @@ video::ITexture* IrrlichtWrapper::getTexture(TextureSpec spec)
|
|||||||
// Throw a request in
|
// Throw a request in
|
||||||
m_get_texture_queue.add(spec, 0, 0, &result_queue);
|
m_get_texture_queue.add(spec, 0, 0, &result_queue);
|
||||||
|
|
||||||
dstream<<"Waiting for texture "<<spec.name<<std::endl;
|
dstream<<"Waiting for texture from main thread: "
|
||||||
|
<<spec.name<<std::endl;
|
||||||
|
|
||||||
// Wait result
|
try
|
||||||
|
{
|
||||||
|
// Wait result for a second
|
||||||
GetResult<TextureSpec, video::ITexture*, u8, u8>
|
GetResult<TextureSpec, video::ITexture*, u8, u8>
|
||||||
result = result_queue.pop_front(1000);
|
result = result_queue.pop_front(1000);
|
||||||
|
|
||||||
@ -62,6 +65,12 @@ video::ITexture* IrrlichtWrapper::getTexture(TextureSpec spec)
|
|||||||
|
|
||||||
t = result.item;
|
t = result.item;
|
||||||
}
|
}
|
||||||
|
catch(ItemNotFoundException &e)
|
||||||
|
{
|
||||||
|
dstream<<"Waiting for texture timed out."<<std::endl;
|
||||||
|
t = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add to cache and return
|
// Add to cache and return
|
||||||
m_texturecache.set(spec.name, t);
|
m_texturecache.set(spec.name, t);
|
||||||
|
165
src/main.cpp
165
src/main.cpp
@ -167,6 +167,9 @@ TODO: Better handling of objects and mobs
|
|||||||
TODO: Draw big amounts of torches better (that is, throw them in the
|
TODO: Draw big amounts of torches better (that is, throw them in the
|
||||||
same meshbuffer (can the meshcollector class be used?))
|
same meshbuffer (can the meshcollector class be used?))
|
||||||
|
|
||||||
|
TODO: Check if the usage of Client::isFetchingBlocks() in
|
||||||
|
updateViewingRange() actually does something
|
||||||
|
|
||||||
Doing now:
|
Doing now:
|
||||||
======================================================================
|
======================================================================
|
||||||
|
|
||||||
@ -270,6 +273,7 @@ extern void set_default_settings();
|
|||||||
u16 g_selected_item = 0;
|
u16 g_selected_item = 0;
|
||||||
|
|
||||||
IrrlichtDevice *g_device = NULL;
|
IrrlichtDevice *g_device = NULL;
|
||||||
|
Client *g_client = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUI Stuff
|
GUI Stuff
|
||||||
@ -323,6 +327,46 @@ u32 getTimeMs()
|
|||||||
return g_irrlicht->getTime();
|
return g_irrlicht->getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Text input system
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct TextDestSign : public TextDest
|
||||||
|
{
|
||||||
|
TextDestSign(v3s16 blockpos, s16 id, Client *client)
|
||||||
|
{
|
||||||
|
m_blockpos = blockpos;
|
||||||
|
m_id = id;
|
||||||
|
m_client = client;
|
||||||
|
}
|
||||||
|
void gotText(std::wstring text)
|
||||||
|
{
|
||||||
|
std::string ntext = wide_to_narrow(text);
|
||||||
|
dstream<<"Changing text of a sign object: "
|
||||||
|
<<ntext<<std::endl;
|
||||||
|
m_client->sendSignText(m_blockpos, m_id, ntext);
|
||||||
|
}
|
||||||
|
|
||||||
|
v3s16 m_blockpos;
|
||||||
|
s16 m_id;
|
||||||
|
Client *m_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextDestChat : public TextDest
|
||||||
|
{
|
||||||
|
TextDestChat(Client *client)
|
||||||
|
{
|
||||||
|
m_client = client;
|
||||||
|
}
|
||||||
|
void gotText(std::wstring text)
|
||||||
|
{
|
||||||
|
m_client->sendChatMessage(text);
|
||||||
|
m_client->addChatMessage(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client *m_client;
|
||||||
|
};
|
||||||
|
|
||||||
class MyEventReceiver : public IEventReceiver
|
class MyEventReceiver : public IEventReceiver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -371,6 +415,14 @@ public:
|
|||||||
&g_active_menu_count))->drop();
|
&g_active_menu_count))->drop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if(event.KeyInput.Key == irr::KEY_KEY_T)
|
||||||
|
{
|
||||||
|
TextDest *dest = new TextDestChat(g_client);
|
||||||
|
|
||||||
|
(new GUITextInputMenu(guienv, guiroot, -1,
|
||||||
|
&g_active_menu_count, dest,
|
||||||
|
L""))->drop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Material selection
|
// Material selection
|
||||||
@ -965,31 +1017,6 @@ private:
|
|||||||
s32 m_selection;
|
s32 m_selection;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
Text input system
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct TextDestSign : public TextDest
|
|
||||||
{
|
|
||||||
TextDestSign(v3s16 blockpos, s16 id, Client *client)
|
|
||||||
{
|
|
||||||
m_blockpos = blockpos;
|
|
||||||
m_id = id;
|
|
||||||
m_client = client;
|
|
||||||
}
|
|
||||||
void gotText(std::wstring text)
|
|
||||||
{
|
|
||||||
std::string ntext = wide_to_narrow(text);
|
|
||||||
dstream<<"Changing text of a sign object: "
|
|
||||||
<<ntext<<std::endl;
|
|
||||||
m_client->sendSignText(m_blockpos, m_id, ntext);
|
|
||||||
}
|
|
||||||
|
|
||||||
v3s16 m_blockpos;
|
|
||||||
s16 m_id;
|
|
||||||
Client *m_client;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1334,6 +1361,10 @@ int main(int argc, char *argv[])
|
|||||||
gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
|
gui::IGUIFont* font = guienv->getFont("../data/fontlucida.png");
|
||||||
if(font)
|
if(font)
|
||||||
skin->setFont(font);
|
skin->setFont(font);
|
||||||
|
|
||||||
|
u32 text_height = font->getDimension(L"Hello, world!").Height;
|
||||||
|
dstream<<"text_height="<<text_height<<std::endl;
|
||||||
|
|
||||||
//skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
|
//skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,0,0,0));
|
||||||
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
|
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255,255,255,255));
|
||||||
//skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
|
//skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(0,0,0,0));
|
||||||
@ -1343,11 +1374,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
const wchar_t *text = L"Loading and connecting...";
|
const wchar_t *text = L"Loading and connecting...";
|
||||||
core::vector2d<s32> center(screenW/2, screenH/2);
|
core::vector2d<s32> center(screenW/2, screenH/2);
|
||||||
core::dimension2d<u32> textd = font->getDimension(text);
|
core::vector2d<s32> textsize(300, text_height);
|
||||||
std::cout<<DTIME<<"Text w="<<textd.Width<<" h="<<textd.Height<<std::endl;
|
|
||||||
// Have to add a bit to disable the text from word wrapping
|
|
||||||
//core::vector2d<s32> textsize(textd.Width+4, textd.Height);
|
|
||||||
core::vector2d<s32> textsize(300, textd.Height);
|
|
||||||
core::rect<s32> textrect(center - textsize/2, center + textsize/2);
|
core::rect<s32> textrect(center - textsize/2, center + textsize/2);
|
||||||
|
|
||||||
gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
|
gui::IGUIStaticText *gui_loadingtext = guienv->addStaticText(
|
||||||
@ -1390,6 +1417,8 @@ int main(int argc, char *argv[])
|
|||||||
g_viewing_range_nodes,
|
g_viewing_range_nodes,
|
||||||
g_viewing_range_all);
|
g_viewing_range_all);
|
||||||
|
|
||||||
|
g_client = &client;
|
||||||
|
|
||||||
Address connect_address(0,0,0,0, port);
|
Address connect_address(0,0,0,0, port);
|
||||||
try{
|
try{
|
||||||
connect_address.Resolve(connect_name);
|
connect_address.Resolve(connect_name);
|
||||||
@ -1498,6 +1527,14 @@ int main(int argc, char *argv[])
|
|||||||
core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
|
core::rect<s32>(100, 70, 100+400, 70+(textsize.Y+5)),
|
||||||
false, false);
|
false, false);
|
||||||
|
|
||||||
|
// Chat text
|
||||||
|
gui::IGUIStaticText *chat_guitext = guienv->addStaticText(
|
||||||
|
L"Chat here\nOther line\nOther line\nOther line\nOther line",
|
||||||
|
core::rect<s32>(70, 60, 795, 150),
|
||||||
|
false, true);
|
||||||
|
core::list<std::wstring> chat_lines;
|
||||||
|
//chat_lines.push_back(L"Minetest-c55 up and running!");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Some statistics are collected in these
|
Some statistics are collected in these
|
||||||
*/
|
*/
|
||||||
@ -2033,7 +2070,7 @@ int main(int argc, char *argv[])
|
|||||||
if(g_input->getLeftClicked() ||
|
if(g_input->getLeftClicked() ||
|
||||||
(g_input->getLeftState() && nodepos != nodepos_old))
|
(g_input->getLeftState() && nodepos != nodepos_old))
|
||||||
{
|
{
|
||||||
std::cout<<DTIME<<"Ground left-clicked"<<std::endl;
|
dstream<<DTIME<<"Started digging"<<std::endl;
|
||||||
client.groundAction(0, nodepos, neighbourpos, g_selected_item);
|
client.groundAction(0, nodepos, neighbourpos, g_selected_item);
|
||||||
}
|
}
|
||||||
if(g_input->getLeftClicked())
|
if(g_input->getLeftClicked())
|
||||||
@ -2042,23 +2079,28 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
if(g_input->getLeftState())
|
if(g_input->getLeftState())
|
||||||
{
|
{
|
||||||
float dig_time_complete = 0.5;
|
|
||||||
MapNode n = client.getNode(nodepos);
|
MapNode n = client.getNode(nodepos);
|
||||||
|
|
||||||
|
// TODO: Get this from some table that is sent by server
|
||||||
|
float dig_time_complete = 0.5;
|
||||||
if(n.d == CONTENT_STONE)
|
if(n.d == CONTENT_STONE)
|
||||||
dig_time_complete = 1.5;
|
dig_time_complete = 1.5;
|
||||||
|
|
||||||
float dig_time_complete0 = dig_time_complete+client.getAvgRtt()*2;
|
|
||||||
if(dig_time_complete0 < 0.0)
|
|
||||||
dig_time_complete0 = 0.0;
|
|
||||||
|
|
||||||
dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
|
dig_index = (u16)((float)CRACK_ANIMATION_LENGTH
|
||||||
* dig_time/dig_time_complete0);
|
* dig_time/dig_time_complete);
|
||||||
|
|
||||||
if(dig_time > 0.125 && dig_index < CRACK_ANIMATION_LENGTH)
|
if(dig_index < CRACK_ANIMATION_LENGTH)
|
||||||
{
|
{
|
||||||
//dstream<<"dig_index="<<dig_index<<std::endl;
|
//dstream<<"dig_index="<<dig_index<<std::endl;
|
||||||
client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
|
client.setTempMod(nodepos, NodeMod(NODEMOD_CRACK, dig_index));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dstream<<DTIME<<"Digging completed"<<std::endl;
|
||||||
|
client.groundAction(3, nodepos, neighbourpos, g_selected_item);
|
||||||
|
client.clearTempMod(nodepos);
|
||||||
|
client.removeNode(nodepos);
|
||||||
|
}
|
||||||
|
|
||||||
dig_time += dtime;
|
dig_time += dtime;
|
||||||
}
|
}
|
||||||
@ -2081,7 +2123,8 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if(g_input->getLeftReleased())
|
if(g_input->getLeftReleased())
|
||||||
{
|
{
|
||||||
std::cout<<DTIME<<"Left released"<<std::endl;
|
std::cout<<DTIME<<"Left button released (stopped digging)"
|
||||||
|
<<std::endl;
|
||||||
client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
|
client.groundAction(2, v3s16(0,0,0), v3s16(0,0,0), 0);
|
||||||
}
|
}
|
||||||
if(g_input->getRightReleased())
|
if(g_input->getRightReleased())
|
||||||
@ -2180,17 +2223,45 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
/*wchar_t temptext[100];
|
|
||||||
swprintf(temptext, 100,
|
|
||||||
SWPRINTF_CHARSTRING,
|
|
||||||
infotext.substr(0,99).c_str()
|
|
||||||
);
|
|
||||||
|
|
||||||
guitext_info->setText(temptext);*/
|
|
||||||
|
|
||||||
guitext_info->setText(infotext.c_str());
|
guitext_info->setText(infotext.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get chat messages from client
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
// Get messages
|
||||||
|
std::wstring message;
|
||||||
|
while(client.getChatMessage(message))
|
||||||
|
{
|
||||||
|
chat_lines.push_back(message);
|
||||||
|
if(chat_lines.size() > 5)
|
||||||
|
{
|
||||||
|
core::list<std::wstring>::Iterator
|
||||||
|
i = chat_lines.begin();
|
||||||
|
chat_lines.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Append them to form the whole static text and throw
|
||||||
|
// it to the gui element
|
||||||
|
std::wstring whole;
|
||||||
|
for(core::list<std::wstring>::Iterator
|
||||||
|
i = chat_lines.begin();
|
||||||
|
i != chat_lines.end(); i++)
|
||||||
|
{
|
||||||
|
whole += (*i) + L'\n';
|
||||||
|
}
|
||||||
|
chat_guitext->setText(whole.c_str());
|
||||||
|
// Update gui element size and position
|
||||||
|
core::rect<s32> rect(
|
||||||
|
10,
|
||||||
|
screensize.Y - 10 - text_height*chat_lines.size(),
|
||||||
|
screensize.X - 10,
|
||||||
|
screensize.Y - 10
|
||||||
|
);
|
||||||
|
chat_guitext->setRelativePosition(rect);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Inventory
|
Inventory
|
||||||
*/
|
*/
|
||||||
|
241
src/server.cpp
241
src/server.cpp
@ -355,8 +355,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
|
|||||||
{
|
{
|
||||||
SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
|
SharedPtr<JMutexAutoLock> lock(m_time_from_building.getLock());
|
||||||
m_time_from_building.m_value += dtime;
|
m_time_from_building.m_value += dtime;
|
||||||
if(m_time_from_building.m_value
|
/*if(m_time_from_building.m_value
|
||||||
< FULL_BLOCK_SEND_ENABLE_MIN_TIME_FROM_BUILDING)
|
< 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
|
maximum_simultaneous_block_sends
|
||||||
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
|
= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
|
||||||
@ -1188,7 +1190,7 @@ void Server::AsyncRunStep()
|
|||||||
|
|
||||||
NOTE: Some of this could be moved to RemoteClient
|
NOTE: Some of this could be moved to RemoteClient
|
||||||
*/
|
*/
|
||||||
|
#if 0
|
||||||
{
|
{
|
||||||
JMutexAutoLock envlock(m_env_mutex);
|
JMutexAutoLock envlock(m_env_mutex);
|
||||||
JMutexAutoLock conlock(m_con_mutex);
|
JMutexAutoLock conlock(m_con_mutex);
|
||||||
@ -1208,7 +1210,10 @@ void Server::AsyncRunStep()
|
|||||||
client->m_dig_time_remaining -= dtime;
|
client->m_dig_time_remaining -= dtime;
|
||||||
|
|
||||||
if(client->m_dig_time_remaining > 0)
|
if(client->m_dig_time_remaining > 0)
|
||||||
|
{
|
||||||
|
client->m_time_from_building.set(0.0);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
v3s16 p_under = client->m_dig_position;
|
v3s16 p_under = client->m_dig_position;
|
||||||
|
|
||||||
@ -1287,6 +1292,7 @@ void Server::AsyncRunStep()
|
|||||||
v.blitBack(modified_blocks);
|
v.blitBack(modified_blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Send object positions
|
// Send object positions
|
||||||
{
|
{
|
||||||
@ -1494,6 +1500,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
m_con.Send(peer->id, 0, data, true);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1688,7 +1708,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
*/
|
*/
|
||||||
if(action == 0)
|
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;
|
u8 content;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -1728,7 +1753,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
|
|
||||||
// Reset build time counter
|
// Reset build time counter
|
||||||
getClient(peer->id)->m_time_from_building.set(0.0);
|
getClient(peer->id)->m_time_from_building.set(0.0);
|
||||||
|
#endif
|
||||||
} // action == 0
|
} // action == 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1736,9 +1761,115 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
*/
|
*/
|
||||||
else if(action == 2)
|
else if(action == 2)
|
||||||
{
|
{
|
||||||
|
#if 0
|
||||||
RemoteClient *client = getClient(peer->id);
|
RemoteClient *client = getClient(peer->id);
|
||||||
JMutexAutoLock digmutex(client->m_dig_mutex);
|
JMutexAutoLock digmutex(client->m_dig_mutex);
|
||||||
client->m_dig_tool_item = -1;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1948,6 +2079,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // action == 1
|
} // action == 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Catch invalid actions
|
Catch invalid actions
|
||||||
*/
|
*/
|
||||||
@ -2100,6 +2232,57 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
|
|||||||
<<std::endl;
|
<<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
|
else
|
||||||
{
|
{
|
||||||
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
|
derr_server<<"WARNING: Server::ProcessData(): Ignoring "
|
||||||
@ -2401,8 +2584,6 @@ void Server::SendInventory(u16 peer_id)
|
|||||||
{
|
{
|
||||||
DSTACK(__FUNCTION_NAME);
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
|
||||||
//JMutexAutoLock envlock(m_env_mutex);
|
|
||||||
|
|
||||||
Player* player = m_env.getPlayer(peer_id);
|
Player* player = m_env.getPlayer(peer_id);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2464,12 +2645,56 @@ void Server::SendInventory(u16 peer_id)
|
|||||||
writeU16(&data[0], TOCLIENT_INVENTORY);
|
writeU16(&data[0], TOCLIENT_INVENTORY);
|
||||||
memcpy(&data[2], s.c_str(), s.size());
|
memcpy(&data[2], s.c_str(), s.size());
|
||||||
|
|
||||||
//JMutexAutoLock conlock(m_con_mutex);
|
|
||||||
|
|
||||||
// Send as reliable
|
// Send as reliable
|
||||||
m_con.Send(peer_id, 0, data, true);
|
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)
|
void Server::SendBlocks(float dtime)
|
||||||
{
|
{
|
||||||
DSTACK(__FUNCTION_NAME);
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
11
src/server.h
11
src/server.h
@ -242,9 +242,9 @@ public:
|
|||||||
m_blocks_sent_mutex.Init();
|
m_blocks_sent_mutex.Init();
|
||||||
m_blocks_sending_mutex.Init();
|
m_blocks_sending_mutex.Init();
|
||||||
|
|
||||||
m_dig_mutex.Init();
|
/*m_dig_mutex.Init();
|
||||||
m_dig_time_remaining = 0;
|
m_dig_time_remaining = 0;
|
||||||
m_dig_tool_item = -1;
|
m_dig_tool_item = -1;*/
|
||||||
}
|
}
|
||||||
~RemoteClient()
|
~RemoteClient()
|
||||||
{
|
{
|
||||||
@ -301,11 +301,11 @@ public:
|
|||||||
// Time from last placing or removing blocks
|
// Time from last placing or removing blocks
|
||||||
MutexedVariable<float> m_time_from_building;
|
MutexedVariable<float> m_time_from_building;
|
||||||
|
|
||||||
JMutex m_dig_mutex;
|
/*JMutex m_dig_mutex;
|
||||||
float m_dig_time_remaining;
|
float m_dig_time_remaining;
|
||||||
// -1 = not digging
|
// -1 = not digging
|
||||||
s16 m_dig_tool_item;
|
s16 m_dig_tool_item;
|
||||||
v3s16 m_dig_position;
|
v3s16 m_dig_position;*/
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
@ -416,6 +416,9 @@ private:
|
|||||||
void SendObjectData(float dtime);
|
void SendObjectData(float dtime);
|
||||||
void SendPlayerInfos();
|
void SendPlayerInfos();
|
||||||
void SendInventory(u16 peer_id);
|
void SendInventory(u16 peer_id);
|
||||||
|
void SendChatMessage(u16 peer_id, const std::wstring &message);
|
||||||
|
void BroadcastChatMessage(const std::wstring &message);
|
||||||
|
|
||||||
// Sends blocks to clients
|
// Sends blocks to clients
|
||||||
void SendBlocks(float dtime);
|
void SendBlocks(float dtime);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user