summaryrefslogblamecommitdiffstats
path: root/src/GameState.cpp
blob: be408ddffdd2c2f8c3d8f21db5ecb0e6632e094a (plain) (tree)
1
2
3
4
5
6
7
8
9
                        


                                       
                   
 
                    
                     
 
                                          
                       















                                                                                                         
























































                                                                                                                                      
 
 
                                                           
















































































                                                                                                                     
                                                                                                                     






































































































                                                                                                               
                                                         












                                                                                                                              
 

                                                








                                                                                                                      









                                                                                                                         
















                                                                                                                         























































                                                                                                                             
                                                             

                                                                                                          
                                                                                                   
 



















                                                                                           













                                                                                   
                              
                 































































































                                                                                                                     

 















































                                                                                


                                                             


                                                   


                                                          












                                                                


                                      













                                                                                

 



                                                                    
                                

                                             
 


                                                                                                      
 
                        


                                 


                                                                                                       

 





                                                           


                                                                                                       
 
 
                                                                     

























                                                                                                          

 
                              

                                             
 


                                                                                                    
 

                                                                    
 
#include "GameState.hpp"

#include <glm/gtc/matrix_transform.hpp>
#include <easylogging++.h>
#include <optick.h>

#include "Event.hpp"
#include "Packet.hpp"

void GameState::Update(double deltaTime) {
	OPTICK_EVENT();

	if (!gameStatus.isGameStarted) {
		if (!receivedEnoughChunks) {
			receivedEnoughChunks = world.GetSectionsList().size() >= 49;
		}

		if (receivedJoinGame && receivedEnoughChunks && receivedFirstPlayerPosAndLook) {
			gameStatus.isGameStarted = true;
			LOG(INFO) << "Game is started";
			PUSH_EVENT("RemoveLoadingScreen", 0);

			auto packetPerformRespawn = std::make_shared<PacketClientStatus>(0);
			PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packetPerformRespawn));
		}
	}

	if (!gameStatus.isGameStarted)
		return;

	std::chrono::steady_clock clock;
	static auto timeOfPreviousSendedPacket(clock.now());
	auto delta = clock.now() - timeOfPreviousSendedPacket;
	using namespace std::chrono_literals;
	if (delta >= 50ms) {
		auto packetToSend = std::make_shared<PacketPlayerPositionAndLookSB>(
			player->pos.x, player->pos.y, player->pos.z,
			player->yaw, player->pitch, player->onGround);

		auto packet = std::static_pointer_cast<Packet>(packetToSend);
		PUSH_EVENT("SendPacket", packet);
		timeOfPreviousSendedPacket = clock.now();
	}

	bool prevOnGround = player->onGround;
	world.UpdatePhysics(deltaTime);
	if (player->onGround != prevOnGround) {
		auto updatePacket = std::make_shared<PacketPlayerPosition>(
			player->pos.x, player->pos.y,
			player->pos.z, player->onGround);

		auto packet = std::static_pointer_cast<Packet>(updatePacket);
		PUSH_EVENT("SendPacket", packet);
	}


	double playerYaw = Entity::DecodeYaw(player->yaw);
	double playerPitch = Entity::DecodePitch(player->pitch);

	glm::vec3 direction;
	direction.x = cos(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));
	direction.y = sin(glm::radians(playerPitch));
	direction.z = sin(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));

	RaycastResult raycast = world.Raycast(player->pos + player->EyeOffset, direction);
	if (raycast.isHit != selectionStatus.isBlockSelected || ((raycast.isHit == true && selectionStatus.isBlockSelected == true) &&
		selectionStatus.selectedBlock != raycast.hitBlock)) {
		PUSH_EVENT("SelectedBlockChanged", 0);
	}

	if (raycast.isHit) {
		selectionStatus.selectedBlock = raycast.hitBlock;
		selectionStatus.distanceToSelectedBlock = (player->pos - raycast.hitPos).GetLength();
	}
	else {
		selectionStatus.selectedBlock = Vector(0, 0, 0);
		selectionStatus.distanceToSelectedBlock = 0.0f;
	}

	selectionStatus.isBlockSelected = raycast.isHit;
	selectionStatus.raycastHit = raycast.hitPos;

	if (timeStatus.doDaylightCycle)
		timeStatus.interpolatedTimeOfDay += 20.0 * deltaTime;
}

