cubicchunks.server.CubeWatcher.java Source code

Java tutorial

Introduction

Here is the source code for cubicchunks.server.CubeWatcher.java

Source

/*
 *  This file is part of Tall Worlds, licensed under the MIT License (MIT).
 *
 *  Copyright (c) 2014 Tall Worlds
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
package cubicchunks.server;

import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.IPacket;
import net.minecraft.util.BlockPos;
import net.minecraft.world.World;

import com.google.common.collect.Maps;

import cubicchunks.network.PacketCubeBlockChange;
import cubicchunks.network.PacketCubeChange;
import cubicchunks.util.AddressTools;
import cubicchunks.world.cube.Cube;

public class CubeWatcher {

    private static final int MaxBlocksPerUpdate = 64;

    private static class PlayerEntry {

        EntityPlayerMP player;
        boolean sawCube;

        public PlayerEntry(EntityPlayerMP player) {
            this.player = player;
            this.sawCube = false;
        }
    }

    private Cube cube;
    private Map<Integer, PlayerEntry> players;
    private long previousWorldTime;
    private SortedSet<Integer> dirtyBlocks;

    public CubeWatcher(Cube cube) {
        if (cube == null) {
            throw new IllegalArgumentException("cube cannot be null!");
        }

        this.cube = cube;
        this.players = Maps.newTreeMap();
        this.previousWorldTime = 0;
        this.dirtyBlocks = new TreeSet<Integer>();
    }

    public Cube getCube() {
        return this.cube;
    }

    public void addPlayer(EntityPlayerMP player) {
        this.players.put(player.getEntityId(), new PlayerEntry(player));
        this.previousWorldTime = getWorldTime();
    }

    public void removePlayer(EntityPlayerMP player) {
        this.players.remove(player.getEntityId());
        updateInhabitedTime();
    }

    public boolean hasPlayers() {
        return !this.players.isEmpty();
    }

    public void setPlayerSawCube(EntityPlayerMP player) {
        PlayerEntry entry = this.players.get(player.getEntityId());
        if (entry != null) {
            entry.sawCube = true;
        }
    }

    public void tick() {
        updateInhabitedTime();
    }

    private long getWorldTime() {
        return this.cube.getWorld().getGameTime();
    }

    private void updateInhabitedTime() {
        final long now = getWorldTime();

        long inhabitedTime = this.cube.getColumn().getInhabitedTime();
        inhabitedTime += now - this.previousWorldTime;

        this.cube.getColumn().setInhabitedTime(inhabitedTime);
        this.previousWorldTime = now;
    }

    public void setDirtyBlock(int localX, int localY, int localZ) {
        // save up to some number of individual block updates
        // once that threshold is passed, the whole cube is sent during an update,
        // so there's no need to save more per-block updates
        if (this.dirtyBlocks.size() < MaxBlocksPerUpdate) {
            this.dirtyBlocks.add(AddressTools.getLocalAddress(localX, localY, localZ));
        }
    }

    public void sendUpdates() {

        // are there any updates?
        if (this.dirtyBlocks.isEmpty()) {
            return;
        }

        World world = this.cube.getWorld();

        if (this.dirtyBlocks.size() == MaxBlocksPerUpdate) {

            // send whole cube
            sendPacketToAllPlayers(new PacketCubeChange(cube));
            for (BlockEntity blockEntity : this.cube.getBlockEntities()) {
                sendBlockEntityToAllPlayers(blockEntity);
            }

        } else {

            // send all the dirty blocks
            sendPacketToAllPlayers(new PacketCubeBlockChange(this.cube, this.dirtyBlocks));

            // send the block entites on those blocks too
            BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
            for (int address : this.dirtyBlocks) {
                cube.localAddressToBlockPos(pos, address);
                if (world.getBlockEntityAt(pos) != null) {
                    sendBlockEntityToAllPlayers(world.getBlockEntityAt(pos));
                }
            }
        }

        this.dirtyBlocks.clear();
    }

    private void sendBlockEntityToAllPlayers(BlockEntity blockEntity) {
        if (blockEntity == null) {
            return;
        }
        IPacket<?> packet = blockEntity.getDescriptionPacket();
        if (packet == null) {
            return;
        }
        sendPacketToAllPlayers(packet);
    }

    private void sendPacketToAllPlayers(IPacket<?> packet) {
        for (PlayerEntry entry : this.players.values()) {
            // has this player seen this cube before?
            if (entry.sawCube) {
                entry.player.netServerHandler.send(packet);
            }
        }
    }
}