// ---------------------------------------------------------------------------- void GetPeerAddress::asynchronousUpdate() { if (m_request->isDone()) { const XMLNode * result = m_request->getXMLData(); std::string success; if(result->get("success", &success) && success == "yes") { uint32_t ip; result->get("ip", &ip); m_address.setIP(ip); uint16_t port; uint32_t my_ip = NetworkConfig::get()->getMyAddress().getIP(); if (m_address.getIP() == my_ip && !NetworkConfig::m_disable_lan) result->get("private_port", &port); else result->get("port", &port); m_address.setPort(port); Log::debug("GetPeerAddress", "Peer address retrieved."); } else { Log::error("GetPeerAddress", "Failed to get peer address."); } requestTerminate(); delete m_request; m_request = NULL; } } // asynchronousUpdate
/** Detects public IP-address and port by first sending a request to a randomly * selected STUN server and then parsing and validating the response */ void GetPublicAddress::asynchronousUpdate() { // If the user has specified an address, use it instead of the stun protocol. if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0) { NetworkConfig::get()->setMyAddress(m_my_address); m_state = EXITING; requestTerminate(); } //#define LAN_TEST #ifdef LAN_TEST TransportAddress address(0x7f000001, 4); NetworkConfig::get()->setMyAddress(address); m_state = EXITING; requestTerminate(); return; #endif if (m_state == NOTHING_DONE) { createStunRequest(); } if (m_state == STUN_REQUEST_SENT) { std::string message = parseStunResponse(); delete m_transaction_host; if (message != "") { Log::warn("GetPublicAddress", "%s", message.c_str()); m_state = NOTHING_DONE; // try again } else { // The address and the port are known, so the connection can be closed m_state = EXITING; requestTerminate(); } } } // asynchronousUpdate
/** Simple finite state machine. First get the public ip address. Once this * is known, register the server and its address with the stk server so that * client can find it. */ void ServerLobbyRoomProtocol::update() { switch (m_state) { case NONE: // Start the protocol to find the public ip address. m_current_protocol = new GetPublicAddress(this); m_current_protocol->requestStart(); m_state = GETTING_PUBLIC_ADDRESS; // The callback from GetPublicAddress will wake this protocol up requestPause(); break; case GETTING_PUBLIC_ADDRESS: { Log::debug("ServerLobbyRoomProtocol", "Public address known."); // Free GetPublicAddress protocol delete m_current_protocol; // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager // to react to any requests before the server is registered. registerServer(); Log::info("ServerLobbyRoomProtocol", "Server registered."); m_state = ACCEPTING_CLIENTS; } break; case ACCEPTING_CLIENTS: { // Only poll the STK server if this is a WAN server. if(NetworkConfig::get()->isWAN()) checkIncomingConnectionRequests(); if (m_in_race && World::getWorld() && RaceEventManager::getInstance<RaceEventManager>()->isRunning()) { checkRaceFinished(); } break; } case SELECTING_KARTS: break; // Nothing to do, this is event based case DONE: m_state = EXITING; requestTerminate(); break; case EXITING: break; } } // update
// ---------------------------------------------------------------------------- void ConnectToServer::update(int ticks) { switch(m_state.load()) { case GOT_SERVER_ADDRESS: { // Make sure lobby display the quick play server name assert(m_server); NetworkingLobby::getInstance()->setJoinedServer(m_server); break; } case DONE: { // lobby room protocol if we're connected only if (STKHost::get()->getPeerCount() > 0 && STKHost::get()->getServerPeerForClient()->isConnected() && !m_server_address.isUnset()) { // Let main thread create ClientLobby for better // synchronization with GUI NetworkConfig::get()->clearActivePlayersForClient(); auto cl = LobbyProtocol::create<ClientLobby>(m_server_address, m_server); STKHost::get()->startListening(); cl->requestStart(); } if (STKHost::get()->getPeerCount() == 0) { // Shutdown STKHost (go back to online menu too) STKHost::get()->setErrorMessage( _("Cannot connect to server %s.", m_server->getName())); STKHost::get()->requestShutdown(); } requestTerminate(); m_state = EXITING; break; } default: break; } } // update
// ---------------------------------------------------------------------------- void StartGameProtocol::update() { switch(m_state) { case SYNCHRONIZATION_WAIT: { // Wait till the synchronisation protocol is running Protocol *p = ProtocolManager::getInstance() ->getProtocol(PROTOCOL_SYNCHRONIZATION); SynchronizationProtocol* protocol = static_cast<SynchronizationProtocol*>(p); if (protocol) { // Now the synchronization protocol exists. Log::info("StartGameProtocol", "Starting the race loading."); // This will create the world instance, // i.e. load track and karts m_game_setup->getRaceConfig()->setRaceData(); World::getWorld()->setNetworkWorld(true); m_state = LOADING; } break; } case LOADING: { if (m_ready) m_state = READY; break; } case READY: { // set karts into the network game setup STKHost::get()->getGameSetup()->bindKartsToProfiles(); m_state = EXITING; requestTerminate(); break; } } // switch } // update
/** Detects public IP-address and port by first sending a request to a randomly * selected STUN server and then parsing and validating the response */ void GetPublicAddress::asynchronousUpdate() { if (m_state == NOTHING_DONE) { createStunRequest(); } if (m_state == STUN_REQUEST_SENT) { std::string message = parseStunResponse(); delete m_transaction_host; if (message != "") { Log::warn("GetPublicAddress", "%s", message.c_str()); m_state = NOTHING_DONE; // try again } else { // The address and the port are known, so the connection can be closed m_state = EXITING; requestTerminate(); } } } // asynchronousUpdate
/** Simple finite state machine. First get the public ip address. Once this * is known, register the server and its address with the stk server so that * client can find it. */ void ServerLobbyRoomProtocol::update(float dt) { switch (m_state) { case NONE: // Start the protocol to find the public ip address. m_current_protocol = new GetPublicAddress(this); m_current_protocol->requestStart(); m_state = GETTING_PUBLIC_ADDRESS; // The callback from GetPublicAddress will wake this protocol up requestPause(); break; case GETTING_PUBLIC_ADDRESS: { Log::debug("ServerLobbyRoomProtocol", "Public address known."); // Free GetPublicAddress protocol delete m_current_protocol; // Register this server with the STK server. This will block // this thread, but there is no need for the protocol manager // to react to any requests before the server is registered. registerServer(); Log::info("ServerLobbyRoomProtocol", "Server registered."); m_state = ACCEPTING_CLIENTS; } break; case ACCEPTING_CLIENTS: { // Only poll the STK server if this is a WAN server. if(NetworkConfig::get()->isWAN()) checkIncomingConnectionRequests(); break; } case SELECTING: break; // Nothing to do, this is event based case RACING: if (World::getWorld() && RaceEventManager::getInstance<RaceEventManager>()->isRunning()) { checkRaceFinished(); } break; case RESULT_DISPLAY: if(StkTime::getRealTime() > m_timeout) { // Send a notification to all clients to exit // the race result screen NetworkString *exit_result_screen = getNetworkString(1); exit_result_screen->setSynchronous(true); exit_result_screen->addUInt8(LE_EXIT_RESULT); sendMessageToPeersChangingToken(exit_result_screen, /*reliable*/true); delete exit_result_screen; m_state = ACCEPTING_CLIENTS; RaceResultGUI::getInstance()->backToLobby(); // notify the network world that it is stopped RaceEventManager::getInstance()->stop(); // stop race protocols findAndTerminateProtocol(PROTOCOL_CONTROLLER_EVENTS); findAndTerminateProtocol(PROTOCOL_KART_UPDATE); findAndTerminateProtocol(PROTOCOL_GAME_EVENTS); } break; case DONE: m_state = EXITING; requestTerminate(); break; case EXITING: break; } } // update
// ---------------------------------------------------------------------------- void ConnectToServer::asynchronousUpdate() { switch(m_state) { case NONE: { Log::info("ConnectToServer", "Protocol starting"); // This protocol will write the public address of this // instance to STKHost. m_current_protocol = new GetPublicAddress(this); m_current_protocol->requestStart(); // This protocol will be unpaused in the callback from // GetPublicAddress requestPause(); m_state = GETTING_SELF_ADDRESS; break; } case GETTING_SELF_ADDRESS: { delete m_current_protocol; // delete GetPublicAddress m_current_protocol = NULL; registerWithSTKServer(); // Register us with STK server if (m_quick_join) { handleQuickConnect(); // Quick connect will give us the server details, // so we can immediately try to connect to the server m_state = REQUESTING_CONNECTION; } else { m_state = GOT_SERVER_ADDRESS; } } break; case GOT_SERVER_ADDRESS: { assert(!m_quick_join); delete m_current_protocol; m_current_protocol = NULL; Log::info("ConnectToServer", "Server's address known"); // we're in the same lan (same public ip address) !! if (m_server_address.getIP() == NetworkConfig::get()->getMyAddress().getIP()) { Log::info("ConnectToServer", "Server appears to be in the same LAN."); } m_state = REQUESTING_CONNECTION; m_current_protocol = new RequestConnection(m_server_id); m_current_protocol->requestStart(); break; } case REQUESTING_CONNECTION: // In case of a LAN server, m_crrent_protocol is NULL if (!m_current_protocol || m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED) { delete m_current_protocol; m_current_protocol = NULL; // Server knows we want to connect Log::info("ConnectToServer", "Connection request made"); if (m_server_address.getIP() == 0 || m_server_address.getPort() == 0 ) { // server data not correct, hide address and stop m_state = HIDING_ADDRESS; Log::error("ConnectToServer", "Server address is %s", m_server_address.toString().c_str()); m_current_protocol = new HidePublicAddress(); m_current_protocol->requestStart(); return; } if (m_server_address.getIP() == NetworkConfig::get()->getMyAddress().getIP() || NetworkConfig::get()->isLAN()) { // We're in the same lan (same public ip address). // The state will change to CONNECTING handleSameLAN(); } else { m_state = CONNECTING; m_current_protocol = new PingProtocol(m_server_address, 2.0); m_current_protocol->requestStart(); } } break; case CONNECTING: // waiting the server to answer our connection { static double timer = 0; if (StkTime::getRealTime() > timer+5.0) // every 5 seconds { STKHost::get()->connect(m_server_address); timer = StkTime::getRealTime(); Log::info("ConnectToServer", "Trying to connect to %s", m_server_address.toString().c_str()); } break; } case CONNECTED: { Log::info("ConnectToServer", "Connected"); if(m_current_protocol) { // Kill the ping protocol because we're connected m_current_protocol->requestTerminate(); } delete m_current_protocol; m_current_protocol = NULL; // LAN networking does not use the stk server tables. if(NetworkConfig::get()->isWAN()) { m_current_protocol = new HidePublicAddress(); m_current_protocol->requestStart(); } m_state = HIDING_ADDRESS; break; } case HIDING_ADDRESS: // Wait till we have hidden our address if (!m_current_protocol || m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED) { if(m_current_protocol) { delete m_current_protocol; m_current_protocol = NULL; Log::info("ConnectToServer", "Address hidden"); } m_state = DONE; // lobby room protocol if we're connected only if(STKHost::get()->getPeers()[0]->isConnected()) { Protocol *p = new ClientLobbyRoomProtocol(m_server_address); p->requestStart(); } } break; case DONE: requestTerminate(); m_state = EXITING; break; case EXITING: break; } } // asynchronousUpdate
void Enchantment::applyEnchantment(std::shared_ptr<Object> target) { //Invalid target? if( target->isTerminated() || (!target->isAlive() && !_enchantProfile->_target._stay) ) { Log::get().warn("%s:%d: invalid target\n", __FILE__, __LINE__); requestTerminate(); return; } //Already added to a target? if(_target.lock()) { throw std::logic_error("Enchantment::applyEnchantment() - Already applied\n"); } // do retargeting, if necessary // Should it choose an inhand item? if (_enchantProfile->retarget) { // Left, right, or both are valid if (target->getRightHandItem()) { // Only right hand is valid target = target->getRightHandItem(); } else if (target->getLeftHandItem()) { // Pick left hand target = target->getLeftHandItem(); } else { // No weapons to pick, make it fail Log::get().debug("Enchantment::applyEnchantment() - failed because target has no valid items in hand\n"); requestTerminate(); return; } } //Set our target, stored as a weak_ptr _target = target; // Check damage type, 90% damage resistance is enough to resist the enchant if (_enchantProfile->required_damagetype < DAMAGE_COUNT) { if (target->getDamageReduction(_enchantProfile->required_damagetype) >= 0.90f) { Log::get().debug("Enchantment::applyEnchantment() - failed because the target is immune to the enchant.\n"); requestTerminate(); return; } } // Check if target has the required damage type we need if (_enchantProfile->require_damagetarget_damagetype < DAMAGE_COUNT) { if (target->damagetarget_damagetype != _enchantProfile->require_damagetarget_damagetype) { Log::get().warn("%s:%d: application of enchantment failed because the target not have the right damagetarget_damagetype.\n", __FILE__, __LINE__); requestTerminate(); return; } } //modify enchant duration with damage resistance (bad resistance actually *increases* duration!) if ( _lifeTime > 0 && _enchantProfile->required_damagetype < DAMAGE_COUNT && target ) { _lifeTime -= std::ceil(target->getDamageReduction(_enchantProfile->required_damagetype) * _enchantProfile->lifetime); } // Create an overlay character? if (_enchantProfile->spawn_overlay) { std::shared_ptr<Object> overlay = _currentModule->spawnObject(target->getPosition(), _spawnerProfileID, target->team, 0, target->ori.facing_z, "", ObjectRef::Invalid ); if (overlay) { _overlay = overlay; //Kill this character on end... overlay->ai.setTarget(target->getObjRef()); overlay->is_overlay = true; overlay->ai.state = _enchantProfile->spawn_overlay; // ??? WHY DO THIS ??? // Start out with ActionMJ... Object activated int action = overlay->getProfile()->getModel()->getAction(ACTION_MJ); if ( !ACTION_IS_TYPE( action, D ) ) { chr_start_anim( overlay.get(), action, false, true ); } // Assume it's transparent... overlay->setLight(254); overlay->setAlpha(128); } } //Check if this enchant has any set modifiers that conflicts with another enchant _modifiers.remove_if([this, &target](const EnchantModifier &modifier) { //Only set types can conflict if(!Ego::Attribute::isOverrideSetAttribute(modifier._type)) { return false; } //Is there no conflict? if(target->getTempAttributes().find(modifier._type) == target->getTempAttributes().end()) { return false; } //Ok there exist a conflict, so now we have to resolve it somehow //Does this enchant override other enchants? if(getProfile()->_override) { bool conflictResolved = false; //Find the active enchant that conflicts with us for(const std::shared_ptr<Ego::Enchantment> &conflictingEnchant : target->getActiveEnchants()) { conflictingEnchant->_modifiers.remove_if([this, &conflictingEnchant, &modifier, &conflictResolved](const EnchantModifier &otherModifier) { //Is this the one? if(modifier._type == otherModifier._type) { conflictResolved = true; //Remove Enchants that conflict with this one? if(getProfile()->remove_overridden) { conflictingEnchant->requestTerminate(); } return true; } //Nope, keep looking return false; }); //Has it been resolved? if(conflictResolved) { break; } } //We have higher priority than exiting enchants return false; } else { //The existing enchant has higher priority than ours return true; } }); //Now actually apply the values to the target for(const EnchantModifier &modifier : _modifiers) { //These should never occur if(modifier._type == Ego::Attribute::NR_OF_PRIMARY_ATTRIBUTES || modifier._type == Ego::Attribute::NR_OF_ATTRIBUTES) { throw std::logic_error("Enchant.cpp - Invalid enchant type: meta-type as modifier"); } //Morph is special and handled differently than others if(modifier._type == Ego::Attribute::MORPH) { //Store target's original armor target->getTempAttributes()[Ego::Attribute::MORPH] = target->skin; //Transform the object target->polymorphObject(_spawnerProfileID, 0); } //Is it a set type? else if(Ego::Attribute::isOverrideSetAttribute(modifier._type)) { target->getTempAttributes()[modifier._type] = modifier._value; } //It's a cumulative addition else { target->getTempAttributes()[modifier._type] += modifier._value; } } //Finally apply boost values to owner as well std::shared_ptr<Object> owner = _owner.lock(); if(owner != nullptr && !owner->isTerminated()) { owner->getTempAttributes()[Ego::Attribute::MANA_REGEN] += _ownerManaSustain; owner->getTempAttributes()[Ego::Attribute::LIFE_REGEN] += _ownerLifeSustain; } //Insert this enchantment into the Objects list of active enchants target->getActiveEnchants().push_front(shared_from_this()); }
void Enchantment::update() { if(isTerminated()) return; //Have we lost our target? std::shared_ptr<Object> target = _target.lock(); std::shared_ptr<Object> owner = _owner.lock(); if(target == nullptr || target->isTerminated()) { requestTerminate(); return; } //End enchant if owner of the Enchant has died? if(!_enchantProfile->_owner._stay) { if(!owner || !owner->isAlive()) { requestTerminate(); return; } } //End enchant if target of the enchant has died? if(!_enchantProfile->_target._stay && !target->isAlive()) { requestTerminate(); return; } //Spawn particles? if(_spawnParticlesTimer > 0) { _spawnParticlesTimer--; if(_spawnParticlesTimer == 0) { _spawnParticlesTimer = _enchantProfile->contspawn._delay; FACING_T facing = target->ori.facing_z; for (uint8_t i = 0; i < _enchantProfile->contspawn._amount; ++i) { ParticleHandler::get().spawnLocalParticle(target->getPosition(), facing, _spawnerProfileID, _enchantProfile->contspawn._lpip, ObjectRef::Invalid, GRIP_LAST, owner != nullptr ? owner->getTeam().toRef() : static_cast<TEAM_REF>(Team::TEAM_DAMAGE), owner != nullptr ? owner->getObjRef() : ObjectRef::Invalid, ParticleRef::Invalid, i, ObjectRef::Invalid); facing += _enchantProfile->contspawn._facingAdd; } } } //Can we kill the target by draining life? if(target->isAlive()) { if (target->getLife() + _targetLifeDrain < 0.0f) { target->kill(owner, false); } } //Can the owner still sustain us? if (owner && owner->isAlive()) { //Killed by draining life? if(owner->getLife() + _ownerLifeSustain < 0.0f) { owner->kill(target, false); if(_enchantProfile->endIfCannotPay || !_enchantProfile->_target._stay) { requestTerminate(); return; } } //Owner has no longer enough mana to sustain the enchant? if(_enchantProfile->endIfCannotPay) { if(owner->getMana() + _ownerManaSustain < 0.0f) { requestTerminate(); return; } } } //Decrement the lifetimer if(_lifeTime > 0) { if(_lifeTime == 0) { requestTerminate(); } } }