void GameState::UpdatePacket(std::shared_ptr<Packet> ptr) {
	switch ((PacketNamePlayCB)ptr->GetPacketId()) {
		case SpawnObject: {
			auto packet = std::static_pointer_cast<PacketSpawnObject>(ptr);
			Entity entity = CreateObject(static_cast<ObjectType>(packet->Type));
			entity.entityId = packet->EntityId;
			entity.pos = VectorF(packet->X, packet->Y, packet->Z);
			entity.uuid = packet->ObjectUuid;
			entity.vel = Entity::DecodeVelocity(packet->VelocityX, packet->VelocityY, packet->VelocityZ);
			entity.yaw = packet->Yaw / 256.0;
			entity.pitch = packet->Pitch / 256.0;
			entity.renderColor = glm::vec3(0, 1, 0);
			world.AddEntity(entity);
			PUSH_EVENT("EntityChanged", entity.entityId);
			break;
		}

		case SpawnExperienceOrb:
			break;

		case SpawnGlobalEntity:
			break;

		case SpawnMob: {
			auto packet = std::static_pointer_cast<PacketSpawnMob>(ptr);
			Entity entity;
			entity.entityId = packet->EntityId;
			entity.pos = VectorF(packet->X, packet->Y, packet->Z);
			entity.uuid = packet->EntityUuid;
			entity.vel = Entity::DecodeVelocity(packet->VelocityX, packet->VelocityY, packet->VelocityZ);
			entity.yaw = packet->Yaw / 256.0;
			entity.pitch = packet->Pitch / 256.0;
			entity.renderColor = glm::vec3(0, 0, 1);
			world.AddEntity(entity);
			PUSH_EVENT("EntityChanged", entity.entityId);
			break;
		}

		case SpawnPainting:
			break;

		case SpawnPlayer: {
			auto packet = std::static_pointer_cast<PacketSpawnPlayer>(ptr);
			Entity entity;
			entity.entityId = packet->EntityId;
			entity.pos = VectorF(packet->X, packet->Y, packet->Z);
			entity.uuid = packet->PlayerUuid;
			entity.yaw = packet->Yaw / 256.0;
			entity.pitch = packet->Pitch / 256.0;
			entity.renderColor = glm::vec3(1, 0, 0);
			entity.height = 1.8;
			entity.width = 0.6;
			world.AddEntity(entity);
			PUSH_EVENT("EntityChanged", entity.entityId);
			break;
		}
		case AnimationCB:
			break;
		case Statistics:
			break;
		case BlockBreakAnimation:
			break;
		case UpdateBlockEntity:
			break;
		case BlockAction:
			break;

		case BlockChange: {
			auto packet = std::static_pointer_cast<PacketBlockChange>(ptr);
			world.ParseChunkData(packet);
			break;
		}

		case BossBar:
			break;
		case ServerDifficulty:
			break;
		case TabCompleteCB:
			break;

		case ChatMessageCB: {
			auto packet = std::static_pointer_cast<PacketChatMessageCB>(ptr);
			LOG(INFO) << "Message (" << int(packet->Position) << "): " << packet->JsonData.ToPlainText();
			PUSH_EVENT("ChatMessageReceived", std::make_tuple(packet->JsonData, packet->Position));
			break;
		}

		case MultiBlockChange: {
			auto packet = std::static_pointer_cast<PacketMultiBlockChange>(ptr);
			world.ParseChunkData(packet);
			break;
		}

		case ConfirmTransactionCB: {
			auto packet = std::static_pointer_cast<PacketConfirmTransactionCB>(ptr);
			if (packet->WindowId == 0) {
				try {
					playerInventory.ConfirmTransaction(*packet);
				}
				catch (std::exception &e) {
					PUSH_EVENT("Disconnected", std::string("Transaction failed"));
				}
			}
			break;
		}

		case CloseWindowCB:
			break;

		case OpenWindow: {
			auto packet = std::static_pointer_cast<PacketOpenWindow>(ptr);

			LOG(INFO) << "Open new window " << packet->WindowTitle << ": " << packet->WindowId;
			break;
		}

		case WindowItems: {
			auto packet = std::static_pointer_cast<PacketWindowItems>(ptr);
			if (packet->WindowId == 0) {
				playerInventory.WindowId = 0;
				playerInventory.slots = packet->SlotData;
			}
			break;
		}

		case WindowProperty:
			break;

		case SetSlot: {
			auto packet = std::static_pointer_cast<PacketSetSlot>(ptr);
			if (packet->WindowId == 0) {
				playerInventory.slots[packet->Slot] = packet->SlotData;
			}
			break;
		}

		case SetCooldown:
			break;
		case PluginMessageCB:
			break;
		case NamedSoundEffect:
			break;

		case DisconnectPlay: {
			auto packet = std::static_pointer_cast<PacketDisconnectPlay>(ptr);
			LOG(INFO) << "Disconnect reason: " << packet->Reason;
			PUSH_EVENT("Disconnected", packet->Reason);
			break;
		}

		case EntityStatus:
			break;
		case Explosion:
			break;

		case UnloadChunk: {
			auto packet = std::static_pointer_cast<PacketUnloadChunk>(ptr);
			world.ParseChunkData(packet);
			break;
		}

		case ChangeGameState:
			break;

		case KeepAliveCB: {
			LOG(WARNING) << "Receive KeepAlive packet in GameState handler";
			break;
		}

		case ChunkData: {
			auto packet = std::static_pointer_cast<PacketChunkData>(ptr);
			world.ParseChunkData(packet);
			break;
		}

		case Effect:
			break;
		case Particle:
			break;

		case JoinGame: {
			auto packet = std::static_pointer_cast<PacketJoinGame>(ptr);
			Entity entity;
			entity.entityId = packet->EntityId;
			entity.width = 0.6;
			entity.height = 1.8;
			world = World(packet->Dimension);
			world.AddEntity(entity);
			player = world.GetEntityPtr(entity.entityId);

			playerStatus.eid = packet->EntityId;
			gameStatus.gamemode = (packet->Gamemode & 0b11111011);
			gameStatus.dimension = packet->Dimension;
			gameStatus.difficulty = packet->Difficulty;
			gameStatus.maxPlayers = packet->MaxPlayers;
			gameStatus.levelType = packet->LevelType;
			gameStatus.reducedDebugInfo = packet->ReducedDebugInfo;
			LOG(INFO) << "Gamemode is " << gameStatus.gamemode << ", Difficulty is " << (int)gameStatus.difficulty
				<< ", Level Type is " << gameStatus.levelType;
			PUSH_EVENT("PlayerConnected", 0);

			receivedJoinGame = true;

			auto packetSettings = std::make_shared<PacketClientSettings>("en_us", 0x14, 0, true, 0x7F, 1);
			PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packetSettings));

			std::string brandStr("\x08""AltCraft");
			std::vector<unsigned char> brandData;
			std::copy(brandStr.begin(), brandStr.end(), std::back_inserter(brandData));
			auto packetPluginBrand = std::make_shared<PacketPluginMessageSB>("MC|Brand", brandData);
			PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packetPluginBrand));

			break;
		}

		case Map:
			break;

		case EntityRelativeMove: {
			auto packet = std::static_pointer_cast<PacketEntityRelativeMove>(ptr);
			Entity &entity = world.GetEntity(packet->EntityId);
			entity.pos = entity.pos + Entity::DecodeDeltaPos(packet->DeltaX, packet->DeltaY, packet->DeltaZ);
			break;
		}

		case EntityLookAndRelativeMove: {
			auto packet = std::static_pointer_cast<PacketEntityLookAndRelativeMove>(ptr);
			Entity &entity = world.GetEntity(packet->EntityId);
			entity.pos = entity.pos + Entity::DecodeDeltaPos(packet->DeltaX, packet->DeltaY, packet->DeltaZ);
			entity.pitch = packet->Pitch / 256.0;
			entity.yaw = packet->Yaw / 256.0;
			break;
		}

		case EntityLook: {
			auto packet = std::static_pointer_cast<PacketEntityLook>(ptr);
			Entity &entity = world.GetEntity(packet->EntityId);
			entity.pitch = packet->Pitch / 256.0;
			entity.yaw = packet->Yaw / 256.0;
			break;
		}

		case EntityCB:
			break;
		case VehicleMove:
			break;
		case OpenSignEditor:
			break;
		case PlayerAbilitiesCB:
			break;
		case CombatEvent:
			break;
		case PlayerListItem:
			break;

		case PlayerPositionAndLookCB: {
			auto packet = std::static_pointer_cast<PacketPlayerPositionAndLookCB>(ptr);
			if ((packet->Flags & 0x10) != 0) {
				player->pitch += packet->Pitch;
			}
			else {
				player->pitch = packet->Pitch;
			}

			if ((packet->Flags & 0x08) != 0) {
				player->yaw += packet->Yaw;
			}
			else {
				player->yaw = packet->Yaw;
			}

			if ((packet->Flags & 0x01) != 0) {
				player->pos.x += packet->X;
			}
			else {
				player->pos.x = packet->X;
			}

			if ((packet->Flags & 0x02) != 0) {
				player->pos.y += packet->Y;
			}
			else {
				player->pos.y = packet->Y;
			}

			if ((packet->Flags & 0x04) != 0) {
				player->pos.z += packet->Z;
			}
			else {
				player->pos.z = packet->Z;
			}

			PUSH_EVENT("PlayerPosChanged", player->pos);
			LOG(INFO) << "PlayerPos is " << player->pos << "\t\tAngle: " << player->yaw << "," << player->pitch;;

			receivedFirstPlayerPosAndLook = true;

			auto packetResponse = std::make_shared<PacketTeleportConfirm>(packet->TeleportId);
			PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packetResponse));

			break;
		}

		case UseBed:
			break;
		case UnlockRecipes:
			break;

		case DestroyEntities: {
			auto packet = std::static_pointer_cast<PacketDestroyEntities>(ptr);
			for (unsigned int entityId : packet->EntityIds) {
				world.DeleteEntity(entityId);
			}
			break;
		}

		case RemoveEntityEffect:
			break;
		case ResourcePackSend:
			break;
		case Respawn: {
			auto packet = std::static_pointer_cast<PacketRespawn>(ptr);
			Entity entity;
			entity.entityId = player->entityId;
			entity.width = 0.6;
			entity.height = 1.8;
			world = World(packet->Dimension);
			world.AddEntity(entity);
			player = world.GetEntityPtr(entity.entityId);

			gameStatus.gamemode = (packet->Gamemode & 0b11111011);
			gameStatus.dimension = packet->Dimension;
			gameStatus.difficulty = packet->Difficulty;
			gameStatus.levelType = packet->LevelType;
			break;
		}
		case EntityHeadLook:
			break;
		case SelectAdvancementTab:
			break;
		case WorldBorder:
			break;
		case Camera:
			break;
		case HeldItemChangeCB:
			break;
		case DisplayScoreboard:
			break;
		case EntityMetadata:
			break;
		case AttachEntity:
			break;

		case EntityVelocity: {
			auto packet = std::static_pointer_cast<PacketEntityVelocity>(ptr);
			Entity &entity = world.GetEntity(packet->EntityId);
			entity.vel = Entity::DecodeVelocity(packet->VelocityX, packet->VelocityY, packet->VelocityZ);
			break;
		}

		case EntityEquipment:
			break;
		case SetExperience:
			break;

		case UpdateHealth: {
			auto packet = std::static_pointer_cast<PacketUpdateHealth>(ptr);
			playerStatus.health = packet->Health;
			if (playerStatus.health <= 0) {
				LOG(INFO) << "Player is dead. Respawning...";
				auto packetPerformRespawn = std::make_shared<PacketClientStatus>(0);
				PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packetPerformRespawn));
			}
			break;
		}

		case ScoreboardObjective:
			break;
		case SetPassengers:
			break;
		case Teams:
			break;
		case UpdateScore:
			break;

		case SpawnPosition: {
			auto packet = std::static_pointer_cast<PacketSpawnPosition>(ptr);
			gameStatus.spawnPosition = packet->Location;
			LOG(INFO) << "Spawn position is " << gameStatus.spawnPosition;
			break;
		}

		case TimeUpdate: {
			auto packet = std::static_pointer_cast<PacketTimeUpdate>(ptr);
			timeStatus.doDaylightCycle = timeStatus.timeOfDay != packet->TimeOfDay;
			timeStatus.worldAge = packet->WorldAge;
			timeStatus.timeOfDay = packet->TimeOfDay;
			timeStatus.interpolatedTimeOfDay = timeStatus.timeOfDay;
			break;
		}

		case Title:
			break;
		case SoundEffect:
			break;
		case PlayerListHeaderAndFooter:
			break;
		case CollectItem:
			break;

		case EntityTeleport: {
			auto packet = std::static_pointer_cast<PacketEntityTeleport>(ptr);
			Entity &entity = world.GetEntity(packet->EntityId);
			entity.pos = VectorF(packet->X, packet->Y, packet->Z);
			entity.pitch = packet->Pitch / 256.0;
			entity.yaw = packet->Yaw / 256.0;
			break;
		}

		case Advancements:
			break;
		case EntityProperties:
			break;
		case EntityEffect:
			break;
	}

	while (!playerInventory.pendingTransactions.empty()) {
		auto packet = std::make_shared<PacketClickWindow>(playerInventory.pendingTransactions.front());
		playerInventory.pendingTransactions.pop();
		PUSH_EVENT("SendPacket", std::static_pointer_cast<Packet>(packet));
	}
}

