| 210 | } |
| 211 | |
| 212 | bool WorldServer::addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin, NetCompatibilityRules netRules) { |
| 213 | if (m_clientInfo.contains(clientId)) |
| 214 | return false; |
| 215 | |
| 216 | Vec2F playerStart; |
| 217 | if (spawnTarget.is<SpawnTargetPosition>()) { |
| 218 | playerStart = spawnTarget.get<SpawnTargetPosition>(); |
| 219 | } else if (spawnTarget.is<SpawnTargetX>()) { |
| 220 | auto targetX = spawnTarget.get<SpawnTargetX>(); |
| 221 | playerStart = findPlayerSpaceStart(targetX); |
| 222 | } else if (spawnTarget.is<SpawnTargetUniqueEntity>()) { |
| 223 | if (auto target = m_entityMap->get<WarpTargetEntity>(m_worldStorage->loadUniqueEntity(spawnTarget.get<SpawnTargetUniqueEntity>()))) |
| 224 | playerStart = target->position() + target->footPosition(); |
| 225 | else |
| 226 | return false; |
| 227 | } else { |
| 228 | playerStart = m_playerStart; |
| 229 | if (m_adjustPlayerStart) { |
| 230 | m_playerStart = findPlayerStart(m_playerStart); |
| 231 | playerStart = m_playerStart; |
| 232 | } |
| 233 | } |
| 234 | RectF spawnRegion = RectF(playerStart, playerStart).padded(m_serverConfig.getInt("playerStartInitialGenRadius")); |
| 235 | generateRegion(RectI::integral(spawnRegion)); |
| 236 | m_spawner.activateEmptyRegion(spawnRegion); |
| 237 | |
| 238 | InterpolationTracker tracker; |
| 239 | if (isLocal) |
| 240 | tracker = InterpolationTracker(m_serverConfig.query("interpolationSettings.local")); |
| 241 | else |
| 242 | tracker = InterpolationTracker(m_serverConfig.query("interpolationSettings.normal")); |
| 243 | |
| 244 | tracker.update(m_currentTime); |
| 245 | |
| 246 | auto& clientInfo = m_clientInfo.add(clientId, make_shared<ClientInfo>(clientId, tracker)); |
| 247 | clientInfo->local = isLocal; |
| 248 | clientInfo->admin = isAdmin; |
| 249 | clientInfo->clientState.setNetCompatibilityRules(netRules); |
| 250 | |
| 251 | auto worldStartPacket = make_shared<WorldStartPacket>(); |
| 252 | auto& templateData = worldStartPacket->templateData = m_worldTemplate->store(); |
| 253 | // this makes it possible to use custom InstanceWorlds without clients having the mod that adds their dungeon: |
| 254 | if (templateData.optQueryString("worldParameters.primaryDungeon") |
| 255 | && Root::singletonPtr()->configuration()->getPath("compatibility.customDungeonWorld").optBool().value(false)) |
| 256 | worldStartPacket->templateData = worldStartPacket->templateData.setPath("worldParameters.primaryDungeon", "testarena"); |
| 257 | |
| 258 | tie(worldStartPacket->skyData, clientInfo->skyNetVersion) = m_sky->writeUpdate(0, netRules); |
| 259 | tie(worldStartPacket->weatherData, clientInfo->weatherNetVersion) = m_weather.writeUpdate(0, netRules); |
| 260 | worldStartPacket->playerStart = playerStart; |
| 261 | worldStartPacket->playerRespawn = m_playerStart; |
| 262 | worldStartPacket->respawnInWorld = m_respawnInWorld; |
| 263 | worldStartPacket->worldProperties = m_worldProperties; |
| 264 | worldStartPacket->dungeonIdGravity = m_dungeonIdGravity; |
| 265 | worldStartPacket->dungeonIdBreathable = m_dungeonIdBreathable; |
| 266 | worldStartPacket->protectedDungeonIds = m_protectedDungeonIds; |
| 267 | worldStartPacket->clientId = clientId; |
| 268 | worldStartPacket->localInterpolationMode = isLocal; |
| 269 | clientInfo->outgoingPackets.append(worldStartPacket); |
no test coverage detected