Browse Source

greatly reduced ticking lags due to network synchronization locks

Kolja Strohm 2 years ago
parent
commit
a3cb05bd4d

+ 5 - 5
FactoryCraft/Block.cpp

@@ -215,13 +215,13 @@ void Block::setHP(float hp)
 	}
 	else
 	{
-		NetworkMessage changeMsg;
-		changeMsg.addressBlock(this);
-		char msg[5];
+		NetworkMessage* changeMsg = new NetworkMessage();
+		changeMsg->addressBlock(this);
+		char* msg = new char[5];
 		msg[0] = 0; // hp changed
 		*(float*)(msg + 1) = this->hp;
-		changeMsg.setMessage(msg, 5, 0);
-		Game::INSTANCE->broadcastMessage(&changeMsg);
+		changeMsg->setMessage(msg, 5);
+		Game::INSTANCE->broadcastMessage(changeMsg);
 	}
 }
 

+ 101 - 70
FactoryCraft/Chunk.cpp

@@ -121,7 +121,7 @@ Framework::Either<Block*, int> Chunk::zBlockNeighbor(Framework::Vec3<int> locati
 	return 0;
 }
 
-void Chunk::notifyObservers(NetworkMessage& msg)
+void Chunk::notifyObservers(NetworkMessage* msg)
 {
 	Array<int> remove;
 	int index = 0;
@@ -131,11 +131,12 @@ void Chunk::notifyObservers(NetworkMessage& msg)
 		if (!zE)
 			remove.add(index, 0);
 		else
-			Game::INSTANCE->sendMessage(&msg, zE);
+			Game::INSTANCE->sendMessage(dynamic_cast<NetworkMessage*>(msg->getThis()), zE);
 		index++;
 	}
 	for (int i : remove)
 		observers.remove(i);
+	msg->release();
 }
 
 void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
@@ -155,17 +156,19 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
 			buffer.schreibe((char*)&location.y, 4);
 			sendToClient(&buffer);
 			sendLightToClient(&buffer);
-			NetworkMessage msg;
-			msg.addressDimension();
+			NetworkMessage* msg = new NetworkMessage();
+			msg->addressDimension();
 			char* message = new char[buffer.getSize()];
 			buffer.lese(message, (int)buffer.getSize());
-			msg.setMessage(message, (int)buffer.getSize(), 1);
-			msg.setUseBackground();
+			msg->setMessage(message, (int)buffer.getSize());
+			msg->setUseBackground();
 			Entity* e = Game::INSTANCE->zEntity(id);
 			if (e)
 			{
-				Game::INSTANCE->sendMessage(&msg, e);
+				Game::INSTANCE->sendMessage(msg, e);
 			}
+			else
+				msg->release();
 		});
 }
 
@@ -325,36 +328,15 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
 		change = old != 0;
 	}
 	blocks[index] = block;
-	Either<Block*, int> neighbor = zBlockNeighbor(location + getDirection(NORTH));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(SOUTH, block);
-	if (block)
-		block->setNeighbour(NORTH, neighbor);
-	neighbor = zBlockNeighbor(location + getDirection(EAST));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(WEST, block);
-	if (block)
-		block->setNeighbour(EAST, neighbor);
-	neighbor = zBlockNeighbor(location + getDirection(SOUTH));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(NORTH, block);
-	if (block)
-		block->setNeighbour(SOUTH, neighbor);
-	neighbor = zBlockNeighbor(location + getDirection(WEST));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(EAST, block);
-	if (block)
-		block->setNeighbour(WEST, neighbor);
-	neighbor = zBlockNeighbor(location + getDirection(TOP));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(BOTTOM, block);
-	if (block)
-		block->setNeighbour(TOP, neighbor);
-	neighbor = zBlockNeighbor(location + getDirection(BOTTOM));
-	if (neighbor.isA())
-		((Block*)neighbor)->setNeighbour(TOP, block);
-	if (block)
-		block->setNeighbour(BOTTOM, neighbor);
+	for (int i = 0; i < 6; i++)
+	{
+		Direction d = getDirectionFromIndex(i);
+		Either<Block*, int> neighbor = zBlockNeighbor(location + getDirection(d));
+		if (neighbor.isA())
+			((Block*)neighbor)->setNeighbour(getOppositeDirection(d), block);
+		if (block)
+			block->setNeighbour(d, neighbor);
+	}
 	if (old)
 		old->release();
 	if (change)
