#include "Game.h" #include "AddEntityUpdate.h" #include "AsynchronCall.h" #include "Entity.h" #include "EntityRemovedUpdate.h" #include "ItemEntity.h" #include "JsonUtils.h" #include "MultiblockTree.h" #include "NetworkMessage.h" #include "NoBlock.h" #include "OverworldDimension.h" #include "Player.h" #include "PlayerHand.h" #include "Zeit.h" using namespace Framework; GameClient::GameClient(Player* zPlayer, FCKlient* client) : Thread(), zPlayer(zPlayer), client(client), viewDistance(DEFAULT_VIEW_DISTANCE), first(1), online(1), finished(0), backgroundFinished(0), foregroundFinished(0) { new AsynchronCall("Game Client Updates", [this]() { while (online) { other.lock(); if (updateQueue.hat(0)) { WorldUpdate* update = updateQueue.get(0); updateQueue.remove(0); other.unlock(); background.lock(); this->client->zBackgroundWriter()->schreibe( (char*)&Message::WORLD_UPDATE, 1); update->writeAndCheck(this->client->zBackgroundWriter()); background.unlock(); update->release(); } else { other.unlock(); updateSync.wait(); } } finished = 1; }); start(); } GameClient::~GameClient() { online = 0; updateSync.notify(); emptyForegroundQueueSync.notifyAll(); emptyBackgroundQueueSync.notifyAll(); foregroundQueueSync.notify(); backgroundQueueSync.notify(); while (!finished || !foregroundFinished || !backgroundFinished) 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(); while (!backgroundQueueSync.wait(1000)) { emptyBackgroundQueueSync.notifyAll(); } } } backgroundFinished = 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(); while (!foregroundQueueSync.wait(1000)) { emptyForegroundQueueSync.notifyAll(); } } } foregroundFinished = 1; } void GameClient::sendWorldUpdate(WorldUpdate* update) { bool add = 0; if (zPlayer->getDimensionId() == update->getAffectedDimension()) { auto pos = (Vec3)zPlayer->getPosition(); int dist = update->distanceTo(pos.x, pos.y); if (dist < viewDistance * CHUNK_SIZE) { other.lock(); updateQueue.add(update); other.unlock(); updateSync.notify(); add = 1; } } if (!add) update->release(); } void GameClient::reply() { other.lock(); for (auto req : requests) Game::INSTANCE->api(req, this); requests.leeren(); other.unlock(); if (first) { foreground.lock(); int id = zPlayer->getId(); client->zForegroundWriter()->schreibe( (char*)&Message::POSITION_UPDATE, 1); client->zForegroundWriter()->schreibe((char*)&id, 4); id = zPlayer->getDimensionId(); client->zForegroundWriter()->schreibe((char*)&id, 4); foreground.unlock(); first = 0; } } void GameClient::logout() { online = 0; updateSync.notify(); emptyForegroundQueueSync.notifyAll(); emptyBackgroundQueueSync.notifyAll(); foregroundQueueSync.notify(); backgroundQueueSync.notify(); } void GameClient::addMessage(StreamReader* reader) { short len = 0; reader->lese((char*)&len, 2); InMemoryBuffer* buffer = new InMemoryBuffer(); char* tmp = new char[len]; reader->lese(tmp, len); buffer->schreibe(tmp, len); delete[] tmp; other.lock(); requests.add(buffer); other.unlock(); } bool GameClient::isOnline() const { return online; } void GameClient::sendResponse(NetworkMessage* response) { queueCs.lock(); if (response->isUseBackground()) { if (backgroundQueue.getEintragAnzahl() > 20) { queueCs.unlock(); while (!emptyBackgroundQueueSync.wait(1000)) { backgroundQueueSync.notify(); } queueCs.lock(); } backgroundQueue.add(response); queueCs.unlock(); backgroundQueueSync.notify(); } else { 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(); while (foregroundQueue.getEintragAnzahl() > 0) { foregroundQueueSync.notify(); emptyForegroundQueueSync.wait(100); } m.messungEnde(); std::cout << "WARNING: Game resumed after " << m.getSekunden() << " seconds.\n"; queueCs.lock(); } foregroundQueue.add(response); queueCs.unlock(); foregroundQueueSync.notify(); } } Player* GameClient::zEntity() const { return zPlayer; } void GameClient::sendTypes() { foreground.lock(); int count = 0; for (int i = 0; i < Game::INSTANCE->getBlockTypeCount(); i++) { if (Game::INSTANCE->zBlockType(i)) count++; } client->zForegroundWriter()->schreibe((char*)&count, 4); for (int i = 0; i < Game::INSTANCE->getBlockTypeCount(); i++) { const BlockType* t = Game::INSTANCE->zBlockType(i); if (t) { t->writeTypeInfo(client->zForegroundWriter()); } } count = 0; for (int i = 0; i < Game::INSTANCE->getItemTypeCount(); i++) { if (Game::INSTANCE->zItemType(i)) count++; } client->zForegroundWriter()->schreibe((char*)&count, 4); for (int i = 0; i < Game::INSTANCE->getItemTypeCount(); i++) { const ItemType* t = Game::INSTANCE->zItemType(i); if (t) { int id = t->getId(); client->zForegroundWriter()->schreibe((char*)&id, 4); char len = (char)t->getName().getLength(); client->zForegroundWriter()->schreibe((char*)&len, 1); client->zForegroundWriter()->schreibe(t->getName().getText(), len); short tlen = (short)t->getTooltipUIML().getLength(); client->zForegroundWriter()->schreibe((char*)&tlen, 2); client->zForegroundWriter()->schreibe( t->getTooltipUIML().getText(), tlen); if (t->zModel()) { t->zModel()->writeTo(client->zForegroundWriter()); } else { ModelInfo("", Framework::RCArray(), false, 1.f) .writeTo(client->zForegroundWriter()); } } } count = 0; for (int i = 0; i < Game::INSTANCE->getEntityTypeCount(); i++) { if (Game::INSTANCE->zEntityType(i)) count++; } client->zForegroundWriter()->schreibe((char*)&count, 4); for (int i = 0; i < count; i++) { const EntityType* t = Game::INSTANCE->zEntityType(i); int id = t->getId(); client->zForegroundWriter()->schreibe((char*)&id, 4); if (t->zModel()) { t->zModel()->writeTo(client->zForegroundWriter()); } else { ModelInfo("", Framework::RCArray(), false, 1.f) .writeTo(client->zForegroundWriter()); } } foreground.unlock(); } Game::Game(Framework::Text name, Framework::Text worldsDir) : Thread(), name(name), typeRegistry(new TypeRegistry()), dimensions(new RCArray()), updates(new RCArray()), clients(new RCArray()), questManager(new QuestManager()), ticker(new TickOrganizer()), path((const char*)(worldsDir + "/" + name)), stop(0), tickId(0), nextEntityId(0), generator(0), loader(0), chat(0), playerRegister(new PlayerRegister(path)), uiController(new UIController()), totalTickTime(0), tickCounter(0), averageTickTime(0), ticksPerSecond(0), totalTime(0), blockTypes(0), blockTypeCount(0), itemTypes(0), itemTypeCount(0), entityTypes(0), entityTypeCount(0), multiblockStructureTypes(0), multiblockStructureTypeCount(0) { if (!DateiExistiert(path)) DateiPfadErstellen(path + "/"); Datei d; d.setDatei(path + "/eid"); if (d.existiert()) { d.open(Datei::Style::lesen); d.lese((char*)&nextEntityId, 4); d.close(); } start(); } Game::~Game() { dimensions->release(); updates->release(); clients->release(); generator->release(); loader->release(); chat->release(); playerRegister->release(); typeRegistry->release(); uiController->release(); for (int i = 0; i < blockTypeCount; i++) { if (blockTypes[i]) blockTypes[i]->release(); } delete[] blockTypes; for (int i = 0; i < itemTypeCount; i++) { if (itemTypes[i]) itemTypes[i]->release(); } delete[] itemTypes; for (int i = 0; i < entityTypeCount; i++) { if (entityTypes[i]) entityTypes[i]->release(); } delete[] entityTypes; for (int i = 0; i < multiblockStructureTypeCount; i++) { if (multiblockStructureTypes[i]) multiblockStructureTypes[i]->release(); } delete[] multiblockStructureTypes; } void Game::initialize() { // TODO load mods libraries // load block types std::cout << "Loading block types\n"; Framework::Array blockTypeArray; Framework::JSON::Validator::JSONValidator* validator = Framework::JSON::Validator::JSONValidator::buildForArray() ->addAcceptedTypeInArray(typeRegistry->getValidator()) ->removeInvalidEntries() ->finishArray(); loadAllJsonsFromDirectory("data/blocks", [this, &blockTypeArray, validator]( Framework::JSON::JSONValue* zValue, Framework::Text path) { Framework::RCArray validationResults; Framework::JSON::JSONValue* validParts = validator->getValidParts(zValue, &validationResults); for (Framework::JSON::Validator::JSONValidationResult* result : validationResults) { result->printInvalidInfo(); } if (validParts) { for (Framework::JSON::JSONValue* value : *validParts->asArray()) { BlockType* blockType = typeRegistry->fromJson(value); if (blockType) { blockTypeArray.add(blockType); } } validParts->release(); } }); validator->release(); std::cout << "Loaded " << blockTypeArray.getEintragAnzahl() << " block types from data/blocks\n"; blockTypes = new BlockType*[2 + blockTypeArray.getEintragAnzahl()]; blockTypes[0] = new NoBlockBlockType(&NoBlock::INSTANCE, "__not_yet_generated"); blockTypes[1] = new NoBlockBlockType(&AirBlock::INSTANCE, "Air"); blockTypeCount = 2; for (BlockType* blockType : blockTypeArray) { blockTypes[blockTypeCount++] = blockType; } for (int i = 0; i < blockTypeCount; i++) { blockTypes[i]->setTypeId(i); } std::cout << "Loading item types\n"; Framework::Array itemTypeArray; validator = Framework::JSON::Validator::JSONValidator::buildForArray() ->addAcceptedTypeInArray(typeRegistry->getValidator()) ->removeInvalidEntries() ->finishArray(); loadAllJsonsFromDirectory("data/items", [this, &itemTypeArray, validator]( Framework::JSON::JSONValue* zValue, Framework::Text path) { Framework::RCArray validationResults; Framework::JSON::JSONValue* validParts = validator->getValidParts(zValue, &validationResults); for (Framework::JSON::Validator::JSONValidationResult* result : validationResults) { result->printInvalidInfo(); } if (validParts) { for (Framework::JSON::JSONValue* value : *validParts->asArray()) { ItemType* itemType = typeRegistry->fromJson(value); if (itemType) { itemTypeArray.add(itemType); } } validParts->release(); } }); validator->release(); std::cout << "Loaded " << itemTypeArray.getEintragAnzahl() << " item types from data/items\n"; itemTypes = new ItemType*[blockTypeCount + itemTypeArray.getEintragAnzahl()]; itemTypes[0] = new PlayerHandItemType(); itemTypeCount = 1; for (int i = 0; i < blockTypeCount; i++) { ItemType* itemType = blockTypes[i]->createItemType(); if (itemType) { itemTypes[itemTypeCount++] = itemType; } } for (ItemType* itemType : itemTypeArray) { itemTypes[itemTypeCount++] = itemType; } for (int i = 0; i < itemTypeCount; i++) { itemTypes[i]->setTypeId(i); } std::cout << "Loading entity types\n"; Framework::Array entityTypeArray; /* validator = Framework::JSON::Validator::JSONValidator::buildForArray() ->addAcceptedTypeInArray(typeRegistry->getValidator()) ->removeInvalidEntries() ->finishArray(); loadAllJsonsFromDirectory("data/entities", [this, &entityTypeArray, validator]( Framework::JSON::JSONValue* zValue, Framework::Text path) { Framework::RCArray validationResults; Framework::JSON::JSONValue* validParts = validator->getValidParts(zValue, &validationResults); for (Framework::JSON::Validator::JSONValidationResult* result : validationResults) { result->printInvalidInfo(); } if (validParts) { for (Framework::JSON::JSONValue* value : *validParts->asArray()) { EntityType* entityType = typeRegistry->fromJson(value); if (entityType) { entityTypeArray.add(entityType); } } validParts->release(); } }); validator->release();*/ std::cout << "Loaded " << entityTypeArray.getEintragAnzahl() << " entity types from data/entities\n"; entityTypes = new EntityType*[2 + entityTypeArray.getEintragAnzahl()]; entityTypes[0] = new PlayerEntityType(); entityTypes[1] = new ItemEntityType(); entityTypeCount = 2; for (EntityType* entityType : entityTypeArray) { entityTypes[entityTypeCount++] = entityType; } for (int i = 0; i < entityTypeCount; i++) { entityTypes[i]->setTypeId(i); } // initialize loaded types bool allInitialized = false; while (!allInitialized) { allInitialized = true; for (int i = 0; i < blockTypeCount; i++) { if (blockTypes[i] && !blockTypes[i]->initialize(this)) { std::cout << "ERROR: Could not initialize Block Type '" << blockTypes[i]->getName() << "'.\n"; blockTypes[i]->release(); blockTypes[i] = 0; allInitialized = false; } } } allInitialized = false; while (!allInitialized) { allInitialized = true; for (int i = 0; i < itemTypeCount; i++) { if (itemTypes[i] && !itemTypes[i]->initialize(this)) { std::cout << "ERROR: Could not initialize Item Type '" << itemTypes[i]->getName() << "'.\n"; itemTypes[i]->release(); itemTypes[i] = 0; allInitialized = false; } } } allInitialized = false; while (!allInitialized) { allInitialized = true; for (int i = 0; i < entityTypeCount; i++) { if (entityTypes[i] && !entityTypes[i]->initialize(this)) { std::cout << "ERROR: Could not initialize Entity Type '" << entityTypes[i]->getName() << "'.\n"; entityTypes[i]->release(); entityTypes[i] = 0; allInitialized = false; } } } for (int i = 0; i < blockTypeCount; i++) { if (blockTypes[i]) { blockTypes[i]->initializeDefault(); } } multiblockStructureTypes = new MultiblockStructureType*[1]; multiblockStructureTypes[0] = new MultiblockTreeStructureType(); multiblockStructureTypeCount = 1; // initialize world generator and world loader int seed = 0; int index = 0; for (const char* n = name; *n; n++) seed += (int)pow((float)*n * 31, (float)++index); generator = new WorldGenerator(seed); loader = new WorldLoader(); // load recipies recipies.loadRecipies("data/recipies"); // initialize chat chat = new Chat(); // load quests questManager->loadQuests(); } void Game::thread() { ZeitMesser waitForLock; ZeitMesser removeOldClients; ZeitMesser tickEntities; ZeitMesser worldUpdates; ZeitMesser clientReply; ZeitMesser removeOldChunks; ZeitMesser m; ZeitMesser total; total.messungStart(); double tickTime = 0; double sleepTime = 0; int nextTimeSync = MAX_TICKS_PER_SECOND; while (!stop) { m.messungStart(); ticker->nextTick(); actionsCs.lock(); while (actions.getEintragAnzahl() > 0) { actions.get(0)(); actions.remove(0); } actionsCs.unlock(); Array removed; double waitTotal = 0; waitForLock.messungStart(); cs.lock(); waitForLock.messungEnde(); waitTotal += waitForLock.getSekunden(); removeOldClients.messungStart(); int index = 0; nextTimeSync--; for (auto player : *clients) { if (!player->isOnline()) { uiController->removePlayerDialogs(player->zEntity()->getId()); chat->removeObserver(player->zEntity()->getId()); chat->broadcastMessage( Framework::Text(player->zEntity()->getName()) + " left the game.", Chat::CHANNEL_INFO); Datei pFile; pFile.setDatei(path + "/player/" + getPlayerId(player->zEntity()->getName())); pFile.erstellen(); if (pFile.open(Datei::Style::schreiben)) zEntityType(EntityTypeEnum::PLAYER) ->saveEntity(player->zEntity(), &pFile); pFile.close(); removed.add(index, 0); Dimension* dim = zDimension(player->zEntity()->getDimensionId()); dim->removeSubscriptions(player->zEntity()); this->requestWorldUpdate( new EntityRemovedUpdate(player->zEntity()->getId(), player->zEntity()->getDimensionId(), player->zEntity()->getPosition())); } else { if (nextTimeSync <= 0 && player->zEntity()) { Dimension* zDim = zDimension(player->zEntity()->getDimensionId()); if (zDim) { NetworkMessage* msg = new NetworkMessage(); msg->syncTime(zDim->getCurrentDayTime(), zDim->getNightDuration(), zDim->getNightTransitionDuration(), zDim->getDayDuration()); player->sendResponse(msg); } } } index++; } if (nextTimeSync <= 0) { nextTimeSync = MAX_TICKS_PER_SECOND; } for (auto i : removed) clients->remove(i); removeOldClients.messungEnde(); cs.unlock(); tickEntities.messungStart(); for (auto dim : *dimensions) dim->tickEntities(); tickEntities.messungEnde(); waitForLock.messungStart(); cs.lock(); waitForLock.messungEnde(); waitTotal += waitForLock.getSekunden(); worldUpdates.messungStart(); while (updates->hat(0)) { WorldUpdate* update = updates->z(0); for (auto client : *clients) client->sendWorldUpdate( dynamic_cast(update->getThis())); if (!zDimension(update->getAffectedDimension())) { Dimension* dim = typeRegistry->createDimension( update->getAffectedDimension()); if (dim) addDimension(dim); else { std::cout << "ERROR: could not create dimension " << update->getAffectedDimension() << ". No Factory was provided.\n"; } } if (zDimension(update->getAffectedDimension())) update->onUpdate(zDimension(update->getAffectedDimension())); updates->remove(0); } worldUpdates.messungEnde(); cs.unlock(); clientReply.messungStart(); for (auto client : *clients) client->reply(); clientReply.messungEnde(); waitForLock.messungStart(); cs.lock(); waitForLock.messungEnde(); waitTotal += waitForLock.getSekunden(); removeOldChunks.messungStart(); for (auto dim : *dimensions) dim->removeOldChunks(); removeOldChunks.messungEnde(); cs.unlock(); m.messungEnde(); double sec = m.getSekunden(); tickCounter++; totalTickTime += sec; sleepTime += 1.0 / MAX_TICKS_PER_SECOND - tickTime; if (sleepTime > 0) { Sleep((int)(sleepTime * 1000)); } total.messungEnde(); total.messungStart(); tickTime = total.getSekunden(); totalTime += tickTime; if (totalTime >= 1) { averageTickTime = totalTickTime / tickCounter; ticksPerSecond = tickCounter; totalTickTime = 0; tickCounter = 0; totalTime = 0; std::cout << std::flush; // update info in console } else if (sec > 1) { std::cout << "WARNING: tick needed " << sec << " seconds. The game will run sower then normal.\n"; std::cout << "waiting: " << waitTotal << "\nremoveOldClients: " << removeOldClients.getSekunden() << "\ntickEntities:" << tickEntities.getSekunden() << "\nworldUpdates: " << worldUpdates.getSekunden() << "\nclientReply: " << clientReply.getSekunden() << "\nremoveOldChunks:" << removeOldChunks.getSekunden() << "\n"; } } save(); generator->exitAndWait(); loader->exitAndWait(); ticker->exitAndWait(); for (Dimension* dim : *dimensions) dim->requestStopAndWait(); std::cout << "Game thread exited\n"; } void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin) { char type; zRequest->lese(&type, 1); NetworkMessage* response = new NetworkMessage(); switch (type) { case 1: // world { Dimension* dim = zDimension(zOrigin->zEntity()->getDimensionId()); if (!dim) { dim = typeRegistry->createDimension( zOrigin->zEntity()->getDimensionId()); if (!dim) { std::cout << "ERROR: could not create dimension " << zOrigin->zEntity()->getDimensionId() << ". No Factory was provided.\n"; return; } addDimension(dim); } dim->api(zRequest, response, zOrigin->zEntity()); break; } case 2: // player zOrigin->zEntity()->playerApi(zRequest, response); break; case 3: // entity { int id; zRequest->lese((char*)&id, 4); for (Dimension* dim : *dimensions) { Entity* entity = dim->zEntity(id); if (entity) { entity->api(zRequest, response, zOrigin->zEntity()); break; } } break; } case 4: { // inventory bool isEntity; zRequest->lese((char*)&isEntity, 1); Inventory* target; if (isEntity) { int id; zRequest->lese((char*)&id, 4); target = zEntity(id); } else { int dim; Vec3 pos; zRequest->lese((char*)&dim, 4); zRequest->lese((char*)&pos.x, 4); zRequest->lese((char*)&pos.y, 4); zRequest->lese((char*)&pos.z, 4); target = zBlockAt(pos, dim); } if (target) target->inventoryApi(zRequest, response, zOrigin->zEntity()); break; } case 5: { // crafting uiml request int id; zRequest->lese((char*)&id, 4); Text uiml = recipies.getCrafingUIML(id); Text dialogId = "crafting_"; dialogId += id; uiController->addDialog(new UIDialog(dialogId, zOrigin->zEntity()->getId(), new Framework::XML::Element(uiml))); break; } case 6: { // chat message chat->chatApi(zRequest, zOrigin->zEntity(), response); break; } case 7: // other dimension { int dimensionId; zRequest->lese((char*)&dimensionId, 4); Dimension* dim = zDimension(dimensionId); if (dim) { dim->api(zRequest, response, zOrigin->zEntity()); } break; } case 8: // ui message { uiController->api(zRequest, response, zOrigin->zEntity()); break; } default: std::cout << "received unknown api request in game with type " << (int)type << "\n"; } if (!response->isEmpty()) { if (response->isBroadcast()) broadcastMessage(response); else zOrigin->sendResponse(response); } else { response->release(); } } void Game::updateLightning(int dimensionId, Vec3 location) { Dimension* zDim = zDimension(dimensionId); if (zDim) zDim->updateLightning(location); } void Game::updateLightningWithoutWait(int dimensionId, Vec3 location) { Dimension* zDim = zDimension(dimensionId); if (zDim) zDim->updateLightningWithoutWait(location); } void Game::broadcastMessage(NetworkMessage* response) { for (auto client : *clients) client->sendResponse( dynamic_cast(response->getThis())); response->release(); } void Game::sendMessage(NetworkMessage* response, Entity* zTargetPlayer) { for (auto client : *clients) { if (client->zEntity()->getId() == zTargetPlayer->getId()) { client->sendResponse(response); return; } } response->release(); } bool Game::requestWorldUpdate(WorldUpdate* update) { cs.lock(); updates->add(update); cs.unlock(); return 1; } bool Game::checkPlayer(Framework::Text name, Framework::Text secret) { if (playerRegister->checkSecret(name, secret)) return 1; else { std::cout << "player " << name.getText() << " tryed to connect with an invalid secret.\n"; return 0; } } bool Game::existsPlayer(Framework::Text name) { return playerRegister->hasPlayer(name); } Framework::Text Game::createPlayer(Framework::Text name) { return playerRegister->addPlayer(name); } GameClient* Game::addPlayer(FCKlient* client, Framework::Text name) { cs.lock(); int id = playerRegister->getPlayerId(name); Datei pFile; pFile.setDatei(path + "/player/" + id); Player* player; bool isNew = 0; if (!pFile.existiert() || !pFile.open(Datei::Style::lesen)) { player = (Player*)zEntityType(EntityTypeEnum::PLAYER) ->createEntityAt( Vec3(0.5, 0.5, 0), DimensionEnum::OVERWORLD); player->setName(name); isNew = 1; } else { player = (Player*)zEntityType(EntityTypeEnum::PLAYER)->loadEntity(&pFile); pFile.close(); } if (player->getId() >= nextEntityId) { nextEntityId = player->getId() + 1; } GameClient* gameClient = new GameClient(player, client); gameClient->sendTypes(); clients->add(gameClient); if (!zDimension(player->getDimensionId())) { Dimension* dim = typeRegistry->createDimension(player->getDimensionId()); if (!dim) { std::cout << "ERROR: could not create dimension " << (int)player->getDimensionId() << ". No Factory was provided.\n"; return 0; } NetworkMessage* msg = new NetworkMessage(); msg->syncTime(dim->getCurrentDayTime(), dim->getNightDuration(), dim->getNightTransitionDuration(), dim->getDayDuration()); gameClient->sendResponse(msg); this->addDimension(dim); } // subscribe the new player as an observer of the new chunk Dimension* dim = zDimension(player->getDimensionId()); InMemoryBuffer* buffer = new InMemoryBuffer(); buffer->schreibe("\0", 1); Punkt center = getChunkCenter( (int)player->getPosition().x, (int)player->getPosition().y); buffer->schreibe((char*)¢er.x, 4); buffer->schreibe((char*)¢er.y, 4); buffer->schreibe("\0", 1); dim->api(buffer, 0, player); buffer->release(); while (isNew && !dim->zChunk(getChunkCenter( (int)player->getPosition().x, (int)player->getPosition().y))) { cs.unlock(); Sleep(1000); cs.lock(); } if (isNew) { Either b = BlockTypeEnum::AIR; int h = WORLD_HEIGHT; while (((b.isA() && (!(Block*)b || ((Block*)b)->isPassable())) || (b.isB() && zBlockType(b)->zDefault()->isPassable())) && h > 0) b = zBlockAt({(int)player->getPosition().x, (int)player->getPosition().y, --h}, player->getDimensionId()); player->setPosition( {player->getPosition().x, player->getPosition().y, (float)h + 1.f}); } requestWorldUpdate(new AddEntityUpdate(player, player->getDimensionId())); chat->addObserver(gameClient->zEntity()->getId()); chat->broadcastMessage(name + " joined the game.", Chat::CHANNEL_INFO); cs.unlock(); return dynamic_cast(gameClient->getThis()); } bool Game::isChunkLoaded(int x, int y, int dimension) const { Dimension* dim = zDimension(dimension); return (dim && dim->hasChunck(x, y)); } bool Game::doesChunkExist(int x, int y, int dimension) { cs.lock(); bool result = isChunkLoaded(x, y, dimension) || loader->existsChunk(x, y, dimension); cs.unlock(); return result; } void Game::blockTargetChanged(Block* zBlock) { for (GameClient* client : *this->clients) { if (client->zEntity()->zTarget() && client->zEntity()->zTarget()->isBlock( zBlock->getPos(), NO_DIRECTION)) { client->zEntity()->onTargetChange(); } } } void Game::entityTargetChanged(Entity* zEntity) { for (GameClient* client : *this->clients) { if (client->zEntity()->zTarget() && client->zEntity()->zTarget()->isEntity(zEntity->getId())) { client->zEntity()->onTargetChange(); } } } void Game::spawnItem( Framework::Vec3 location, int dimensionId, Item* stack) { spawnItem(location, dimensionId, new ItemStack(stack, 1)); } void Game::spawnItem( Framework::Vec3 location, int dimensionId, ItemStack* stack) { ItemEntity* itemEntity = (ItemEntity*)zEntityType(EntityTypeEnum::ITEM) ->createEntity( location, dimensionId, Game::INSTANCE->getNextEntityId()); itemEntity->unsaveAddItem(stack, NO_DIRECTION, 0); stack->release(); requestWorldUpdate(new AddEntityUpdate(itemEntity, dimensionId)); } Framework::Either Game::zBlockAt( Framework::Vec3 location, int dimension) const { Dimension* dim = zDimension(dimension); if (dim) return dim->zBlock(location); return 0; } Block* Game::zRealBlockInstance(Framework::Vec3 location, int dimension) { Dimension* dim = zDimension(dimension); if (dim) return dim->zRealBlockInstance(location); return 0; } int Game::getBlockType(Framework::Vec3 location, int dimension) { Dimension* dim = zDimension(dimension); if (dim) return dim->getBlockType(location); return 0; } Dimension* Game::zDimension(int id) const { for (auto dim : *dimensions) { if (dim->getDimensionId() == id) return dim; } return 0; } Framework::Punkt Game::getChunkCenter(int x, int y) { return Punkt(((x < 0 ? x + 1 : x) / CHUNK_SIZE) * CHUNK_SIZE + (x < 0 ? -CHUNK_SIZE : CHUNK_SIZE) / 2, ((y < 0 ? y + 1 : y) / CHUNK_SIZE) * CHUNK_SIZE + (y < 0 ? -CHUNK_SIZE : CHUNK_SIZE) / 2); } Area Game::getChunckArea(Punkt center) const { return {center.x - CHUNK_SIZE / 2, center.y - CHUNK_SIZE / 2, center.x + CHUNK_SIZE / 2 - 1, center.y + CHUNK_SIZE / 2 - 1, 0}; } Framework::Text Game::getWorldDirectory() const { return path; } void Game::requestArea(Area area) { generator->requestGeneration(area); loader->requestLoading(area); } void Game::save() const { questManager->saveQuests(); Datei d; d.setDatei(path + "/eid"); d.open(Datei::Style::schreiben); d.schreibe((char*)&nextEntityId, 4); d.close(); playerRegister->save(); for (auto dim : *dimensions) dim->save(path); chat->save(); std::cout << "Game was saved\n"; } void Game::requestStop() { stop = 1; warteAufThread(1000000); } void Game::addDimension(Dimension* d) { dimensions->add(d); } int Game::getNextEntityId() { cs.lock(); int result = nextEntityId++; cs.unlock(); return result; } WorldGenerator* Game::zGenerator() const { return generator; } Game* Game::INSTANCE = 0; void Game::initialize(Framework::Text name, Framework::Text worldsDir) { if (!Game::INSTANCE) { Game::INSTANCE = new Game(name, worldsDir); Game::INSTANCE->initialize(); } } Entity* Game::zEntity(int id, int dimensionId) const { Dimension* d = zDimension(dimensionId); if (d) return d->zEntity(id); return 0; } Entity* Game::zEntity(int id) const { for (Dimension* d : *dimensions) { Entity* e = d->zEntity(id); if (e) return e; } // for new players that are currently loading for (GameClient* client : *clients) { if (client->zEntity()->getId() == id) { return client->zEntity(); } } return 0; } Entity* Game::zNearestEntity(int dimensionId, Framework::Vec3 pos, std::function filter) { Dimension* d = zDimension(dimensionId); if (!d) return 0; return d->zNearestEntity(pos, filter); } const RecipieLoader& Game::getRecipies() const { return recipies; } void Game::doLater(std::function action) { actionsCs.lock(); actions.add(action); actionsCs.unlock(); } TickOrganizer* Game::zTickOrganizer() const { return ticker; } Chat* Game::zChat() const { return chat; } Player* Game::zPlayerByName(const char* name) const { for (GameClient* client : *clients) { if (strcmp(client->zEntity()->getName(), name) == 0) { return client->zEntity(); } } return 0; } TypeRegistry* Game::zTypeRegistry() const { return typeRegistry; } int Game::getPlayerId(const char* name) const { return playerRegister->getPlayerId(name); } QuestManager* Game::zQuestManager() const { return questManager; } UIController* Game::zUIController() const { return uiController; } double Game::getAverageTickTime() const { return averageTickTime; } int Game::getTicksPerSecond() const { return ticksPerSecond; } int Game::getPlayerCount() const { return clients->getEintragAnzahl(); } int Game::getChunkCount() const { int result = 0; for (Dimension* dim : *dimensions) { result += dim->getChunkCount(); } return result; } const BlockType* Game::zBlockType(int id) const { return blockTypes[id]; } const ItemType* Game::zItemType(int id) const { return itemTypes[id]; } const EntityType* Game::zEntityType(int id) const { return entityTypes[id]; } int Game::getBlockTypeId(const char* name) const { for (int i = 0; i < blockTypeCount; i++) { if (blockTypes[i] && Framework::Text(blockTypes[i]->getName()).istGleich(name)) { return i; } } std::cout << "WARNING: no block type with name '" << name << "' found.\n"; return -1; } int Game::getItemTypeId(const char* name) const { for (int i = 0; i < itemTypeCount; i++) { if (itemTypes[i] && Framework::Text(itemTypes[i]->getName()).istGleich(name)) { return i; } } std::cout << "WARNING: no item type with name '" << name << "' found.\n"; return -1; } int Game::getBlockTypeCount() const { return blockTypeCount; } int Game::getItemTypeCount() const { return itemTypeCount; } int Game::getEntityTypeCount() const { return entityTypeCount; } const MultiblockStructureType* Game::zMultiblockStructureType(int id) const { return multiblockStructureTypes[id]; } int Game::getMultiblockStructureTypeCount() const { return multiblockStructureTypeCount; }