void GameState::HandleMovement(GameState::MoveType direction, float deltaTime) {
	if (!gameStatus.isGameStarted)
		return;

	const double playerSpeed = 43;

	float velocity = playerSpeed * deltaTime;

	double playerYaw = Entity::DecodeYaw(player->yaw);
	double playerPitch = Entity::DecodePitch(player->pitch);

	glm::vec3 front, right, worldUp, up;
	worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
	front.x = cos(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));
	front.y = player->isFlying ? sin(glm::radians(playerPitch)) : 0;
	front.z = sin(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));
	front = glm::normalize(front);
	right = glm::normalize(glm::cross(front, worldUp));
	up = glm::normalize(glm::cross(right, front));

	glm::vec3 vel = player->vel.glm();
	switch (direction) {
		case FORWARD: {
			vel += front * velocity;
			break;
		}

		case BACKWARD: {
			vel -= front * velocity;
			break;
		}

		case RIGHT: {
			vel += right * velocity;
			break;
		}

		case LEFT: {
			vel -= right * velocity;
			break;
		}

		case JUMP:
			if (player->onGround && !player->isFlying) {
				vel.y += 10;
				player->onGround = false;
			}
			else
				if (player->isFlying) {
					vel += up * velocity;
				}
			break;
	}
	player->vel = VectorF(vel.x, vel.y, vel.z);
}