@@ -368,15 +350,45 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
 		}
 		if (added)
 		{
-			char msg[9];
+			char* msg = new char[9];
 			msg[0] = 0; // set block
 			*(int*)(msg + 1) = index;
 			*(int*)(msg + 5) = block ? block->zBlockType()->getId() : NoBlockBlockType::ID;
-			NetworkMessage message;
-			message.addressChunck(this);
-			message.setMessage(msg, 9, 0);
+			NetworkMessage* message = new NetworkMessage();
+			message->addressChunck(this);
+			message->setMessage(msg, 9);
 			notifyObservers(message);
-			Game::INSTANCE->updateLightning(getDimensionId(), Vec3<int>(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z));
+			for (int i = 0; i < 6; i++)
+			{
+				Direction d = getDirectionFromIndex(i);
+				Framework::Vec3<int> loc = location + getDirection(d);
+				if (loc.x >= 0 && loc.x < CHUNK_SIZE && loc.y >= 0 && loc.y < CHUNK_SIZE && loc.z >= 0 && loc.z < WORLD_HEIGHT)
+				{
+					NetworkMessage* msg = new NetworkMessage();
+					msg->addressChunck(this);
+					char* message = new char[11];
+					message[0] = 1;
+					int index = ((loc.x * CHUNK_SIZE + loc.y) * WORLD_HEIGHT + loc.z) * 6;
+					*(int*)(message + 1) = index / 6;
+					memcpy(message + 5, lightData + index, 6);
+					msg->setMessage(message, 11);
+					notifyObservers(msg);
+				}
+				else if (loc.z >= 0 && loc.z < WORLD_HEIGHT && i < 4 && zNeighbours[i])
+				{
+					NetworkMessage* msg = new NetworkMessage();
+					msg->addressChunck(zNeighbours[i]);
+					char* message = new char[11];
+					message[0] = 1;
+					loc -= getDirection(d) * CHUNK_SIZE;
+					int index = ((loc.x * CHUNK_SIZE + loc.y) * WORLD_HEIGHT + loc.z) * 6;
+					*(int*)(message + 1) = index / 6;
+					memcpy(message + 5, zNeighbours[i]->getLightData(loc), 6);
+					msg->setMessage(message, 11);
+					notifyObservers(msg);
+				}
+			}
+			Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Vec3<int>(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z));
 		}
 	}
 }
@@ -390,24 +402,13 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
 	if (blockIds[index] != (unsigned short)type)
 	{
 		blockIds[index] = (unsigned short)type;
-		Either<Block*, int> neighbor = zBlockNeighbor(location + getDirection(NORTH));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(SOUTH, type);
-		neighbor = zBlockNeighbor(location + getDirection(EAST));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(WEST, type);
-		neighbor = zBlockNeighbor(location + getDirection(SOUTH));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(NORTH, type);
-		neighbor = zBlockNeighbor(location + getDirection(WEST));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(EAST, type);
-		neighbor = zBlockNeighbor(location + getDirection(TOP));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(BOTTOM, type);
-		neighbor = zBlockNeighbor(location + getDirection(BOTTOM));
-		if (neighbor.isA())
-			((Block*)neighbor)->setNeighbourType(TOP, type);
+		for (int i = 0; i < 6; i++)
+		{
+			Direction d = getDirectionFromIndex(i);
+			Either<Block*, int> neighbor = zBlockNeighbor(location + getDirection(d));
+			if (neighbor.isA())
+				((Block*)neighbor)->setNeighbourType(getOppositeDirection(d), type);
+		}
 		if (isLightSource != wasLightSource)
 		{
 			if (isLightSource)
@@ -417,15 +418,45 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
 		}
 		if (added)
 		{
-			char msg[9];
+			char* msg = new char[9];
 			msg[0] = 0; // set block
 			*(int*)(msg + 1) = index;
 			*(int*)(msg + 5) = type;
-			NetworkMessage message;
-			message.addressChunck(this);
-			message.setMessage(msg, 9, 0);
+			NetworkMessage* message = new NetworkMessage();
+			message->addressChunck(this);
+			message->setMessage(msg, 9);
 			notifyObservers(message);
-			Game::INSTANCE->updateLightning(getDimensionId(), Vec3<int>(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z));
+			for (int i = 0; i < 6; i++)
+			{
+				Direction d = getDirectionFromIndex(i);
+				Framework::Vec3<int> loc = location + getDirection(d);
+				if (loc.x >= 0 && loc.x < CHUNK_SIZE && loc.y >= 0 && loc.y < CHUNK_SIZE && loc.z >= 0 && loc.z < WORLD_HEIGHT)
+				{
+					NetworkMessage* msg = new NetworkMessage();
+					msg->addressChunck(this);
+					char* message = new char[11];
+					message[0] = 1;
+					int index = ((loc.x * CHUNK_SIZE + loc.y) * WORLD_HEIGHT + loc.z) * 6;
+					*(int*)(message + 1) = index / 6;
+					memcpy(message + 5, lightData + index, 6);
+					msg->setMessage(message, 11);
+					notifyObservers(msg);
+				}
+				else if (loc.z >= 0 && loc.z < WORLD_HEIGHT && i < 4 && zNeighbours[i])
+				{
+					NetworkMessage* msg = new NetworkMessage();
+					msg->addressChunck(zNeighbours[i]);
+					char* message = new char[11];
+					message[0] = 1;
+					loc -= getDirection(d) * CHUNK_SIZE;
+					int index = ((loc.x * CHUNK_SIZE + loc.y) * WORLD_HEIGHT + loc.z) * 6;
+					*(int*)(message + 1) = index / 6;
+					memcpy(message + 5, zNeighbours[i]->getLightData(loc), 6);
+					msg->setMessage(message, 11);
+					notifyObservers(msg);
+				}
+			}
+			Game::INSTANCE->updateLightningWithoutWait(getDimensionId(), Vec3<int>(location.x + this->location.x - CHUNK_SIZE / 2, location.y + this->location.y - CHUNK_SIZE / 2, location.z));
 		}
 	}
 }
