Package org.terasology.network.internal

Source Code of org.terasology.network.internal.ServerImpl

/*
* Copyright 2013 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.network.internal;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import com.google.common.collect.SetMultimap;

import gnu.trove.iterator.TIntIterator;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;

import org.jboss.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.registry.CoreRegistry;
import org.terasology.engine.EngineTime;
import org.terasology.engine.Time;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.entitySystem.event.Event;
import org.terasology.math.TeraMath;
import org.terasology.math.Vector3i;
import org.terasology.network.NetMetricSource;
import org.terasology.network.NetworkComponent;
import org.terasology.network.Server;
import org.terasology.network.ServerInfoMessage;
import org.terasology.network.serialization.ClientComponentFieldCheck;
import org.terasology.persistence.serializers.EventSerializer;
import org.terasology.persistence.serializers.NetworkEntitySerializer;
import org.terasology.protobuf.EntityData;
import org.terasology.protobuf.NetData;
import org.terasology.world.BlockEntityRegistry;
import org.terasology.world.WorldProvider;
import org.terasology.world.biomes.Biome;
import org.terasology.world.biomes.BiomeManager;
import org.terasology.world.block.Block;
import org.terasology.world.block.BlockComponent;
import org.terasology.world.block.BlockManager;
import org.terasology.world.block.BlockUri;
import org.terasology.world.block.internal.BlockManagerImpl;
import org.terasology.world.chunks.Chunk;
import org.terasology.world.chunks.internal.ChunkSerializer;
import org.terasology.world.chunks.remoteChunkProvider.RemoteChunkProvider;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

/**
* Used to interact with a remote server (from client end)
*
* @author Immortius
*/
public class ServerImpl implements Server {
    private static final Logger logger = LoggerFactory.getLogger(ServerImpl.class);

    private int clientEntityNetId;

    private NetworkSystemImpl networkSystem;
    private Channel channel;
    private NetMetricSource metricsSource;
    private BlockingQueue<NetData.NetMessage> queuedMessages = Queues.newLinkedBlockingQueue();
    private List<NetData.EventMessage> queuedOutgoingEvents = Lists.newArrayList();
    private NetData.ServerInfoMessage serverInfo;

    private EngineEntityManager entityManager;
    private NetworkEntitySerializer entitySerializer;
    private EventSerializer eventSerializer;
    private BlockManagerImpl blockManager;
    private BiomeManager biomeManager;

    private BlockEntityRegistry blockEntityRegistry;
    private RemoteChunkProvider remoteWorldProvider;
    private BlockingQueue<Chunk> chunkQueue = Queues.newLinkedBlockingQueue();
    private TIntSet netDirty = new TIntHashSet();
    private SetMultimap<Integer, Class<? extends Component>> changedComponents = HashMultimap.create();
    private ListMultimap<Vector3i, NetData.BlockChangeMessage> awaitingChunkReadyBlockUpdates = ArrayListMultimap.create();
    private ListMultimap<Vector3i, NetData.BiomeChangeMessage> awaitingChunkReadyBiomeUpdates = ArrayListMultimap.create();

    private EngineTime time;



    public ServerImpl(NetworkSystemImpl system, Channel channel) {
        this.channel = channel;
        metricsSource = (NetMetricSource) channel.getPipeline().get(MetricRecordingHandler.NAME);
        this.networkSystem = system;
        this.time = (EngineTime) CoreRegistry.get(Time.class);
    }

    void connectToEntitySystem(EngineEntityManager newEntityManager, NetworkEntitySerializer newEntitySerializer,
                               EventSerializer newEventSerializer, BlockEntityRegistry newBlockEntityRegistry) {
        this.entityManager = newEntityManager;
        this.eventSerializer = newEventSerializer;
        this.entitySerializer = newEntitySerializer;
        this.blockEntityRegistry = newBlockEntityRegistry;
        blockManager = (BlockManagerImpl) CoreRegistry.get(BlockManager.class);
        biomeManager = CoreRegistry.get(BiomeManager.class);
    }