void GameState::HandleRotation(double yaw, double pitch) {
	if (!gameStatus.isGameStarted)
		return;

	double playerYaw = Entity::DecodeYaw(player->yaw);
	double playerPitch = Entity::DecodePitch(player->pitch);
	playerYaw += yaw;
	playerPitch += pitch;
	if (playerPitch > 89.0)
		playerPitch = 89.0;
	if (playerPitch < -89.0)
		playerPitch = -89.0;
	player->yaw = Entity::EncodeYaw(playerYaw);
	player->pitch = Entity::EncodePitch(playerPitch);
}

glm::mat4 GameState::GetViewMatrix() {
	double playerYaw = Entity::DecodeYaw(player->yaw);
	double playerPitch = Entity::DecodePitch(player->pitch);
	glm::vec3 front, right, worldUp, up;
	worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
	front.x = cos(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));
	front.y = sin(glm::radians(playerPitch));
	front.z = sin(glm::radians(playerYaw)) * cos(glm::radians(playerPitch));
	front = glm::normalize(front);
	right = glm::normalize(glm::cross(front, worldUp));
	up = glm::normalize(glm::cross(right, front));

	glm::vec3 eyePos = player->pos.glm();
	eyePos += player->EyeOffset.glm();
	return glm::lookAt(eyePos, eyePos + front, up);
}