@@ -731,14 +762,14 @@ void Chunk::setLightData(Framework::Vec3<int> location, unsigned char* data)
 	}
 	if (needSend)
 	{
-		NetworkMessage msg;
-		msg.addressChunck(this);
-		char message[11];
+		NetworkMessage* msg = new NetworkMessage();
+		msg->addressChunck(this);
+		char* message = new char[11];
 		message[0] = 1;
 		*(int*)(message + 1) = index / 6;
 		memcpy(message + 5, data, 6);
-		msg.setMessage(message, 11, 0);
-		msg.setUseBackground();
+		msg->setMessage(message, 11);
+		msg->setUseBackground();
 		notifyObservers(msg);
 	}
 }

+ 1 - 1
FactoryCraft/Chunk.h

@@ -34,7 +34,7 @@ public:
 	Chunk(Framework::Punkt location, int dimensionId, Framework::StreamReader* zReader);
 	~Chunk();
 
-	void notifyObservers(NetworkMessage& msg);
+	void notifyObservers(NetworkMessage* msg);
 	void addObserver(Entity* zEntity, DoLaterHandler& laterHandler);
 	void removeObserver(Entity* zEntity);
 	void api(Framework::StreamReader* zRequest, Entity* zSource, DoLaterHandler& laterHandler);

+ 8 - 8
FactoryCraft/CraftingStorage.cpp

@@ -33,15 +33,15 @@ BasicShapedCrafter::BasicShapedCrafter(int width, int height, Inventory* zInvent
 			calculateOutputPreview();
 			if (old == currentRecipie)
 			{
-				NetworkMessage message;
+				NetworkMessage* message = new NetworkMessage();
 				getOutputPreview(message);
-				message.addressGui(id);
-				Game::INSTANCE->sendMessage(&message, zSource);
+				message->addressGui(id);
+				Game::INSTANCE->sendMessage(message, zSource);
 			}
 		});
 }
 
-void BasicShapedCrafter::getOutputPreview(NetworkMessage& message)
+void BasicShapedCrafter::getOutputPreview(NetworkMessage* zMessage)
 {
 	if (currentRecipie)
 	{
@@ -71,14 +71,14 @@ void BasicShapedCrafter::getOutputPreview(NetworkMessage& message)
 		msg[0] = 100; // set crafting result
 		*(int*)(msg + 1) = count;
 		buffer.lese(msg + 5, (int)buffer.getSize());
-		message.setMessage(msg, 5 + (int)buffer.getSize(), 1);
+		zMessage->setMessage(msg, 5 + (int)buffer.getSize());
 	}
 	else
 	{
-		char msg[5];
+		char* msg = new char[5];
 		msg[0] = 100; // set crafting result
 		*(int*)(msg + 1) = 0;
-		message.setMessage(msg, 5, 0);
+		zMessage->setMessage(msg, 5);
 	}
 }
 
@@ -180,7 +180,7 @@ void BasicShapedCrafter::calculateOutputPreview()
 		if (recipie != currentRecipie)
 		{
 			currentRecipie = recipie;
-			NetworkMessage message;
+			NetworkMessage* message = new NetworkMessage();
 			getOutputPreview(message);
 			zInventory->notyObservers(message);
 		}

+ 1 - 1
FactoryCraft/CraftingStorage.h

@@ -36,7 +36,7 @@ private:
 	int width;
 	int height;
 
-	void getOutputPreview(NetworkMessage& msg);
+	void getOutputPreview(NetworkMessage* zMsg);
 
 public:
 	BasicShapedCrafter(int width, int height, Inventory* zInventory, Framework::Text recipieList);

+ 39 - 14
FactoryCraft/Dimension.cpp

@@ -76,21 +76,41 @@ void Dimension::thread()
 	ZeitMesser messer;
 	messer.messungStart();
 	double time = 0;
+	Framework::Array<Framework::Vec3<int>> internalLightUpdateQueue;
 	while (true)
 	{
-		lightCs.lock();
-		if (!lightUpdateQueue.getEintragAnzahl())
+		Vec3<int> position;
+		if (internalLightUpdateQueue.getEintragAnzahl())
 		{
-			lightCs.unlock();
-			messer.messungEnde();
-			time += messer.getSekunden();
-			Sleep(500);
-			messer.messungStart();
-			continue;
+			position = internalLightUpdateQueue.get(0);
+			internalLightUpdateQueue.remove(0);
+		}
+		else
+		{
+			if (priorizedLightUpdateQueue.getEintragAnzahl())
+			{
+				prioLightCs.lock();
+				position = priorizedLightUpdateQueue.get(0);
+				priorizedLightUpdateQueue.remove(0);
+				prioLightCs.unlock();
+			}
+			else
+			{
+				if (!lightUpdateQueue.getEintragAnzahl())
+				{
+					messer.messungEnde();
+					time += messer.getSekunden();
+					Sleep(500);
+					messer.messungStart();
+					continue;
+				}
+				lightCs.lock();
+				position = lightUpdateQueue.get(0);
+				lightUpdateQueue.remove(0);
+				lightCs.unlock();
+			}
+
 		}
-		Vec3<int> position = lightUpdateQueue.get(0);
-		lightUpdateQueue.remove(0);
-		lightCs.unlock();
 		// TODO: do not remove chunks while light calculation
 		Chunk* chunk = zChunk(Game::INSTANCE->getChunkCenter(position.x, position.y));
 		if (position.z >= 0 && position.z < WORLD_HEIGHT)
@@ -153,7 +173,7 @@ void Dimension::thread()
 					{
 						chunk->setLightData(Vec3<int>(x, y, position.z), newLight);
 						for (int j = 0; j < 6; j++)
-							updateLightning(position + getDirection(getDirectionFromIndex(j)));
+							internalLightUpdateQueue.add(position + getDirection(getDirectionFromIndex(j)), 0);
 						break;
 					}
 				}