    void setServerInfo(NetData.ServerInfoMessage serverInfo) {
        this.serverInfo = serverInfo;
    }

    void setClientId(int id) {
        clientEntityNetId = id;
    }

    @Override
    public EntityRef getClientEntity() {
        return networkSystem.getEntity(clientEntityNetId);
    }

    @Override
    public ServerInfoMessage getInfo() {
        return new ServerInfoMessageImpl(serverInfo);
    }
   
    public NetData.ServerInfoMessage getRawInfo() {
        return serverInfo;
    }

    @Override
    public void send(Event event, EntityRef target) {
        NetworkComponent netComp = target.getComponent(NetworkComponent.class);
        if (netComp != null) {
            queuedOutgoingEvents.add(NetData.EventMessage.newBuilder()
                    .setEvent(eventSerializer.serialize(event))
                    .setTargetId(netComp.getNetworkId()).build());
        }
    }

    @Override
    public void update(boolean netTick) {
        processReceivedChunks();
        if (entityManager != null) {
            if (netTick) {
                NetData.NetMessage.Builder message = NetData.NetMessage.newBuilder();
                message.setTime(time.getGameTimeInMs());
                sendEntities(message);
                sendEvents(message);
                send(message.build());
            } else if (!queuedOutgoingEvents.isEmpty()) {
                NetData.NetMessage.Builder message = NetData.NetMessage.newBuilder();
                message.setTime(time.getGameTimeInMs());
                sendEvents(message);
                send(message.build());
            }

            processMessages();
        }
    }

    private void sendEvents(NetData.NetMessage.Builder message) {
        for (NetData.EventMessage event : queuedOutgoingEvents) {
            message.addEvent(event);
        }
        queuedOutgoingEvents.clear();
    }

    private void processReceivedChunks() {
        if (remoteWorldProvider != null) {
            List<Chunk> chunks = Lists.newArrayListWithExpectedSize(chunkQueue.size());
            chunkQueue.drainTo(chunks);
            for (Chunk chunk : chunks) {
                remoteWorldProvider.receiveChunk(chunk);
            }
        }
    }

    private void send(NetData.NetMessage data) {
        logger.trace("Sending with size {}", data.getSerializedSize());
        channel.write(data);
    }

    private void sendEntities(NetData.NetMessage.Builder message) {
        TIntIterator dirtyIterator = netDirty.iterator();
        while (dirtyIterator.hasNext()) {
            int netId = dirtyIterator.next();
            EntityRef entity = networkSystem.getEntity(netId);
            if (isOwned(entity)) {
                Set<Class<? extends Component>> emptyComponentClassSet = Collections.emptySet();
                EntityData.PackedEntity entityData = entitySerializer.serialize(entity, emptyComponentClassSet, changedComponents.get(netId),
                        emptyComponentClassSet, new ClientComponentFieldCheck());
                if (entityData != null) {
                    message.addUpdateEntity(NetData.UpdateEntityMessage.newBuilder().setEntity(entityData).setNetId(netId));
                }
            }
        }
        netDirty.clear();
    }

    private boolean isOwned(EntityRef entity) {
        EntityRef owner = networkSystem.getOwnerEntity(entity);
        return getClientEntity().equals(owner);
    }


    private void processEvent(NetData.EventMessage message) {
        Event event = eventSerializer.deserialize(message.getEvent());
        EntityRef target = EntityRef.NULL;
        if (message.hasTargetBlockPos()) {
            target = blockEntityRegistry.getBlockEntityAt(NetMessageUtil.convert(message.getTargetBlockPos()));
        } else if (message.hasTargetId()) {
            target = networkSystem.getEntity(message.getTargetId());
        }
        if (target.exists()) {
            target.send(event);
        } else {
            logger.info("Dropping event {} for unavailable entity {}", event.getClass().getSimpleName(), target);
        }
    }

