コード例 #1
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var blockCount = BitConverter.ToInt32(ReadStream(sizeof(int)), 0);

                    for (var i = 0; i < blockCount; i++)
                    {
                        var bytes     = ReadStream(Position.SIZE + sizeof(ushort));
                        var position  = new Position(bytes, 0);
                        var blockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Position.SIZE);
                        Blocks.Add(new AddBlock(ref position, blockType));
                    }
                }
            }

            Settings.ChunkUpdatesDisabled = true;
            foreach (var addBlock in Blocks)
            {
                WorldData.PlaceBlock(addBlock.Position, addBlock.BlockType);
            }
            Settings.ChunkUpdatesDisabled = false;

            if (Config.IsServer)
            {
                //bm: this has to wait until the server can manage who's in creative mode
                //if (ConnectedPlayer.Inventory[(int)BlockType] <= 0) return;
                //ConnectedPlayer.Inventory[(int)BlockType] -= 1;

                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti {
                        ConnectedPlayer = player
                    };
                    addBlockMulti.Blocks.AddRange(Blocks);
                    addBlockMulti.Send();
                }
            }
        }
コード例 #2
0
        internal override void Receive()
        {
            if (!Config.IsSinglePlayer)
            {
                lock (TcpClient)
                {
                    base.Receive();
                    var blockCount = BitConverter.ToInt32(ReadStream(sizeof(int)), 0);

                    for (var i = 0; i < blockCount; i++)
                    {
                        var bytes = ReadStream(Position.SIZE + sizeof(ushort));
                        var position = new Position(bytes, 0);
                        var blockType = (Block.BlockType)BitConverter.ToUInt16(bytes, Position.SIZE);
                        Blocks.Add(new AddBlock(ref position, blockType));
                    }
                }
            }

            Settings.ChunkUpdatesDisabled = true;
            foreach (var addBlock in Blocks) WorldData.PlaceBlock(addBlock.Position, addBlock.BlockType);
            Settings.ChunkUpdatesDisabled = false;

            if (Config.IsServer)
            {
                //bm: this has to wait until the server can manage who's in creative mode
                //if (ConnectedPlayer.Inventory[(int)BlockType] <= 0) return;
                //ConnectedPlayer.Inventory[(int)BlockType] -= 1;

                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti { ConnectedPlayer = player };
                    addBlockMulti.Blocks.AddRange(Blocks);
                    addBlockMulti.Send();
                }
            }
        }