@@ -513,9 +533,15 @@ void Dimension::updateLightning(Vec3<int> location)
 	lightCs.unlock();
 }
 
+void Dimension::updateLightningWithoutWait(Framework::Vec3<int> location)
+{
+	prioLightCs.lock();
+	priorizedLightUpdateQueue.add(location, 0);
+	prioLightCs.unlock();
+}
+
 void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
 {
-	lightCs.lock();
 	if (lightUpdateQueue.getEintragAnzahl() > 300000)
 	{
 		std::cout << "warning: light calculation queue is over 300000 blocks long";
@@ -534,5 +560,4 @@ void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
 			updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 8 + j, chunkCenter.y + CHUNK_SIZE / 8, i));
 		}
 	}
-	lightCs.unlock();
 }

+ 3 - 0
FactoryCraft/Dimension.h

@@ -28,7 +28,9 @@ private:
 	void getAddrOf(Framework::Punkt cPos, char* addr) const;
 	void getAddrOfWorld(Framework::Punkt wPos, char* addr) const;
 	Framework::Array<Framework::Vec3<int>> lightUpdateQueue;
+	Framework::Array<Framework::Vec3<int>> priorizedLightUpdateQueue;
 	Framework::Critical lightCs;
+	Framework::Critical prioLightCs;
 
 public:
 	Dimension(int id);
@@ -56,5 +58,6 @@ public:
 	void removeEntity(int id);
 	void removeSubscriptions(Entity* zEntity);
 	void updateLightning(Framework::Vec3<int> location);
+	void updateLightningWithoutWait(Framework::Vec3<int> location);
 	void updateLightAtChunkBorders(Framework::Punkt chunkCenter);
 };

+ 18 - 14
FactoryCraft/Entity.cpp

@@ -43,16 +43,20 @@ void ActionTarget::applyItemSkillOnTarget(Entity* zActor, ItemSkill* zItemSkill,
 void ActionTarget::placeBlock(Entity* zActor, Item* zItem)
 {
 	// TODO: check stamina of actor
-	Block* block = zItem->zPlacedBlockType()->createBlockAt(blockPos + getDirection(targetBlockSide), zItem);
-	if (block)
+	auto zB = Game::INSTANCE->zBlockAt(blockPos + getDirection(targetBlockSide), zActor->getCurrentDimensionId());
+	if ((zB.isA() && zB.getA()->isPassable()) || (zB.isB() && StaticRegistry<BlockType>::INSTANCE.zElement(zB.getB())->zDefault()->isPassable()))
 	{
-		Game::INSTANCE->zDimension(zActor->getCurrentDimensionId())->placeBlock(block->getPos(), block);
-		zItem->onPlaced();
-		// TODO: decrese stamina of actor
+		Block* block = zItem->zPlacedBlockType()->createBlockAt(blockPos + getDirection(targetBlockSide), zItem);
+		if (block)
+		{
+			Game::INSTANCE->zDimension(zActor->getCurrentDimensionId())->placeBlock(block->getPos(), block);
+			zItem->onPlaced();
+			// TODO: decrese stamina of actor
+		}
 	}
 }
 
-void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage& msg)
+void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage* zMsg)
 {
 	if (zTarget)
 	{
@@ -62,7 +66,7 @@ void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage& msg)
 			message[0] = 3;
 			message[1] = 1;
 			*(int*)(message + 2) = zTarget->entityId;
-			msg.setMessage(message, 6, 1);
+			zMsg->setMessage(message, 6);
 		}
 		else
 		{
@@ -73,7 +77,7 @@ void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage& msg)
 			*(int*)(message + 6) = zTarget->blockPos.y;
 			*(int*)(message + 10) = zTarget->blockPos.z;
 			*(int*)(message + 14) = zTarget->targetBlockSide;
-			msg.setMessage(message, 18, 1);
+			zMsg->setMessage(message, 18);
 		}
 	}
 	else
@@ -81,7 +85,7 @@ void ActionTarget::toMessage(const ActionTarget* zTarget, NetworkMessage& msg)
 		char* message = new char[2];
 		message[0] = 3;
 		message[1] = 0;
-		msg.setMessage(message, 2, 1);
+		zMsg->setMessage(message, 2);
 	}
 }
 
@@ -200,9 +204,9 @@ void Entity::addMovementFrame(MovementFrame& frame)
 	cs.lock();
 	movements.add(frame);
 	cs.unlock();
-	NetworkMessage message;
-	message.addressEntity(this);
-	char msg[37];
+	NetworkMessage* message = new NetworkMessage();
+	message->addressEntity(this);
+	char* msg = new char[37];
 	msg[0] = 0;
 	*(float*)(msg + 1) = frame.direction.x;
 	*(float*)(msg + 5) = frame.direction.y;
@@ -212,8 +216,8 @@ void Entity::addMovementFrame(MovementFrame& frame)
 	*(float*)(msg + 21) = frame.targetPosition.z;
 	*(int*)(msg + 25) = frame.movementFlags;
 	*(double*)(msg + 29) = frame.duration;