// TODO: it should actually be something like this:
//    function start_digging():
//        send_packet(packet_type=start_digging_packet)
//        delay(time=selected_block_dig_time, action=finish_digging)
void GameState::StartDigging() {
	if (!selectionStatus.isBlockSelected)
		return;

	auto packetStart = std::make_shared<PacketPlayerDigging>(0, selectionStatus.selectedBlock, 1);
	auto packet = std::static_pointer_cast<Packet>(packetStart);
	PUSH_EVENT("SendPacket", packet);

	FinishDigging();
}

void GameState::FinishDigging() {
	auto packetFinish = std::make_shared<PacketPlayerDigging>(2, selectionStatus.selectedBlock, 1);
	auto packet = std::static_pointer_cast<Packet>(packetFinish);
	PUSH_EVENT("SendPacket", packet);
}

// TODO: it should actually be something like this:
//    function cancel_digging():
//        if finish_digging is in delayed_actions:
//            send_packet(packet_type=start_digging_packet)
//            remove_delayed_action(finish_digging)
void GameState::CancelDigging() {
	auto packetCancel = std::make_shared<PacketPlayerDigging>(1, selectionStatus.selectedBlock, 1);
	auto packet = std::static_pointer_cast<Packet>(packetCancel);
	PUSH_EVENT("SendPacket", packet);
}

