/* * Copyright 2016 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.logic.players; import com.google.common.collect.Lists; import org.terasology.entitySystem.entity.EntityManager; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.entitySystem.event.ReceiveEvent; import org.terasology.entitySystem.systems.BaseComponentSystem; import org.terasology.entitySystem.systems.RegisterMode; import org.terasology.entitySystem.systems.RegisterSystem; import org.terasology.entitySystem.systems.UpdateSubscriberSystem; import org.terasology.logic.characters.CharacterComponent; import org.terasology.logic.location.Location; import org.terasology.logic.location.LocationComponent; import org.terasology.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.logic.players.event.RespawnRequestEvent; import org.terasology.math.geom.Quat4f; import org.terasology.math.geom.Vector3f; import org.terasology.math.geom.Vector3i; import org.terasology.network.Client; import org.terasology.network.ClientComponent; import org.terasology.network.NetworkSystem; import org.terasology.network.events.ConnectedEvent; import org.terasology.network.events.DisconnectedEvent; import org.terasology.persistence.PlayerStore; import org.terasology.registry.In; import org.terasology.rendering.world.viewDistance.ViewDistance; import org.terasology.world.WorldProvider; import org.terasology.world.chunks.ChunkProvider; import org.terasology.world.generator.WorldGenerator; import java.util.Iterator; import java.util.List; @RegisterSystem(RegisterMode.AUTHORITY) public class PlayerSystem extends BaseComponentSystem implements UpdateSubscriberSystem { @In private EntityManager entityManager; @In private WorldGenerator worldGenerator; @In private WorldProvider worldProvider; @In private ChunkProvider chunkProvider; @In private NetworkSystem networkSystem; private List<SpawningClientInfo> clientsPreparingToSpawn = Lists.newArrayList(); @Override public void initialise() { } @Override public void update(float delta) { Iterator<SpawningClientInfo> i = clientsPreparingToSpawn.iterator(); while (i.hasNext()) { SpawningClientInfo spawning = i.next(); if (worldProvider.isBlockRelevant(spawning.position)) { PlayerStore playerStore = spawning.playerStore; if (playerStore == null) { spawnPlayer(spawning.clientEntity); } else { playerStore.restoreEntities(); EntityRef character = playerStore.getCharacter(); restoreCharacter(spawning.clientEntity, character); } i.remove(); } } } @ReceiveEvent(components = ClientComponent.class) public void onConnect(ConnectedEvent connected, EntityRef entity) { LocationComponent loc = entity.getComponent(LocationComponent.class); // for new clients, the player store will return default values PlayerStore playerStore = connected.getPlayerStore(); Client owner = networkSystem.getOwner(entity); Vector3i minViewDist = ViewDistance.LEGALLY_BLIND.getChunkDistance(); if (playerStore.hasCharacter()) { Vector3f storedLocation = playerStore.getRelevanceLocation(); loc.setWorldPosition(storedLocation); entity.saveComponent(loc); if (worldProvider.isBlockRelevant(storedLocation)) { // chunk for spawning location is ready, so spawn right now playerStore.restoreEntities(); EntityRef character = playerStore.getCharacter(); Vector3i viewDist = owner.getViewDistance().getChunkDistance(); addRelevanceEntity(entity, viewDist, owner); restoreCharacter(entity, character); } else { // otherwise wait until chunk is ready addRelevanceEntity(entity, minViewDist, owner); clientsPreparingToSpawn.add(new SpawningClientInfo(entity, storedLocation, playerStore)); } } else { Vector3f spawnPosition = worldGenerator.getSpawnPosition(entity); loc.setWorldPosition(spawnPosition); entity.saveComponent(loc); addRelevanceEntity(entity, minViewDist, owner); if (worldProvider.isBlockRelevant(spawnPosition)) { spawnPlayer(entity); } else { clientsPreparingToSpawn.add(new SpawningClientInfo(entity, spawnPosition)); } } } private void restoreCharacter(EntityRef entity, EntityRef character) { Client clientListener = networkSystem.getOwner(entity); updateRelevanceEntity(entity, clientListener.getViewDistance().getChunkDistance()); ClientComponent client = entity.getComponent(ClientComponent.class); client.character = character; entity.saveComponent(client); CharacterComponent characterComp = character.getComponent(CharacterComponent.class); if (characterComp != null) { characterComp.controller = entity; character.saveComponent(characterComp); character.setOwner(entity); Location.attachChild(character, entity, new Vector3f(), new Quat4f(0, 0, 0, 1)); } else { character.destroy(); spawnPlayer(entity); } } private void updateRelevanceEntity(EntityRef entity, Vector3i chunkDistance) { //RelevanceRegionComponent relevanceRegion = new RelevanceRegionComponent(); //relevanceRegion.distance = chunkDistance; //entity.saveComponent(relevanceRegion); chunkProvider.updateRelevanceEntity(entity, chunkDistance); } private void removeRelevanceEntity(EntityRef entity) { //entity.removeComponent(RelevanceRegionComponent.class); chunkProvider.removeRelevanceEntity(entity); } private void addRelevanceEntity(EntityRef entity, Vector3i chunkDistance, Client owner) { //RelevanceRegionComponent relevanceRegion = new RelevanceRegionComponent(); //relevanceRegion.distance = chunkDistance; //entity.addComponent(relevanceRegion); chunkProvider.addRelevanceEntity(entity, chunkDistance, owner); } @ReceiveEvent(components = ClientComponent.class) public void onDisconnect(DisconnectedEvent event, EntityRef entity) { removeRelevanceEntity(entity); } @ReceiveEvent(components = {ClientComponent.class}) public void onRespawnRequest(RespawnRequestEvent event, EntityRef entity) { Vector3f spawnPosition = worldGenerator.getSpawnPosition(entity); LocationComponent loc = entity.getComponent(LocationComponent.class); loc.setWorldPosition(spawnPosition); loc.setLocalRotation(new Quat4f()); // reset rotation entity.saveComponent(loc); if (worldProvider.isBlockRelevant(spawnPosition)) { spawnPlayer(entity); } else { updateRelevanceEntity(entity, ViewDistance.LEGALLY_BLIND.getChunkDistance()); SpawningClientInfo info = new SpawningClientInfo(entity, spawnPosition); clientsPreparingToSpawn.add(info); } } private void spawnPlayer(EntityRef clientEntity) { ClientComponent client = clientEntity.getComponent(ClientComponent.class); PlayerFactory playerFactory = new PlayerFactory(entityManager, worldProvider); EntityRef playerCharacter = playerFactory.newInstance(clientEntity); Client clientListener = networkSystem.getOwner(clientEntity); Vector3i distance = clientListener.getViewDistance().getChunkDistance(); updateRelevanceEntity(clientEntity, distance); client.character = playerCharacter; clientEntity.saveComponent(client); playerCharacter.send(new OnPlayerSpawnedEvent()); } private static class SpawningClientInfo { public EntityRef clientEntity; public PlayerStore playerStore; public Vector3f position; SpawningClientInfo(EntityRef client, Vector3f position) { this.clientEntity = client; this.position = position; } SpawningClientInfo(EntityRef client, Vector3f position, PlayerStore playerStore) { this(client, position); this.playerStore = playerStore; } } }