-	message.setMessage(msg, 37, 0);
-	Game::INSTANCE->broadcastMessage(&message);
+	message->setMessage(msg, 37);
+	Game::INSTANCE->broadcastMessage(message);
 	faceDir = frame.direction;
 	// TODO implement subscription system to notify only interested clients
 }

+ 1 - 1
FactoryCraft/Entity.h

@@ -31,7 +31,7 @@ public:
 
 	void applyItemSkillOnTarget(Entity* zActor, ItemSkill* zItemSkill, Item* zUsedItem);
 	void placeBlock(Entity* zActor, Item* zItem);
-	static void toMessage(const ActionTarget* zTarget, NetworkMessage& msg);
+	static void toMessage(const ActionTarget* zTarget, NetworkMessage* zMsg);
 	static void save(ActionTarget* zTarget, Framework::StreamWriter* zWriter);
 	static ActionTarget* load(Framework::StreamReader* zReader);
 };

+ 107 - 24
FactoryCraft/Game.cpp

@@ -12,7 +12,7 @@
 using namespace Framework;
 
 GameClient::GameClient(Player* zPlayer, FCKlient* client)
-	: ReferenceCounter(),
+	: Thread(),
 	zPlayer(zPlayer),
 	client(client),
 	viewDistance(DEFAULT_VIEW_DISTANCE),
@@ -20,7 +20,7 @@ GameClient::GameClient(Player* zPlayer, FCKlient* client)
 	online(1),
 	finished(0)
 {
-	new AsynchronCall("Game Client", [this]()
+	new AsynchronCall("Game Client Updates", [this]()
 		{
 			while (online)
 			{
@@ -44,17 +44,70 @@ GameClient::GameClient(Player* zPlayer, FCKlient* client)
 			}
 			finished = 1;
 		});
+	start();
 }
 
 GameClient::~GameClient()
 {
 	online = 0;
 	updateSync.notify();
+	emptyForegroundQueueSync.notifyAll();
+	emptyBackgroundQueueSync.notifyAll();
+	foregroundQueueSync.notify();
+	backgroundQueueSync.notify();
 	while (!finished)
 		Sleep(100);
 	client->release();
 }
 
+void GameClient::thread()
+{
+	new AsynchronCall("Game Client Background", [this]()
+		{
+			while (online)
+			{
+				queueCs.lock();
+				if (backgroundQueue.hat(0))
+				{
+					NetworkMessage* message = backgroundQueue.get(0);
+					backgroundQueue.remove(0);
+					queueCs.unlock();
+					background.lock();
+					message->writeTo(client->zBackgroundWriter());
+					background.unlock();
+					message->release();
+				}
+				else
+				{
+					queueCs.unlock();
+					emptyBackgroundQueueSync.notifyAll();
+					backgroundQueueSync.wait();
+				}
+			}
+			finished = 1;
+		});
+	while (online)
+	{
+		queueCs.lock();
+		if (foregroundQueue.hat(0))
+		{
+			NetworkMessage* message = foregroundQueue.get(0);
+			foregroundQueue.remove(0);
+			queueCs.unlock();
+			foreground.lock();
+			message->writeTo(client->zForegroundWriter());
+			foreground.unlock();
+			message->release();
+		}
+		else
+		{
+			queueCs.unlock();
+			emptyForegroundQueueSync.notifyAll();
+			foregroundQueueSync.wait();
+		}
+	}
+}
+
 void GameClient::sendWorldUpdate(WorldUpdate* update)
 {
 	bool add = 0;
@@ -125,19 +178,37 @@ bool GameClient::isOnline() const
 	return online;
 }
 
-void GameClient::sendResponse(NetworkMessage* zResponse)
+void GameClient::sendResponse(NetworkMessage* response)
 {
-	if (zResponse->isUseBackground())
+	queueCs.lock();
+	if (response->isUseBackground())
 	{
-		background.lock();
-		zResponse->writeTo(client->zBackgroundWriter());
-		background.unlock();
+		if (backgroundQueue.getEintragAnzahl() > 20)
+		{
+			queueCs.unlock();
+			emptyBackgroundQueueSync.wait();
+			queueCs.lock();
+		}
+		backgroundQueue.add(response);
+		queueCs.unlock();
+		backgroundQueueSync.notify();
 	}
 	else
 	{
-		foreground.lock();
-		zResponse->writeTo(client->zForegroundWriter());
-		foreground.unlock();
+		if (foregroundQueue.getEintragAnzahl() > 100)
+		{
+			queueCs.unlock();
+			std::cout << "WARNING: Game paused because nework connection to " << zPlayer->getName() << " is to slow.\n";
+			ZeitMesser m;
+			m.messungStart();
+			emptyForegroundQueueSync.wait();
+			m.messungEnde();
+			std::cout << "WARNING: Game resumed after " << m.getSekunden() << " seconds.\n";
+			queueCs.lock();
+		}
+		foregroundQueue.add(response);
+		queueCs.unlock();
+		foregroundQueueSync.notify();
 	}
 }
 
@@ -344,7 +415,7 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
 {
 	char type;
 	zRequest->lese(&type, 1);
-	NetworkMessage response;
+	NetworkMessage* response = new NetworkMessage();
 	switch (type)
 	{
 	case 1: // world
@@ -355,11 +426,11 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
 			dim = new Dimension(zOrigin->zEntity()->getCurrentDimensionId());
 			addDimension(dim);
 		}
-		dim->api(zRequest, &response, zOrigin->zEntity());
+		dim->api(zRequest, response, zOrigin->zEntity());
 		break;
 	}
 	case 2: // player
-		zOrigin->zEntity()->playerApi(zRequest, &response);
+		zOrigin->zEntity()->playerApi(zRequest, response);
 		break;
 	case 3: // entity
 	{
@@ -370,7 +441,7 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
 			Entity* entity = dim->zEntity(id);
 			if (entity)
 			{
-				entity->api(zRequest, &response);
+				entity->api(zRequest, response);
 				break;
 			}
 		}
@@ -398,18 +469,22 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
 			target = zBlockAt(pos, dim);
 		}
 		if (target)