コード例 #3
0
ファイル: Chunk.cs プロジェクト: MagistrAVSH/voxelgame
        /// <summary>Only called for SinglePlayer and Servers.</summary>
        private void GrassGrow()
        {
            var possibleChanges = new List<Tuple<Block.BlockType, Position>>();
            for (var x = 0; x < CHUNK_SIZE; x++)
            {
                int worldX = Coords.WorldCoordsX + x;
                for (var z = 0; z < CHUNK_SIZE; z++)
                {
                    int worldZ = Coords.WorldCoordsZ + z;
                    for (var y = 0; y <= Math.Min(CHUNK_HEIGHT - 1, HeightMap[x, z] + 1); y++) //look +1 above heightmap as water directly above heightmap could change to ice
                    {
                        var blockType = Blocks[x, y, z].Type;
                        switch (blockType)
                        {
                            case Block.BlockType.Grass:
                            case Block.BlockType.Dirt:
                            case Block.BlockType.Snow:
                            case Block.BlockType.Water:
                            case Block.BlockType.Sand:
                            case Block.BlockType.SandDark:
                                break;
                            default:
                                continue; //continue if this block type can never cause changes
                        }

                        bool hasAirAbove = y >= CHUNK_HEIGHT - 1 || Blocks[x, y + 1, z].Type == Block.BlockType.Air;
                        bool isReceivingSunlight = y > HeightMap[x, z] || (hasAirAbove && WorldData.HasAdjacentBlockReceivingDirectSunlight(worldX, y, worldZ));

                        switch (WorldData.WorldType)
                        {
                            case WorldType.Grass:
                                if (isReceivingSunlight)
                                {
                                    switch (blockType)
                                    {
                                        case Block.BlockType.Dirt:
                                            if (hasAirAbove) possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Grass, new Position(worldX, y, worldZ)));
                                            continue;
                                        case Block.BlockType.Snow:
                                            possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Dirt, new Position(worldX, y, worldZ)));
                                            continue;
                                    }
                                }
                                else
                                {
                                    switch (blockType)
                                    {
                                        case Block.BlockType.Grass:
                                        case Block.BlockType.Snow:
                                            possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Dirt, new Position(worldX, y, worldZ)));
                                            continue;
                                    }
                                }
                                break;
                            case WorldType.Desert: //lighting doesnt matter for deserts
                                switch (blockType)
                                {
                                    case Block.BlockType.Grass:
                                    case Block.BlockType.Snow:
                                        possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Sand, new Position(worldX, y, worldZ)));
                                        continue;
                                    case Block.BlockType.SandDark:
                                        if (hasAirAbove) possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Sand, new Position(worldX, y, worldZ)));
                                        continue;
                                }
                                break;
                            case WorldType.Winter:
                                switch (blockType)
                                {
                                    case Block.BlockType.Water:
                                        //water with air above and without more water below can freeze
                                        //note: this will cause multiple lightbox updates and chunk queues if multiple water freezes at once because water -> ice is a change in transparency; therefore this is acceptable
                                        if (hasAirAbove)
                                        {
                                            var hasWaterBelow = y > 0 && Blocks[x, y - 1, z].Type == Block.BlockType.Water;
                                            if (!hasWaterBelow) possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Ice, new Position(worldX, y, worldZ)));
                                        }
                                        continue;
                                }

                                if (isReceivingSunlight)
                                {
                                    switch (blockType)
                                    {
                                        case Block.BlockType.Dirt:
                                            if (hasAirAbove) possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Snow, new Position(worldX, y, worldZ)));
                                            continue;
                                        case Block.BlockType.Grass:
                                            possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Snow, new Position(worldX, y, worldZ)));
                                            continue;
                                    }
                                }
                                else
                                {
                                    switch (blockType)
                                    {
                                        case Block.BlockType.Grass:
                                        case Block.BlockType.Snow:
                                            possibleChanges.Add(new Tuple<Block.BlockType, Position>(Block.BlockType.Dirt, new Position(worldX, y, worldZ)));
                                            continue;
                                    }
                                }
                                break;
                        }
                    }
                }
            }

            if (possibleChanges.Count == 0)
            {
                //this happens after a change is made in the chunk that did not cause any possible grass grow style changes
                GrassGrowing = false;
                Debug.WriteLine("Grass finished growing in chunk {0} No possible changes found", Coords);
                return;
            }
            Debug.WriteLine("Grass growing in chunk {0} {1} possible change(s)", Coords, possibleChanges.Count);

            var changesMade = 0;
            var addBlocks = new List<AddBlock>(); //only gets used for servers
            Settings.ChunkUpdatesDisabled = true; //change blocks while updates are disabled so chunk is only rebuilt once
            {
                foreach (var change in possibleChanges)
                {
                    //add some randomness so the changes dont happen all at once
                    if (possibleChanges.Count > 1)
                    {
                        switch (change.Item1) //can assign different percentages based on block type
                        {
                            case Block.BlockType.Ice:
                                if (Settings.Random.NextDouble() > 0.05) continue; //give ice forming a very low chance because its a change in transparency and causes lightbox updates and must queue multiple chunks
                                break;
                            default:
                                if (Settings.Random.NextDouble() > 0.18) continue;
                                break;
                        }
                    }
                    else //when only one possible change is left, greatly increase its chance; prevents tons of chunks lingering performing the logic until the final change gets made
                    {
                        if (Settings.Random.NextDouble() > 0.5) continue;
                    }

                    changesMade++;
                    var changePosition = change.Item2;
                    WorldData.PlaceBlock(changePosition, change.Item1);
                    if (Config.IsServer)
                    {
                        addBlocks.Add(new AddBlock(ref changePosition, change.Item1));
                    }
                }
            }
            Settings.ChunkUpdatesDisabled = false;

            //send updates to multiplayer clients
            if (Config.IsServer && addBlocks.Count > 0)
            {
                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti {ConnectedPlayer = player};
                    addBlockMulti.Blocks.AddRange(addBlocks);
                    addBlockMulti.Send();
                }
            }

            if (changesMade == possibleChanges.Count)
            {
                //when all possible changes have been made we can stop GrassGrowing here without waiting for the next iteration to confirm it
                GrassGrowing = false;
                Debug.WriteLine("Grass finished growing in chunk {0} All possible changes made", Coords);
            }
        }
