<commit_before>﻿using System;
using TrueCraft.API.Server;
using TrueCraft.API.Networking;
using TrueCraft.Core.Networking;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using TrueCraft.API.World;
using TrueCraft.API.Logging;
using TrueCraft.Core.Networking.Packets;
using TrueCraft.API;
using TrueCraft.Core.Logging;
using TrueCraft.API.Logic;
using TrueCraft.Exceptions;
using TrueCraft.Core.Logic;
using TrueCraft.Core.Lighting;
using TrueCraft.Core.World;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Collections.Concurrent;
using TrueCraft.Profiling;

namespace TrueCraft
{
    public class MultiplayerServer : IMultiplayerServer, IDisposable
    {
        public event EventHandler<ChatMessageEventArgs> ChatMessageReceived;
        public event EventHandler<PlayerJoinedQuitEventArgs> PlayerJoined;
        public event EventHandler<PlayerJoinedQuitEventArgs> PlayerQuit;

        public IAccessConfiguration AccessConfiguration { get; internal set; }

        public IPacketReader PacketReader { get; private set; }
        public IList<IRemoteClient> Clients { get; private set; }
        public IList<IWorld> Worlds { get; private set; }
        public IList<IEntityManager> EntityManagers { get; private set; }
        public IList<WorldLighting> WorldLighters { get; set; }
        public IEventScheduler Scheduler { get; private set; }
        public IBlockRepository BlockRepository { get; private set; }
        public IItemRepository ItemRepository { get; private set; }
        public ICraftingRepository CraftingRepository { get; private set; }
        public bool EnableClientLogging { get; set; }
        public IPEndPoint EndPoint { get; private set; }

        private static readonly int MillisecondsPerTick = 1000 / 20;

        private bool _BlockUpdatesEnabled = true;

        private struct BlockUpdate
        {
            public Coordinates3D Coordinates;
            public IWorld World;
        }
        private Queue<BlockUpdate> PendingBlockUpdates { get; set; }
        public bool BlockUpdatesEnabled
        {
            get
            {
                return _BlockUpdatesEnabled;
            }
            set
            {
                _BlockUpdatesEnabled = value;
                if (_BlockUpdatesEnabled)
                {
                    ProcessBlockUpdates();
                }
            }
        }

        private Timer EnvironmentWorker;
        private TcpListener Listener;
        private readonly PacketHandler[] PacketHandlers;
        private IList<ILogProvider> LogProviders;
        private ConcurrentBag<Tuple<IWorld, IChunk>> ChunksToSchedule;
        internal object ClientLock = new object();
        
        private QueryProtocol QueryProtocol;

        internal bool ShuttingDown { get; private set; }
        
        public MultiplayerServer()
        {
            var reader = new PacketReader();
            PacketReader = reader;
            Clients = new List<IRemoteClient>();
            EnvironmentWorker = new Timer(DoEnvironment);
            PacketHandlers = new PacketHandler[0x100];
            Worlds = new List<IWorld>();
            EntityManagers = new List<IEntityManager>();
            LogProviders = new List<ILogProvider>();
            Scheduler = new EventScheduler(this);
            var blockRepository = new BlockRepository();
            blockRepository.DiscoverBlockProviders();
            BlockRepository = blockRepository;
            var itemRepository = new ItemRepository();
            itemRepository.DiscoverItemProviders();
            ItemRepository = itemRepository;
            BlockProvider.ItemRepository = ItemRepository;
            BlockProvider.BlockRepository = BlockRepository;
            var craftingRepository = new CraftingRepository();
            craftingRepository.DiscoverRecipes();
            CraftingRepository = craftingRepository;
            PendingBlockUpdates = new Queue<BlockUpdate>();
            EnableClientLogging = false;
            QueryProtocol = new TrueCraft.QueryProtocol(this);
            WorldLighters = new List<WorldLighting>();
            ChunksToSchedule = new ConcurrentBag<Tuple<IWorld, IChunk>>();

            AccessConfiguration = Configuration.LoadConfiguration<AccessConfiguration>("access.yaml");

            reader.RegisterCorePackets();
            Handlers.PacketHandlers.RegisterHandlers(this);
        }

        public void RegisterPacketHandler(byte packetId, PacketHandler handler)
        {
            PacketHandlers[packetId] = handler;
        }

        public void Start(IPEndPoint endPoint)
        {
            ShuttingDown = false;
            Listener = new TcpListener(endPoint);
            Listener.Start();
            EndPoint = (IPEndPoint)Listener.LocalEndpoint;

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.Completed += AcceptClient;

            if (!Listener.Server.AcceptAsync(args))
                AcceptClient(this, args);
            
            Log(LogCategory.Notice, "Running TrueCraft server on {0}", EndPoint);
            EnvironmentWorker.Change(MillisecondsPerTick, 0);
            if(Program.ServerConfiguration.Query)
                QueryProtocol.Start();
        }

        public void Stop()
        {
            ShuttingDown = true;
            Listener.Stop();
            if(Program.ServerConfiguration.Query)
                QueryProtocol.Stop();
            foreach (var w in Worlds)
                w.Save();
            foreach (var c in Clients)
                DisconnectClient(c);
        }

        public void AddWorld(IWorld world)
        {
            Worlds.Add(world);
            world.BlockRepository = BlockRepository;
            world.ChunkGenerated += HandleChunkGenerated;
            world.ChunkLoaded += HandleChunkLoaded;
            world.BlockChanged += HandleBlockChanged;
            var manager = new EntityManager(this, world);
            EntityManagers.Add(manager);
            var lighter = new WorldLighting(world, BlockRepository);
            WorldLighters.Add(lighter);
            foreach (var chunk in world)
                HandleChunkLoaded(world, new ChunkLoadedEventArgs(chunk));
        }

        void HandleChunkLoaded(object sender, ChunkLoadedEventArgs e)
        {
            ChunksToSchedule.Add(new Tuple<IWorld, IChunk>(sender as IWorld, e.Chunk));
        }

        void HandleBlockChanged(object sender, BlockChangeEventArgs e)
        {
            // TODO: Propegate lighting changes to client (not possible with beta 1.7.3 protocol)
            if (e.NewBlock.ID != e.OldBlock.ID || e.NewBlock.Metadata != e.OldBlock.Metadata)
            {
                for (int i = 0, ClientsCount = Clients.Count; i < ClientsCount; i++)
                {
                    var client = (RemoteClient)Clients[i];
                    // TODO: Confirm that the client knows of this block
                    if (client.LoggedIn && client.World == sender)
                    {
                        client.QueuePacket(new BlockChangePacket(e.Position.X, (sbyte)e.Position.Y, e.Position.Z,
                                (sbyte)e.NewBlock.ID, (sbyte)e.NewBlock.Metadata));
                    }
                }
                PendingBlockUpdates.Enqueue(new BlockUpdate { Coordinates = e.Position, World = sender as IWorld });
                ProcessBlockUpdates();
                if (Program.ServerConfiguration.EnableLighting)
                {
                    var lighter = WorldLighters.SingleOrDefault(l => l.World == sender);
                    if (lighter != null)
                    {
                        lighter.EnqueueOperation(new BoundingBox(e.Position, e.Position + Vector3.One), true);
                        lighter.EnqueueOperation(new BoundingBox(e.Position, e.Position + Vector3.One), false);
                    }
                }
            }
        }

        void HandleChunkGenerated(object sender, ChunkLoadedEventArgs e)
        {
            if (Program.ServerConfiguration.EnableLighting)
            {<commit_msg>Fix light changes through transparent sources<commit_after>﻿using System;
using TrueCraft.API.Server;
using TrueCraft.API.Networking;
using TrueCraft.Core.Networking;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using TrueCraft.API.World;
using TrueCraft.API.Logging;
using TrueCraft.Core.Networking.Packets;
using TrueCraft.API;
using TrueCraft.Core.Logging;
using TrueCraft.API.Logic;
using TrueCraft.Exceptions;
using TrueCraft.Core.Logic;
using TrueCraft.Core.Lighting;
using TrueCraft.Core.World;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Collections.Concurrent;
using TrueCraft.Profiling;

namespace TrueCraft
{
    public class MultiplayerServer : IMultiplayerServer, IDisposable
    {
        public event EventHandler<ChatMessageEventArgs> ChatMessageReceived;
        public event EventHandler<PlayerJoinedQuitEventArgs> PlayerJoined;
        public event EventHandler<PlayerJoinedQuitEventArgs> PlayerQuit;

        public IAccessConfiguration AccessConfiguration { get; internal set; }

        public IPacketReader PacketReader { get; private set; }
        public IList<IRemoteClient> Clients { get; private set; }
        public IList<IWorld> Worlds { get; private set; }
        public IList<IEntityManager> EntityManagers { get; private set; }
        public IList<WorldLighting> WorldLighters { get; set; }
        public IEventScheduler Scheduler { get; private set; }
        public IBlockRepository BlockRepository { get; private set; }
        public IItemRepository ItemRepository { get; private set; }
        public ICraftingRepository CraftingRepository { get; private set; }
        public bool EnableClientLogging { get; set; }
        public IPEndPoint EndPoint { get; private set; }

        private static readonly int MillisecondsPerTick = 1000 / 20;

        private bool _BlockUpdatesEnabled = true;

        private struct BlockUpdate
        {
            public Coordinates3D Coordinates;
            public IWorld World;
        }
        private Queue<BlockUpdate> PendingBlockUpdates { get; set; }
        public bool BlockUpdatesEnabled
        {
            get
            {
                return _BlockUpdatesEnabled;
            }
            set
            {
                _BlockUpdatesEnabled = value;
                if (_BlockUpdatesEnabled)
                {
                    ProcessBlockUpdates();
                }
            }
        }

        private Timer EnvironmentWorker;
        private TcpListener Listener;
        private readonly PacketHandler[] PacketHandlers;
        private IList<ILogProvider> LogProviders;
        private ConcurrentBag<Tuple<IWorld, IChunk>> ChunksToSchedule;
        internal object ClientLock = new object();
        
        private QueryProtocol QueryProtocol;

        internal bool ShuttingDown { get; private set; }
        
        public MultiplayerServer()
        {
            var reader = new PacketReader();
            PacketReader = reader;
            Clients = new List<IRemoteClient>();
            EnvironmentWorker = new Timer(DoEnvironment);
            PacketHandlers = new PacketHandler[0x100];
            Worlds = new List<IWorld>();
            EntityManagers = new List<IEntityManager>();
            LogProviders = new List<ILogProvider>();
            Scheduler = new EventScheduler(this);
            var blockRepository = new BlockRepository();
            blockRepository.DiscoverBlockProviders();
            BlockRepository = blockRepository;
            var itemRepository = new ItemRepository();
            itemRepository.DiscoverItemProviders();
            ItemRepository = itemRepository;
            BlockProvider.ItemRepository = ItemRepository;
            BlockProvider.BlockRepository = BlockRepository;
            var craftingRepository = new CraftingRepository();
            craftingRepository.DiscoverRecipes();
            CraftingRepository = craftingRepository;
            PendingBlockUpdates = new Queue<BlockUpdate>();
            EnableClientLogging = false;
            QueryProtocol = new TrueCraft.QueryProtocol(this);
            WorldLighters = new List<WorldLighting>();
            ChunksToSchedule = new ConcurrentBag<Tuple<IWorld, IChunk>>();

            AccessConfiguration = Configuration.LoadConfiguration<AccessConfiguration>("access.yaml");

            reader.RegisterCorePackets();
            Handlers.PacketHandlers.RegisterHandlers(this);
        }

        public void RegisterPacketHandler(byte packetId, PacketHandler handler)
        {
            PacketHandlers[packetId] = handler;
        }

        public void Start(IPEndPoint endPoint)
        {
            ShuttingDown = false;
            Listener = new TcpListener(endPoint);
            Listener.Start();
            EndPoint = (IPEndPoint)Listener.LocalEndpoint;

            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.Completed += AcceptClient;

            if (!Listener.Server.AcceptAsync(args))
                AcceptClient(this, args);
            
            Log(LogCategory.Notice, "Running TrueCraft server on {0}", EndPoint);
            EnvironmentWorker.Change(MillisecondsPerTick, 0);
            if(Program.ServerConfiguration.Query)
                QueryProtocol.Start();
        }

        public void Stop()
        {
            ShuttingDown = true;
            Listener.Stop();
            if(Program.ServerConfiguration.Query)
                QueryProtocol.Stop();
            foreach (var w in Worlds)
                w.Save();
            foreach (var c in Clients)
                DisconnectClient(c);
        }

        public void AddWorld(IWorld world)
        {
            Worlds.Add(world);
            world.BlockRepository = BlockRepository;
            world.ChunkGenerated += HandleChunkGenerated;
            world.ChunkLoaded += HandleChunkLoaded;
            world.BlockChanged += HandleBlockChanged;
            var manager = new EntityManager(this, world);
            EntityManagers.Add(manager);
            var lighter = new WorldLighting(world, BlockRepository);
            WorldLighters.Add(lighter);
            foreach (var chunk in world)
                HandleChunkLoaded(world, new ChunkLoadedEventArgs(chunk));
        }

        void HandleChunkLoaded(object sender, ChunkLoadedEventArgs e)
        {
            ChunksToSchedule.Add(new Tuple<IWorld, IChunk>(sender as IWorld, e.Chunk));
        }

        void HandleBlockChanged(object sender, BlockChangeEventArgs e)
        {
            // TODO: Propegate lighting changes to client (not possible with beta 1.7.3 protocol)
            if (e.NewBlock.ID != e.OldBlock.ID || e.NewBlock.Metadata != e.OldBlock.Metadata)
            {
                for (int i = 0, ClientsCount = Clients.Count; i < ClientsCount; i++)
                {
                    var client = (RemoteClient)Clients[i];
                    // TODO: Confirm that the client knows of this block
                    if (client.LoggedIn && client.World == sender)
                    {
                        client.QueuePacket(new BlockChangePacket(e.Position.X, (sbyte)e.Position.Y, e.Position.Z,
                                (sbyte)e.NewBlock.ID, (sbyte)e.NewBlock.Metadata));
                    }
                }
                PendingBlockUpdates.Enqueue(new BlockUpdate { Coordinates = e.Position, World = sender as IWorld });
                ProcessBlockUpdates();
                if (Program.ServerConfiguration.EnableLighting)
                {
                    var lighter = WorldLighters.SingleOrDefault(l => l.World == sender);
                    if (lighter != null)
                    {
                        var posA = e.Position;
                        posA.Y = 0;
                        var posB = e.Position;
                        posB.Y = World.Height;
                        posB.X++; posB.Z++;
                        lighter.EnqueueOperation(new BoundingBox(posA, posB), true);
                        lighter.EnqueueOperation(new BoundingBox(posA, posB), false);
                    }
                }
            }
        }

        void HandleChunkGenerated(object sender, ChunkLoadedEventArgs e)
        {
            if (Program.ServerConfiguration.EnableLighting)
            {
{'fraction_non_alphanumeric': 0.07775965198477433, 'fraction_numerical': 0.002235514470424748, 'mean_word_length': 2.0432064717779004, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7634162', 'n_tokens_mistral': 4138, 'n_tokens_neox': 3965, 'n_words': 955}