-			target->inventoryApi(zRequest, &response, zOrigin->zEntity());
+			target->inventoryApi(zRequest, response, zOrigin->zEntity());
 		break;
 	}
 	default:
 		std::cout << "received unknown api request in game with type " << (int)type << "\n";
 	}
-	if (!response.isEmpty())
+	if (!response->isEmpty())
 	{
-		if (response.isBroadcast())
-			broadcastMessage(&response);
+		if (response->isBroadcast())
+			broadcastMessage(response);
 		else
-			zOrigin->sendResponse(&response);
+			zOrigin->sendResponse(response);
+	}
+	else
+	{
+		response->release();
 	}
 }
 
@@ -420,22 +495,30 @@ void Game::updateLightning(int dimensionId, Vec3<int> location)
 		zDim->updateLightning(location);
 }
 
-void Game::broadcastMessage(NetworkMessage* zResponse)
+void Game::updateLightningWithoutWait(int dimensionId, Vec3<int> location)
+{
+	Dimension* zDim = zDimension(dimensionId);
+	if (zDim)
+		zDim->updateLightningWithoutWait(location);
+}
+
+void Game::broadcastMessage(NetworkMessage* response)
 {
 	for (auto client : *clients)
-		client->sendResponse(zResponse);
+		client->sendResponse(dynamic_cast<NetworkMessage*>(response->getThis()));
 }
 
-void Game::sendMessage(NetworkMessage* zResponse, Entity* zTargetPlayer)
+void Game::sendMessage(NetworkMessage* response, Entity* zTargetPlayer)
 {
 	for (auto client : *clients)
 	{
 		if (client->zEntity()->getId() == zTargetPlayer->getId())
 		{
-			client->sendResponse(zResponse);
-			break;
+			client->sendResponse(response);
+			return;
 		}
 	}
+	response->release();
 }
 
 bool Game::requestWorldUpdate(WorldUpdate* update)

+ 13 - 4
FactoryCraft/Game.h

@@ -19,7 +19,7 @@
 
 class FCKlient;
 
-class GameClient : public virtual Framework::ReferenceCounter
+class GameClient : public Framework::Thread
 {
 private:
 	Player* zPlayer;
@@ -30,6 +30,13 @@ private:
 	Framework::Synchronizer updateSync;
 	RCArray<InMemoryBuffer> requests;
 	RCArray<WorldUpdate> updateQueue;
+	RCArray<NetworkMessage> backgroundQueue;
+	RCArray<NetworkMessage> foregroundQueue;
+	Framework::Synchronizer foregroundQueueSync;
+	Framework::Synchronizer backgroundQueueSync;
+	Critical queueCs;
+	Framework::Synchronizer emptyForegroundQueueSync;
+	Framework::Synchronizer emptyBackgroundQueueSync;
 	int viewDistance;
 	bool first;
 	bool online;
@@ -39,12 +46,13 @@ public:
 	GameClient(Player* zPlayer, FCKlient* client);
 	~GameClient();
 
+	void thread() override;
 	void sendWorldUpdate(WorldUpdate* update);
 	void reply();
 	void logout();
 	void addMessage(StreamReader* reader);
 	bool isOnline() const;
-	void sendResponse(NetworkMessage* zResponse);
+	void sendResponse(NetworkMessage* response);
 	Player* zEntity() const;
 	void sendTypes();
 
@@ -85,8 +93,9 @@ public:
 	void initialize();
 	void api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin);
 	void updateLightning(int dimensionId, Vec3<int> location);
-	void broadcastMessage(NetworkMessage* zResponse);
-	void sendMessage(NetworkMessage* zResponse, Entity* zTargetPlayer);
+	void updateLightningWithoutWait(int dimensionId, Vec3<int> location);
+	void broadcastMessage(NetworkMessage* response);
+	void sendMessage(NetworkMessage* response, Entity* zTargetPlayer);
 	bool requestWorldUpdate(WorldUpdate* update);
 	GameClient* addPlayer(FCKlient* client, Framework::Text name);
 	bool doesChunkExist(int x, int y, int dimension);

+ 18 - 13
FactoryCraft/Inventory.cpp