コード例 #4
0
ファイル: Controller.cs プロジェクト: MagistrAVSH/voxelgame
        // ReSharper restore FunctionNeverReturns
        private static void PlayerThread(NetworkPlayer player)
        {
            NetworkStream clientStream;

            //make all the introductions. we do this before sending the world so the client doesn't see them as new connections
            foreach (var otherPlayer in Players.Values)
            {
                try
                {
                    new Connect(otherPlayer.Id, otherPlayer.UserName, otherPlayer.Coords) { ConnectedPlayer = player, Immediate = true }.Send();
                }
                catch (Exception ex)
                {
                    WriteToServerConsoleLog(string.Format("{0} {1} caused an exception and was removed: {2}", player.UserName, player.IpAddress, ex.Message));
            #if DEBUG
                    WriteToServerConsoleLog(ex.StackTrace);
            #endif
                }

                new Connect(player.Id, player.UserName, player.Coords) { ConnectedPlayer = otherPlayer }.Send();
            }

            try
            {
                Players.TryAdd(player.Id, player); //note: it is not possible for the add to fail on ConcurrentDictionary, see: http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections
                UpdateServerConsolePlayerList();

                var getWorld = new GetWorld { ConnectedPlayer = player };
                getWorld.Send();
                WriteToServerConsoleLog(String.Format("World send complete to {0} ({1} compressed, {2} uncompressed)", player.IpAddress, getWorld.DataLength, getWorld.UncompressedLength));

                //create a thread to handle communication with connected client
                player.TcpClient.NoDelay = true;
                clientStream = player.TcpClient.GetStream();
            }
            catch (Exception ex)
            {
                HandleNetworkError(player, ex);
                return;
            }

            var actionTypebytes = new byte[sizeof(ushort)];
            try
            {
                if (!string.IsNullOrWhiteSpace(Config.MOTD)) new ServerMsg(Config.MOTD, player).Send();

                while (true)
                {
                    Thread.Sleep(10); //bm: polling is expensive. don't remove this or the server will pin your machine when only a couple users are online
                    GameAction gameAction;
                    while (player.SendQueue.Count > 0 && player.SendQueue.TryDequeue(out gameAction))
                    {
                        gameAction.Immediate = true;
                        gameAction.Send();
                    }

                    if (!clientStream.DataAvailable) continue;
                    var bytesRead = 0;
                    while (bytesRead < actionTypebytes.Length) bytesRead += clientStream.Read(actionTypebytes, bytesRead, actionTypebytes.Length - bytesRead);
                    var actionType = (ActionType)BitConverter.ToUInt16(actionTypebytes, 0);
                    switch (actionType)
                    {
                        case ActionType.AddBlock:
                            gameAction = new AddBlock();
                            break;
                        case ActionType.AddBlockItem:
                            gameAction = new AddBlockItem();
                            break;
                        case ActionType.AddBlockMulti:
                            gameAction = new AddBlockMulti();
                            break;
                        case ActionType.AddCuboid:
                            gameAction = new AddCuboid();
                            break;
                        case ActionType.AddProjectile:
                            gameAction = new AddProjectile();
                            break;
                        case ActionType.AddStaticItem:
                            gameAction = new AddStaticItem();
                            break;
                        case ActionType.AddStructure:
                            gameAction = new AddStructure();
                            break;
                        case ActionType.ChatMsg:
                            gameAction = new ChatMsg();
                            break;
                        case ActionType.Disconnect:
                            gameAction = new Disconnect();
                            break;
                        case ActionType.PickupBlockItem:
                            gameAction = new PickupBlockItem();
                            break;
                        case ActionType.PlayerInfo:
                            gameAction = new PlayerInfo();
                            break;
                        case ActionType.PlayerMove:
                            gameAction = new PlayerMove();
                            break;
                        case ActionType.PlayerOption:
                            gameAction = new PlayerOption();
                            break;
                        case ActionType.RemoveBlock:
                            gameAction = new RemoveBlock();
                            break;
                        case ActionType.RemoveBlockItem:
                            gameAction = new RemoveBlockItem();
                            break;
                        case ActionType.RemoveBlockMulti:
                            gameAction = new RemoveBlockMulti();
                            break;
                        case ActionType.ServerCommand:
                            gameAction = new ServerCommand();
                            break;
                        case ActionType.Connect:
                        case ActionType.ServerMsg:
                        case ActionType.ServerSync:
                        case ActionType.GetWorld:
                            throw new Exception(string.Format("Server should not receive action type: {0}", actionType));
                        case ActionType.Error:
                            var bytes = 0;
                            while (clientStream.ReadByte() != -1)
                            {
                                bytes++;
                            }
                            throw new Exception("GameAction 'Error' received. " + bytes + " byte(s) remained in the stream.");
                        default:
                            throw new Exception(string.Format("Unknown action type: {0}", actionType));
                    }
                    gameAction.ConnectedPlayer = player;
                    gameAction.Receive();
                    if (HasServerConsole && CaptureIncoming) //only stream messages if there is a console window and it has requested to display them
                    {
                        _serverConsole.UpdateStreamLogInvokable(gameAction, player, false);
                    }
                    if (actionType == ActionType.Disconnect) return;
                }
            }
            catch (Exception ex)
            {
                HandleNetworkError(player, ex);
            }
        }
コード例 #5
0
ファイル: Chunk.cs プロジェクト: MagistrAVSH/voxelgame
        /// <summary>Only called for SinglePlayer and Servers.</summary>
        private void WaterExpand()
        {
            Debug.WriteLine("Water expanding in chunk {0}...", Coords);
            var newWater = new List<Position>();
            for (var i = 0; i < CHUNK_SIZE; i++)
            {
                for (var j = 0; j < CHUNK_HEIGHT; j++)
                {
                    for (var k = 0; k < CHUNK_SIZE; k++)
                    {
                        if (Blocks[i, j, k].Type != Block.BlockType.Water) continue;
                        var belowCurrent = new Position();
                        for (var q = 0; q < 5; q++)
                        {
                            Position adjacent;
                            switch (q)
                            {
                                case 0:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j - 1, Coords.WorldCoordsZ + k);
                                    belowCurrent = adjacent;
                                    break;
                                case 1:
                                    adjacent = new Position(Coords.WorldCoordsX + i + 1, j, Coords.WorldCoordsZ + k);
                                    break;
                                case 2:
                                    adjacent = new Position(Coords.WorldCoordsX + i - 1, j, Coords.WorldCoordsZ + k);
                                    break;
                                case 3:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j, Coords.WorldCoordsZ + k + 1);
                                    break;
                                default:
                                    adjacent = new Position(Coords.WorldCoordsX + i, j, Coords.WorldCoordsZ + k - 1);
                                    break;
                            }

                            if (newWater.Contains(adjacent)) continue;

                            //if there's air or water below the current block, don't spread sideways
                            if (q != 0 && belowCurrent.IsValidBlockLocation && (Blocks[belowCurrent].Type == Block.BlockType.Air || Blocks[belowCurrent].Type == Block.BlockType.Water)) continue;
                            if (adjacent.IsValidBlockLocation && adjacent.GetBlock().Type == Block.BlockType.Air) newWater.Add(adjacent);
                        }
                    }
                }
            }

            if (newWater.Count == 0)
            {
                WaterExpanding = false;
                Debug.WriteLine("Water finished expanding in chunk {0}", Coords);
                return;
            }

            var addBlocks = new List<AddBlock>();
            Settings.ChunkUpdatesDisabled = true; //change blocks while updates are disabled so chunk is only rebuilt once
            foreach (var newWaterPosition in newWater.Where(newWaterCoords => newWaterCoords.GetBlock().Type != Block.BlockType.Water))
            {
                WorldData.PlaceBlock(newWaterPosition, Block.BlockType.Water);
                if (Config.IsServer)
                {
                    var temp = newWaterPosition;
                    addBlocks.Add(new AddBlock(ref temp, Block.BlockType.Water));
                }
            }
            Settings.ChunkUpdatesDisabled = false;

            if (Config.IsServer && addBlocks.Count > 0)
            {
                foreach (var player in Server.Controller.Players.Values)
                {
                    var addBlockMulti = new AddBlockMulti {ConnectedPlayer = player};
                    addBlockMulti.Blocks.AddRange(addBlocks);
                    addBlockMulti.Send();
                }
            }
        }