    void setRemoteWorldProvider(RemoteChunkProvider remoteWorldProvider) {
        this.remoteWorldProvider = remoteWorldProvider;
        remoteWorldProvider.subscribe(this);
    }

    private void processMessages() {
        List<NetData.NetMessage> messages = Lists.newArrayListWithExpectedSize(queuedMessages.size());
        queuedMessages.drainTo(messages);

        for (NetData.NetMessage message : messages) {
            if (message.hasTime()) {
                time.updateTimeFromServer(message.getTime());
            }
            processBlockRegistrations(message);
            processReceivedChunks(message);
            processInvalidatedChunks(message);
            processBlockChanges(message);
            processBiomeChanges(message);
            processRemoveEntities(message);
            for (NetData.CreateEntityMessage createEntity : message.getCreateEntityList()) {
                createEntityMessage(createEntity);
            }
            for (NetData.UpdateEntityMessage updateEntity : message.getUpdateEntityList()) {
                updateEntity(updateEntity);
            }
            for (NetData.EventMessage event : message.getEventList()) {
                processEvent(event);
            }
        }
    }

    private void processRemoveEntities(NetData.NetMessage message) {
        for (NetData.RemoveEntityMessage removeEntity : message.getRemoveEntityList()) {
            int netId = removeEntity.getNetId();
            EntityRef entity = networkSystem.getEntity(netId);
            if (entity.exists()) {
                logger.info("Destroying entity: {}", entity);
                entity.destroy();
                networkSystem.unregisterClientNetworkEntity(netId);
            }
        }
    }