@@ -303,12 +303,12 @@ bool Inventory::allowPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem
 
 void Inventory::afterPullStack(ItemSlot* zSlot, Direction dir, const Item* zItem, int count)
 {
-	NetworkMessage msg;
-	char message[9];
+	NetworkMessage* msg = new NetworkMessage();
+	char* message = new char[9];
 	message[0] = 1; // set count of items
 	*(int*)(message + 1) = zSlot->getId();
 	*(int*)(message + 5) = zSlot->getNumberOfItems();
-	msg.setMessage(message, 9, 0);
+	msg->setMessage(message, 9);
 	notyObservers(msg);
 	for (auto call : afterPullStackCalls)
 		call(zSlot, dir, zItem, count);
@@ -318,18 +318,18 @@ void Inventory::afterPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem
 {
 	if (zSlot->getNumberOfItems() > count)
 	{
-		NetworkMessage msg;
-		char message[9];
+		NetworkMessage* msg = new NetworkMessage();
+		char* message = new char[9];
 		message[0] = 1; // set count of items
 		*(int*)(message + 1) = zSlot->getId();
 		*(int*)(message + 5) = zSlot->getNumberOfItems();
-		msg.setMessage(message, 9, 0);
+		msg->setMessage(message, 9);
 		notyObservers(msg);
 	}
 	else
 	{
-		NetworkMessage msg;
-		char message[29];
+		NetworkMessage* msg = new NetworkMessage();
+		char* message = new char[29];
 		message[0] = 2; // add new stack
 		*(int*)(message + 1) = zSlot->getId();
 		*(int*)(message + 5) = zSlot->getNumberOfItems();
@@ -339,7 +339,7 @@ void Inventory::afterPushStack(ItemSlot* zSlot, Direction dir, const Item* zItem
 		*(float*)(message + 17) = zItem->getDurability();
 		*(float*)(message + 21) = zItem->getMaxDurability();
 		*(int*)(message + 25) = zItem->zItemType()->getId();
-		msg.setMessage(message, 29, 0);
+		msg->setMessage(message, 29);
 		notyObservers(msg);
 	}
 	for (auto call : afterPushStackCalls)
@@ -389,7 +389,7 @@ void Inventory::saveInventory(Framework::StreamWriter* zWriter)
 	}
 }
 
-void Inventory::notyObservers(NetworkMessage& msg)
+void Inventory::notyObservers(NetworkMessage* msg)
 {
 	cs.lock();
 	int index = 0;
@@ -399,8 +399,8 @@ void Inventory::notyObservers(NetworkMessage& msg)
 		Entity* e = Game::INSTANCE->zEntity(observer.getFirst());
 		if (e)
 		{
-			msg.addressGui(observer.getSecond());
-			Game::INSTANCE->sendMessage(&msg, e);
+			msg->addressGui(observer.getSecond());
+			Game::INSTANCE->sendMessage(dynamic_cast<NetworkMessage*>(msg->getThis()), e);
 		}
 		else
 			toDelete.add(index, 0);
@@ -409,6 +409,7 @@ void Inventory::notyObservers(NetworkMessage& msg)
 	for (int i : toDelete)
 		observers.remove(i);
 	cs.unlock();
+	msg->release();
 }
 
 void Inventory::removeObserver(Entity* zSource, Framework::Text id)
@@ -491,6 +492,8 @@ void Inventory::localTransaction(Array< ItemSlot* >* zSourceSlots, Array< ItemSl
 										toDelete.add(index, 0);
 									targetSlot->addItems(stack, inDir);
 									updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId());
+									afterPullStack(sourceSlot, outDir, targetSlot->zStack()->zItem(), number);
+									afterPushStack(targetSlot, inDir, targetSlot->zStack()->zItem(), number);
 									if (stack->getSize())
 									{
 										cs.unlock();
@@ -530,6 +533,8 @@ void Inventory::localTransaction(Array< ItemSlot* >* zSourceSlots, Array< ItemSl
 							targetSlot->addItems(stack, inDir);
 							updateCache(sourceSlot, targetSlot->zStack()->zItem()->zItemType()->getId());
 							updateCache(targetSlot, -1);
+							afterPullStack(sourceSlot, outDir, targetSlot->zStack()->zItem(), number);
+							afterPushStack(targetSlot, inDir, targetSlot->zStack()->zItem(), number);
 							if (stack->getSize())
 							{
 								cs.unlock();
@@ -737,7 +742,7 @@ void Inventory::inventoryApi(Framework::StreamReader* zRequest, NetworkMessage*
 		msg[0] = 0;
 		*(int*)(msg + 1) = count;
 		buffer.lese(msg + 5, (int)buffer.getSize());
-		zResponse->setMessage(msg, 5 + (int)buffer.getSize(), 1);
+		zResponse->setMessage(msg, 5 + (int)buffer.getSize());
 		break;
 	}
 	case 1: // remove Observer

+ 1 - 1
FactoryCraft/Inventory.h

@@ -64,7 +64,7 @@ protected:
 public:
 	Inventory(const Framework::Vec3<float> location, bool hasInventory);
 	virtual ~Inventory();
-	void notyObservers(NetworkMessage& msg);
+	void notyObservers(NetworkMessage* msg);
 	const ItemSlot* zSlot(int id) const;
 	void addSlot(ItemSlot* slot);
 	void localTransaction(Framework::Array< ItemSlot* >* zSourceSlots, Framework::Array<ItemSlot*>* zTargetSlots, ItemFilter* zFilter, int count, Direction outDir, Direction inDir);

+ 2 - 2
FactoryCraft/Item.cpp

@@ -7,8 +7,8 @@ Item::Item(const ItemType* zType, const char* name)
 	zBlockType(0),
 	damage(0),
 	maxDamage(0),
-	durability(0),
-	maxDurability(0),
+	durability(1),
+	maxDurability(1),
 	eatable(0),
 	placeable(0),
 	equippable(0),

+ 4 - 7
FactoryCraft/NetworkMessage.cpp

@@ -4,20 +4,19 @@
 #include "Game.h"
 
 NetworkMessage::NetworkMessage()
+	: Framework::ReferenceCounter()
 {
 	address = 0;
 	addressLength = 0;
 	broadcast = 0;
 	message = 0;
-	msgDelete = 0;
 	msgLength = 0;
 	useBackground = 0;
 }
 
 NetworkMessage::~NetworkMessage()
 {
-	if (msgDelete)
-		delete[] message;
+	delete[] message;
 	delete[] address;
 }
 
@@ -86,13 +85,11 @@ void NetworkMessage::addressGui(Framework::Text elementId)
 	memcpy(address + 4, elementId.getText(), elementId.getLength());
 }
 
-void NetworkMessage::setMessage(char* msg, int length, bool deleteMsg)
+void NetworkMessage::setMessage(char* msg, int length)
 {
-	if (msgDelete)
-		delete[] message;
+	delete[] message;
 	message = msg;
 	msgLength = length;
-	msgDelete = deleteMsg;
 }
 
 void NetworkMessage::setUseBackground()

+ 3 - 3
FactoryCraft/NetworkMessage.h

@@ -2,19 +2,19 @@
 
 #include <Writer.h>
 #include <Vec3.h>
+#include <ReferenceCounter.h>
 
 class Chunk;
 class Block;
 class Entity;
 
-class NetworkMessage
+class NetworkMessage : public virtual Framework::ReferenceCounter
 {
 private:
 	char* address;
 	char addressLength;
 	bool broadcast;
 	char* message;
-	bool msgDelete;
 	int msgLength;
 	bool useBackground;
 
@@ -28,7 +28,7 @@ public:
 	void addressDimension();
 	void openDialog(Framework::Text dialogName);
 	void addressGui(Framework::Text elementId);
-	void setMessage(char* msg, int length, bool deleteMsg);
+	void setMessage(char* msg, int length);
 	void setUseBackground();
 	void sendToAll();
 

+ 10 - 11
FactoryCraft/Player.cpp

@@ -37,9 +37,9 @@ Player::Player(Framework::Vec3<float> location, int dimensionId, int entityId)
 
 void Player::onTargetChange()
 {
-	NetworkMessage msg;
+	NetworkMessage* msg = new NetworkMessage();
 	ActionTarget::toMessage(zTarget(), msg);
-	Game::INSTANCE->sendMessage(&msg, this);
+	Game::INSTANCE->sendMessage(msg, this);
 }
 
 Framework::Text Player::getInventoryUIML()
@@ -86,7 +86,6 @@ void Player::useItemSlot(ItemSlot* zSlot)
 			if (item->getDurability() > 0)
 			{ // put used item back
 				stack->addToStack(item);
-				// TODO: use inventory wrapper to update the cache of the inventory
 				if (!zSlot->numberOfAddableItems(stack, NO_DIRECTION))
 				{ // move other items to other space
 					ItemStack* oldItems = takeItemsOut(zSlot, zSlot->zStack()->getSize(), NO_DIRECTION);
@@ -104,7 +103,7 @@ void Player::useItemSlot(ItemSlot* zSlot)
 			{ // item is broken
 				// move other items of the same type to the slot
 				Array< ItemSlot*> fromSlots;
-				for (ItemSlot* slot : itemBar)
+				for (ItemSlot* slot : *this)
 				{
 					if (slot != zSlot)
 						fromSlots.add(slot);
@@ -208,13 +207,13 @@ void Player::playerApi(Framework::StreamReader* zRequest, NetworkMessage* zRespo
 	{ // switch item bar position
 		zRequest->lese((char*)&leftHandPosition, 4);
 		leftHandPosition = leftHandPosition % itemBar.getEintragAnzahl();
-		NetworkMessage msg;
-		msg.addressGui("gui_item_bar");
-		char message[5];
+		NetworkMessage* msg = new NetworkMessage();
+		msg->addressGui("gui_item_bar");
+		char* message = new char[5];
 		message[0] = 3; // set selected slot
 		*(int*)(message + 1) = leftHandPosition;
-		msg.setMessage(message, 5, 0);
-		Game::INSTANCE->sendMessage(&msg, this);
+		msg->setMessage(message, 5);
+		Game::INSTANCE->sendMessage(msg, this);
 		break;
 	}
 	case 4:
@@ -226,7 +225,7 @@ void Player::playerApi(Framework::StreamReader* zRequest, NetworkMessage* zRespo
 		char* msg = new char[msgSize];
 		*(int*)msg = uiml.getLength();
 		memcpy(msg + 4, uiml.getText(), uiml.getLength());
-		zResponse->setMessage(msg, msgSize, 1);
+		zResponse->setMessage(msg, msgSize);
 		break;
 	}
 	case 5:
@@ -239,7 +238,7 @@ void Player::playerApi(Framework::StreamReader* zRequest, NetworkMessage* zRespo
 		msg[1] = 2; // set gui
 		*(int*)(msg + 2) = uiml.getLength();
 		memcpy(msg + 6, uiml.getText(), uiml.getLength());
-		zResponse->setMessage(msg, msgSize, 1);
+		zResponse->setMessage(msg, msgSize);
 		break;
 	}
 	case 6: