diff --git a/src/guiengine/widgets/CGUISTKListBox.cpp b/src/guiengine/widgets/CGUISTKListBox.cpp index 363421926..6ff1cf2f2 100644 --- a/src/guiengine/widgets/CGUISTKListBox.cpp +++ b/src/guiengine/widgets/CGUISTKListBox.cpp @@ -426,8 +426,11 @@ void CGUISTKListBox::selectNew(s32 ypos, bool onlyHover) s32 oldSelected = Selected; Selected = getItemAt(AbsoluteRect.UpperLeftCorner.X, ypos); - if (Selected < 0 && !Items.empty()) - Selected = 0; + if (Selected == -1 || Items.empty()) + { + Selected = -1; + return; + } recalculateScrollPos(); diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 852d06f2a..a019d2f56 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -235,9 +235,18 @@ unsigned int ItemManager::insertItem(Item *item) m_all_items[index] = item; } item->setItemId(index); - + insertItemInQuad(item); // Now insert into the appropriate quad list, if there is a quad list // (i.e. race mode has a quad graph). + return index; +} // insertItem + +//----------------------------------------------------------------------------- +/** Insert into the appropriate quad list, if there is a quad list + * (i.e. race mode has a quad graph). + */ +void ItemManager::insertItemInQuad(Item *item) +{ if(m_items_in_quads) { int graph_node = item->getGraphNode(); @@ -249,8 +258,7 @@ unsigned int ItemManager::insertItem(Item *item) else // otherwise store it in the 'outside' index (*m_items_in_quads)[m_items_in_quads->size()-1].push_back(item); } // if m_items_in_quads - return index; -} // insertItem +} // insertItemInQuad //----------------------------------------------------------------------------- /** Creates a new item at the location of the kart (e.g. kart drops a @@ -303,7 +311,10 @@ Item* ItemManager::dropNewItem(ItemState::ItemType type, Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type], m_item_lowres_mesh[mesh_type], /*prev_owner*/kart); - insertItem(item); + + // restoreState in NetworkItemManager will handle the insert item + if (!server_xyz) + insertItem(item); if(m_switch_ticks>=0) { ItemState::ItemType new_type = m_switch_to[item->getType()]; @@ -522,6 +533,18 @@ void ItemManager::updateGraphics(float dt) void ItemManager::deleteItem(ItemState *item) { // First check if the item needs to be removed from the items-in-quad list + deleteItemInQuad(item); + int index = item->getItemId(); + m_all_items[index] = NULL; + delete item; +} // delete item + +//----------------------------------------------------------------------------- +/** Removes an items from the items-in-quad list only + * \param The item to delete. + */ +void ItemManager::deleteItemInQuad(ItemState* item) +{ if(m_items_in_quads) { int sector = item->getGraphNode(); @@ -533,11 +556,7 @@ void ItemManager::deleteItem(ItemState *item) assert(it!=items.end()); items.erase(it); } // if m_items_in_quads - - int index = item->getItemId(); - m_all_items[index] = NULL; - delete item; -} // delete item +} // deleteItemInQuad //----------------------------------------------------------------------------- /** Switches all items: boxes become bananas and vice versa for a certain diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index b3896b256..2732d2019 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -125,7 +125,8 @@ protected: virtual unsigned int insertItem(Item *item); void switchItemsInternal(std::vector < ItemState*> &all_items); void setSwitchItems(const std::vector &switch_items); - + void insertItemInQuad(Item *item); + void deleteItemInQuad(ItemState *item); ItemManager(); public: virtual ~ItemManager(); diff --git a/src/items/network_item_manager.cpp b/src/items/network_item_manager.cpp index e22f6573f..0b47bc5ab 100644 --- a/src/items/network_item_manager.cpp +++ b/src/items/network_item_manager.cpp @@ -492,12 +492,15 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count) // the client, but not yet confirmed). So unsigned int max_index = std::max(m_confirmed_state.size(), m_all_items.size() ); + m_all_items.resize(max_index, NULL); for(unsigned int i=0; igetNormal(); Item *item_new = dropNewItem(is->getType(), is->getPreviousOwner(), &xyz, &normal ); - if (i != item_new->getItemId()) - { - // The newly created item on the client has been given a - // different index than the one given by the server. This - // indicates that this client has either a different item - // at the index that the server sent (i.e. client predicted - // an item, which the server has not confirmed), or the - // client found an empty slot that the server did not have - // (so an earlier index was found). - if(i < m_all_items.size() && m_all_items[i]) - deleteItem(m_all_items[i]); - // Move item_new from its position to the index given - // by the server - m_all_items[item_new->getItemId()] = NULL; - m_all_items[i] = item_new; - item_new->setItemId(i); - } - item_new->setDeactivatedTicks(is->getDeactivatedTicks()); - *((ItemState*)m_all_items[i]) = *is; + *((ItemState*)item_new) = *is; + m_all_items[i] = item_new; + insertItemInQuad(item_new); } else if (!is && item) { - deleteItem(m_all_items[i]); + deleteItemInQuad(item); + delete item; + m_all_items[i] = NULL; } } // for i < max_index + // Clean up the rest + m_all_items.resize(m_confirmed_state.size()); // Now set the clock back to the 'rewindto' time: world->setTicksForRewind(rewind_to_time); diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 21786f1a7..9e05eab26 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -33,7 +33,9 @@ #include "physics/physics.hpp" #include "network/network_config.hpp" #include "network/network_string.hpp" +#include "network/protocols/game_events_protocol.hpp" #include "network/server_config.hpp" +#include "network/stk_host.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/check_manager.hpp" @@ -1156,3 +1158,66 @@ void LinearWorld::restoreCompleteState(const BareNetworkString& b) for (unsigned i = 0; i < cc; i++) CheckManager::get()->getCheckStructure(i)->restoreCompleteState(b); } // restoreCompleteState + +// ---------------------------------------------------------------------------- +/** Called in server whenever a kart cross a check line, it send server + * all karts lap count, last triggered checkline and check structure status + * to all players in game (including spectators so that the lap count is + * correct) + */ +void LinearWorld::updateCheckLinesServer() +{ + if (!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isClient()) + return; + + NetworkString cl(PROTOCOL_GAME_EVENTS); + cl.setSynchronous(true); + cl.addUInt8(GameEventsProtocol::GE_CHECK_LINE); + + for (KartInfo& ki : m_kart_info) + { + int8_t finished_laps = (int8_t)ki.m_finished_laps; + cl.addUInt8(finished_laps); + } + + for (TrackSector* ts : m_kart_track_sector) + { + int8_t ltcl = (int8_t)ts->getLastTriggeredCheckline(); + cl.addUInt8(ltcl); + } + + const uint8_t cc = (uint8_t)CheckManager::get()->getCheckStructureCount(); + cl.addUInt8(cc); + for (unsigned i = 0; i < cc; i++) + CheckManager::get()->getCheckStructure(i)->saveIsActive(&cl); + + STKHost::get()->sendPacketToAllPeers(&cl, true); +} // updateCheckLinesServer + +// ---------------------------------------------------------------------------- +/* Synchronize with server from the above data. */ +void LinearWorld::updateCheckLinesClient(const BareNetworkString& b) +{ + for (KartInfo& ki : m_kart_info) + { + int8_t finished_laps = b.getUInt8(); + ki.m_finished_laps = finished_laps; + } + + for (TrackSector* ts : m_kart_track_sector) + { + int8_t ltcl = b.getUInt8(); + ts->setLastTriggeredCheckline(ltcl); + } + + const unsigned cc = b.getUInt8(); + if (cc != CheckManager::get()->getCheckStructureCount()) + { + throw std::invalid_argument( + "Server has different check structures size."); + } + for (unsigned i = 0; i < cc; i++) + CheckManager::get()->getCheckStructure(i)->restoreIsActive(b); + +} // updateCheckLinesClient diff --git a/src/modes/linear_world.hpp b/src/modes/linear_world.hpp index 09f2408be..e28cc9297 100644 --- a/src/modes/linear_world.hpp +++ b/src/modes/linear_world.hpp @@ -220,6 +220,10 @@ public: virtual void saveCompleteState(BareNetworkString* bns) OVERRIDE; // ------------------------------------------------------------------------ virtual void restoreCompleteState(const BareNetworkString& b) OVERRIDE; + // ------------------------------------------------------------------------ + void updateCheckLinesServer(); + // ------------------------------------------------------------------------ + void updateCheckLinesClient(const BareNetworkString& b); }; // LinearWorld #endif diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 40dadd2ee..5dacccfe5 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -3,6 +3,7 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/player_controller.hpp" #include "modes/capture_the_flag.hpp" +#include "modes/linear_world.hpp" #include "modes/soccer_world.hpp" #include "network/event.hpp" #include "network/game_setup.hpp" @@ -52,6 +53,7 @@ bool GameEventsProtocol::notifyEvent(Event* event) CaptureTheFlag* ctf = dynamic_cast(World::getWorld()); FreeForAll* ffa = dynamic_cast(World::getWorld()); SoccerWorld* sw = dynamic_cast(World::getWorld()); + LinearWorld* lw = dynamic_cast(World::getWorld()); switch (type) { case GE_KART_FINISHED_RACE: @@ -126,6 +128,14 @@ bool GameEventsProtocol::notifyEvent(Event* event) } break; } + case GE_CHECK_LINE: + { + if (!lw) + throw std::invalid_argument("No linear world"); + if (NetworkConfig::get()->isClient()) + lw->updateCheckLinesClient(data); + break; + } default: Log::warn("GameEventsProtocol", "Unkown message type."); break; diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp index a2b9b2d55..830221b01 100644 --- a/src/network/protocols/game_events_protocol.hpp +++ b/src/network/protocols/game_events_protocol.hpp @@ -17,6 +17,7 @@ public: GE_CTF_SCORED = 4, GE_RESET_BALL = 5, GE_PLAYER_GOAL = 6, + GE_CHECK_LINE = 7 }; // GameEventType private: int m_last_finished_position; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 2be860f93..984833b15 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -982,7 +982,7 @@ void ServerLobby::update(int ticks) { for (unsigned i = 0; i < race_manager->getNumPlayers(); i++) { - const RemoteKartInfo& rki = race_manager->getKartInfo(i); + RemoteKartInfo& rki = race_manager->getKartInfo(i); std::shared_ptr player = rki.getNetworkPlayerProfile().lock(); if (player) @@ -1003,7 +1003,6 @@ void ServerLobby::update(int ticks) Log::info("ServerLobby", "%s hasn't live-joined within" " 60 seconds, remove it.", peer->getAddress().toString().c_str()); - RemoteKartInfo& rki = race_manager->getKartInfo(i); rki.makeReserved(); continue; } @@ -1012,9 +1011,10 @@ void ServerLobby::update(int ticks) { if (w && w->getKart(i)->hasFinishedRace()) continue; - Log::info("ServerLobby", "%s has been idle for more than" + Log::info("ServerLobby", "%s %s has been idle for more than" " %d seconds, kick.", - peer->getAddress().toString().c_str(), sec); + peer->getAddress().toString().c_str(), + StringUtils::wideToUtf8(rki.getPlayerName()).c_str(), sec); peer->kick(); } } diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 977716645..a87b5eb60 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -64,8 +64,7 @@ void ServersManager::deallocate() // ---------------------------------------------------------------------------- ServersManager::ServersManager() { - m_last_load_time.store(0); - m_list_updated = false; + reset(); } // ServersManager // ---------------------------------------------------------------------------- @@ -237,6 +236,7 @@ void ServersManager::setLanServers(const std::map& getBroadcastAddresses(); - + // ------------------------------------------------------------------------ + void reset() + { + m_last_load_time.store(0); + m_list_updated = false; + } }; // class ServersManager #endif // HEADER_SERVERS_MANAGER_HPP diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index bc81c840d..3bbd6e97b 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -774,13 +774,22 @@ void STKHost::mainLoop() if (p.second->isValidated() && p.second->getConnectedTime() > 5.0f && ap > max_ping) { - if (ServerConfig::m_kick_high_ping_players && - !p.second->isDisconnected()) + std::string player_name; + if (!p.second->getPlayerProfiles().empty()) { - Log::info("STKHost", "%s with ping %d is higher" - " than %d ms, kick.", + player_name = StringUtils::wideToUtf8 + (p.second->getPlayerProfiles()[0]->getName()); + } + const bool peer_not_in_game = + sl->getCurrentState() <= ServerLobby::SELECTING + || p.second->isWaitingForGame(); + if (ServerConfig::m_kick_high_ping_players && + !p.second->isDisconnected() && peer_not_in_game) + { + Log::info("STKHost", "%s %s with ping %d is higher" + " than %d ms when not in game, kick.", p.second->getAddress().toString().c_str(), - ap, max_ping); + player_name.c_str(), ap, max_ping); p.second->setWarnedForHighPing(true); p.second->setDisconnected(true); std::lock_guard lock(m_enet_cmd_mutex); @@ -790,10 +799,10 @@ void STKHost::mainLoop() } else if (!p.second->hasWarnedForHighPing()) { - Log::info("STKHost", "%s with ping %d is higher" + Log::info("STKHost", "%s %s with ping %d is higher" " than %d ms.", p.second->getAddress().toString().c_str(), - ap, max_ping); + player_name.c_str(), ap, max_ping); p.second->setWarnedForHighPing(true); NetworkString msg(PROTOCOL_LOBBY_ROOM); msg.setSynchronous(true); diff --git a/src/states_screens/online/server_selection.cpp b/src/states_screens/online/server_selection.cpp index ae2e47b2a..bc5d39a4b 100644 --- a/src/states_screens/online/server_selection.cpp +++ b/src/states_screens/online/server_selection.cpp @@ -165,6 +165,7 @@ void ServerSelection::init() m_server_list_widget->setIcons(m_icon_bank, row_height); m_sort_desc = false; /** Triggers the loading of the server list in the servers manager. */ + ServersManager::get()->reset(); refresh(true); } // init diff --git a/src/tracks/check_lap.hpp b/src/tracks/check_lap.hpp index d8b6f82f3..5f932a9a7 100644 --- a/src/tracks/check_lap.hpp +++ b/src/tracks/check_lap.hpp @@ -20,6 +20,7 @@ #define HEADER_CHECK_LAP_HPP #include "tracks/check_structure.hpp" +#include "utils/cpp2011.hpp" class XMLNode; class CheckManager; @@ -40,8 +41,9 @@ public: CheckLap(const XMLNode &node, unsigned int index); virtual ~CheckLap() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - int indx); - virtual void reset(const Track &track); + int indx) OVERRIDE; + virtual void reset(const Track &track) OVERRIDE; + virtual bool triggeringCheckline() const OVERRIDE { return true; } }; // CheckLine #endif diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 7ff751a45..c80ca24a5 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -26,6 +26,7 @@ using namespace irr; #include "tracks/check_structure.hpp" +#include "utils/cpp2011.hpp" class XMLNode; class CheckManager; @@ -88,11 +89,11 @@ public: CheckLine(const XMLNode &node, unsigned int index); virtual ~CheckLine(); virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - int indx); - virtual void reset(const Track &track); - virtual void resetAfterKartMove(unsigned int kart_index); - virtual void changeDebugColor(bool is_active); - virtual bool triggeringCheckline() const { return true; } + int indx) OVERRIDE; + virtual void reset(const Track &track) OVERRIDE; + virtual void resetAfterKartMove(unsigned int kart_index) OVERRIDE; + virtual void changeDebugColor(bool is_active) OVERRIDE; + virtual bool triggeringCheckline() const OVERRIDE { return true; } // ------------------------------------------------------------------------ /** Returns the actual line data for this checkpoint. */ const core::line2df &getLine2D() const {return m_line;} @@ -102,9 +103,9 @@ public: * be too heigh to otherwise trigger he cannon). */ void setIgnoreHeight(bool b) { m_ignore_height = b; } // ------------------------------------------------------------------------ - virtual void saveCompleteState(BareNetworkString* bns); + virtual void saveCompleteState(BareNetworkString* bns) OVERRIDE; // ------------------------------------------------------------------------ - virtual void restoreCompleteState(const BareNetworkString& b); + virtual void restoreCompleteState(const BareNetworkString& b) OVERRIDE; }; // CheckLine #endif diff --git a/src/tracks/check_structure.cpp b/src/tracks/check_structure.cpp index ca6544ff5..9c31f258c 100644 --- a/src/tracks/check_structure.cpp +++ b/src/tracks/check_structure.cpp @@ -101,6 +101,7 @@ void CheckStructure::reset(const Track &track) void CheckStructure::update(float dt) { World *world = World::getWorld(); + LinearWorld* lw = dynamic_cast(World::getWorld()); for(unsigned int i=0; igetNumKarts(); i++) { const Vec3 &xyz = world->getKart(i)->getFrontXYZ(); @@ -114,6 +115,8 @@ void CheckStructure::update(float dt) m_index, world->getKart(i)->getIdent().c_str(), World::getWorld()->getTime()); trigger(i); + if (triggeringCheckline() && lw) + lw->updateCheckLinesServer(); } m_previous_position[i] = xyz; } // for igetNumKarts(); i++) + bns->addUInt8(m_is_active[i] ? 1 : 0); +} // saveIsActive + +// ---------------------------------------------------------------------------- +void CheckStructure::restoreIsActive(const BareNetworkString& b) +{ + m_is_active.clear(); + World* world = World::getWorld(); + for (unsigned int i = 0; i < world->getNumKarts(); i++) + { + bool is_active = b.getUInt8() == 1; + m_is_active.push_back(is_active); + } +} // restoreIsActive diff --git a/src/tracks/check_structure.hpp b/src/tracks/check_structure.hpp index bc39365bd..e9fb44ed4 100644 --- a/src/tracks/check_structure.hpp +++ b/src/tracks/check_structure.hpp @@ -131,9 +131,15 @@ public: m_check_structures_to_change_state.push_back(i); } // addSuccessor // ------------------------------------------------------------------------ + virtual bool triggeringCheckline() const { return false; } + // ------------------------------------------------------------------------ virtual void saveCompleteState(BareNetworkString* bns); // ------------------------------------------------------------------------ virtual void restoreCompleteState(const BareNetworkString& b); + // ------------------------------------------------------------------------ + void saveIsActive(BareNetworkString* bns); + // ------------------------------------------------------------------------ + void restoreIsActive(const BareNetworkString& b); }; // CheckStructure #endif diff --git a/src/tracks/track_sector.hpp b/src/tracks/track_sector.hpp index f8362edc2..c57389faf 100644 --- a/src/tracks/track_sector.hpp +++ b/src/tracks/track_sector.hpp @@ -90,6 +90,9 @@ public: // ------------------------------------------------------------------------ void setLastTriggeredCheckline(int i) { m_last_triggered_checkline = i; } // ------------------------------------------------------------------------ + int getLastTriggeredCheckline() const + { return m_last_triggered_checkline; } + // ------------------------------------------------------------------------ int getLastValidGraphNode() const { return m_last_valid_graph_node; } // ------------------------------------------------------------------------ void saveState(BareNetworkString* buffer) const; diff --git a/src/utils/log.cpp b/src/utils/log.cpp index fc94c587a..40ce3b146 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -19,8 +19,10 @@ #include "utils/log.hpp" #include "config/user_config.hpp" +#include "network/network_config.hpp" #include +#include #include #ifdef ANDROID @@ -154,7 +156,21 @@ void Log::printMessage(int level, const char *component, const char *format, remaining = MAX_LENGTH - index > 0 ? MAX_LENGTH - index : 0; } - index += snprintf (line + index, remaining, "[%s] %s: ", names[level], component); +#ifndef ANDROID + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer()) + { + std::time_t result = std::time(nullptr); + index += snprintf (line + index, remaining, + "%.24s [%s] %s: ", std::asctime(std::localtime(&result)), + names[level], component); + } + else +#endif + { + index += snprintf (line + index, remaining, + "[%s] %s: ", names[level], component); + } remaining = MAX_LENGTH - index > 0 ? MAX_LENGTH - index : 0; index += vsnprintf(line + index, remaining, format, args); remaining = MAX_LENGTH - index > 0 ? MAX_LENGTH - index : 0; diff --git a/src/utils/string_utils.cpp b/src/utils/string_utils.cpp index b064d7d8d..6a4fea77b 100644 --- a/src/utils/string_utils.cpp +++ b/src/utils/string_utils.cpp @@ -1092,6 +1092,25 @@ namespace StringUtils // Step 7 push_text: + + // Calculate break index depending on max text length if there is no + // breakable character + if (break_index == 0) + { + for (unsigned int i = 0; i < work_copy.size(); i++) + { + std::wstring text = work_copy.substr(0, i+1); + unsigned int width = font->getDimension(text.c_str()).Width; + + if (width > max_width) + break; + + break_index++; + } + + break_index = std::max(0, (int)break_index - 1); + } + // To include the char at break_index, we need a length of break_index+1 std::wstring text_line = work_copy.substr(0,break_index+1); output.push_back(text_line); @@ -1106,6 +1125,7 @@ namespace StringUtils { work_copy = work_copy.substr(break_index+1); // All the string except the pushed back part index = 0; + break_index = 0; } } // While(true) - active until the whole string has been broken and copied if (right_to_left)