    private void processBlockChanges(NetData.NetMessage message) {
        for (NetData.BlockChangeMessage blockChange : message.getBlockChangeList()) {
            Block newBlock = blockManager.getBlock((short) blockChange.getNewBlock());
            logger.debug("Received block change to {}", newBlock);
            // TODO: Store changes to blocks that aren't ready to be modified (the surrounding chunks aren't available)
            WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);
            Vector3i pos = NetMessageUtil.convert(blockChange.getPos());
            if (worldProvider.isBlockRelevant(pos)) {
                worldProvider.setBlock(pos, newBlock);
            } else {
                awaitingChunkReadyBlockUpdates.put(TeraMath.calcChunkPos(pos), blockChange);
            }
        }
    }

    private void processBiomeChanges(NetData.NetMessage message) {
        for (NetData.BiomeChangeMessage biomeChange : message.getBiomeChangeList()) {
            logger.debug("Received block change to {}", blockManager.getBlock((short) biomeChange.getNewBiome()));
            // TODO: Store changes to blocks that aren't ready to be modified (the surrounding chunks aren't available)
            WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);
            Vector3i pos = NetMessageUtil.convert(biomeChange.getPos());
            if (worldProvider.isBlockRelevant(pos)) {
                Biome newBiome = biomeManager.getBiomeByShortId((short) biomeChange.getNewBiome());
                worldProvider.setBiome(pos, newBiome);
            } else {
                awaitingChunkReadyBiomeUpdates.put(TeraMath.calcChunkPos(pos), biomeChange);
            }
        }
    }

    private void processInvalidatedChunks(NetData.NetMessage message) {
        for (NetData.InvalidateChunkMessage chunk : message.getInvalidateChunkList()) {
            Vector3i chunkPos = NetMessageUtil.convert(chunk.getPos());
            remoteWorldProvider.invalidateChunks(chunkPos);
            awaitingChunkReadyBlockUpdates.removeAll(chunkPos);
            awaitingChunkReadyBiomeUpdates.removeAll(chunkPos);
        }
    }

    private void processReceivedChunks(NetData.NetMessage message) {
        for (EntityData.ChunkStore chunkInfo : message.getChunkInfoList()) {
            Chunk chunk = ChunkSerializer.decode(chunkInfo);
            chunkQueue.offer(chunk);
        }
    }

    private void processBlockRegistrations(NetData.NetMessage message) {
        for (NetData.BlockFamilyRegisteredMessage blockFamily : message.getBlockFamilyRegisteredList()) {
            if (blockFamily.getBlockIdCount() != blockFamily.getBlockUriCount()) {
                logger.error("Received block registration with mismatched id<->uri mapping");
            } else if (blockFamily.getBlockUriCount() == 0) {
                logger.error("Received empty block registration");
            } else {
                BlockUri family = new BlockUri(blockFamily.getBlockUri(0)).getFamilyUri();
                Map<String, Integer> registrationMap = Maps.newHashMap();
                for (int i = 0; i < blockFamily.getBlockIdCount(); ++i) {
                    registrationMap.put(blockFamily.getBlockUri(i), blockFamily.getBlockId(i));
                }
                blockManager.receiveFamilyRegistration(family, registrationMap);
            }
        }
    }

    private void updateEntity(NetData.UpdateEntityMessage updateEntity) {
        EntityRef currentEntity = networkSystem.getEntity(updateEntity.getNetId());
        if (currentEntity.exists()) {
            NetworkComponent netComp = currentEntity.getComponent(NetworkComponent.class);
            if (netComp == null) {
                logger.error("Updating entity with no network component: {}, expected netId {}", currentEntity, updateEntity.getNetId());
                return;
            }
            if (netComp.getNetworkId() != updateEntity.getNetId()) {
                logger.error("Network ID wrong before update");
            }
            boolean blockEntityBefore = currentEntity.hasComponent(BlockComponent.class);
            entitySerializer.deserializeOnto(currentEntity, updateEntity.getEntity());
            BlockComponent blockComponent = currentEntity.getComponent(BlockComponent.class);
            if (blockComponent != null && !blockEntityBefore) {
                if (!blockEntityRegistry.getExistingBlockEntityAt(blockComponent.getPosition()).equals(currentEntity)) {
                    logger.error("Failed to associated new block entity");
                }
            }
            if (netComp.getNetworkId() != updateEntity.getNetId()) {
                logger.error("Network ID lost in update: {}, {} -> {}", currentEntity, updateEntity.getNetId(), netComp.getNetworkId());
            }
        } else {
            logger.warn("Received update for non-existent entity {}", updateEntity.getNetId());
        }
    }

    private void createEntityMessage(NetData.CreateEntityMessage message) {
        entitySerializer.deserialize(message.getEntity());
    }

    @Override
    public void queueMessage(NetData.NetMessage message) {
        queuedMessages.offer(message);
    }

    @Override
    public void setComponentDirty(int netId, Class<? extends Component> componentType) {
        netDirty.add(netId);
        changedComponents.put(netId, componentType);
    }

    @Override
    public NetMetricSource getMetrics() {
        return metricsSource;
    }

    @Override
    public void onChunkReady(Vector3i chunkPos) {
        WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class);

        List<NetData.BlockChangeMessage> updateBlockMessages = awaitingChunkReadyBlockUpdates.removeAll(chunkPos);
        for (NetData.BlockChangeMessage message : updateBlockMessages) {
            Vector3i pos = NetMessageUtil.convert(message.getPos());
            Block newBlock = blockManager.getBlock((short) message.getNewBlock());
            worldProvider.setBlock(pos, newBlock);
        }

        List<NetData.BiomeChangeMessage> updateBiomeMessages = awaitingChunkReadyBiomeUpdates.removeAll(chunkPos);
        for (NetData.BiomeChangeMessage message : updateBiomeMessages) {
            Vector3i pos = NetMessageUtil.convert(message.getPos());
            Biome newBiome = biomeManager.getBiomeByShortId((short) message.getNewBiome());
            worldProvider.setBiome(pos, newBiome);
        }
    }
}

TOP

Related Classes of org.terasology.network.internal.ServerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.