BlockFacing detectHitFace(VectorF raycastHit, Vector selectedBlock) {
	auto vec = VectorF(selectedBlock.x + .5, selectedBlock.y + .5, selectedBlock.z + .5) - raycastHit;

	// TODO: move these vectors to Vector.hpp
	static const auto vecUp = VectorF(0, 1, 0);
	static const auto vecRight = VectorF(1, 0, 0);
	static const auto vecForward = VectorF(0, 0, -1);

	const double up = vec.cosBetween(vecUp);
	const double down = -up;
	const double right = vec.cosBetween(vecRight);
	const double left = -right;
	const double forward = vec.cosBetween(vecForward);
	const double backward = -forward;

	const double min_cos = _min(up, down, right, left, forward, backward);
	if (min_cos == down)
		return BlockFacing::Bottom;
	else if (min_cos == up)
		return BlockFacing::Top;
	else if (min_cos == forward)
		return BlockFacing::North;
	else if (min_cos == backward)
		return BlockFacing::South;
	else if (min_cos == left)
		return BlockFacing::West;
	else return BlockFacing::East;
}

void GameState::PlaceBlock() {
	if (!selectionStatus.isBlockSelected)
		return;

	BlockFacing face = detectHitFace(selectionStatus.raycastHit, selectionStatus.selectedBlock);
	auto packetPlace = std::make_shared<PacketPlayerBlockPlacement>(
		selectionStatus.selectedBlock, (unsigned char)face, 0, 0, 0, 0);

	auto packet = std::static_pointer_cast<Packet>(packetPlace);
	PUSH_EVENT("SendPacket", packet);
}