Пример #1
0
        /// <summary>
        /// Starts this server.
        /// </summary>
        public async Task StartServerAsync()
        {
            this.StartTime = DateTimeOffset.Now;

            this.Logger.LogInformation($"Launching Obsidian Server v{Version} with ID {Id}");

            // Check if MPDM and OM are enabled, if so, we can't handle connections
            if (this.Config.MulitplayerDebugMode && this.Config.OnlineMode)
            {
                this.Logger.LogError("Incompatible Config: Multiplayer debug mode can't be enabled at the same time as online mode since usernames will be overwritten");
                this.StopServer();
                return;
            }

            await Registry.RegisterBlocksAsync();

            await Registry.RegisterItemsAsync();

            await Registry.RegisterBiomesAsync();

            await Registry.RegisterDimensionsAsync();

            await Registry.RegisterTagsAsync();

            PacketHandler.RegisterHandlers();

            this.Logger.LogInformation($"Loading properties...");
            await(this.Operators as OperatorList).InitializeAsync();
            await this.RegisterDefaultAsync();

            this.Logger.LogInformation("Loading plugins...");
            Directory.CreateDirectory(Path.Join(ServerFolderPath, "plugins")); // Creates if doesn't exist.
            this.PluginManager.DirectoryWatcher.Filters = new[] { ".cs", ".dll" };
            this.PluginManager.DefaultPermissions       = API.Plugins.PluginPermissions.All;
            this.PluginManager.DirectoryWatcher.Watch(Path.Join(ServerFolderPath, "plugins"));
            await Task.WhenAll(Config.DownloadPlugins.Select(path => PluginManager.LoadPluginAsync(path)));

            this.World = new World("world", this);
            if (!this.World.Load())
            {
                if (!this.WorldGenerators.TryGetValue(this.Config.Generator, out WorldGenerator value))
                {
                    this.Logger.LogWarning($"Unknown generator type {this.Config.Generator}");
                }
                var gen = value ?? new SuperflatGenerator();
                this.Logger.LogInformation($"Creating new {gen.Id} ({gen}) world...");
                this.World.Init(gen);
                // TODO: save world
            }

            if (!this.Config.OnlineMode)
            {
                this.Logger.LogInformation($"Starting in offline mode...");
            }

            _ = Task.Run(this.ServerLoop);

            this.Logger.LogDebug($"Listening for new clients...");

            this.tcpListener.Start();

            while (!this.cts.IsCancellationRequested)
            {
                if (this.tcpListener.Pending())
                {
                    var tcp = await this.tcpListener.AcceptTcpClientAsync();

                    this.Logger.LogDebug($"New connection from client with IP {tcp.Client.RemoteEndPoint}");

                    var client = new Client(tcp, this.Config, Math.Max(0, this.clients.Count + this.World.TotalLoadedEntities()), this);
                    this.clients.Add(client);

                    _ = Task.Run(client.StartConnectionAsync);
                }
                await Task.Delay(50);
            }

            this.Logger.LogWarning("Server is shutting down...");
        }
Пример #2
0
 private Task <Packet> GetNextPacketAsync() => this.compressionEnabled ? PacketHandler.ReadCompressedPacketAsync(this.minecraftStream) : PacketHandler.ReadPacketAsync(this.minecraftStream);
Пример #3
0
        public async Task StartConnectionAsync()
        {
            _ = Task.Run(ProcessQueue);

            while (!Cancellation.IsCancellationRequested && this.tcp.Connected)
            {
                Packet packet = await this.GetNextPacketAsync();

                if (this.State == ClientState.Play && packet.data.Length < 1)
                {
                    this.Disconnect();
                }

                switch (this.State)
                {
                case ClientState.Status:     //server ping/list
                    switch (packet.id)
                    {
                    case 0x00:
                        var status = new ServerStatus(Server);
                        await this.SendPacketAsync(new RequestResponse(status));

                        break;

                    case 0x01:
                        await this.SendPacketAsync(new PingPong(packet.data));

                        this.Disconnect();
                        break;
                    }
                    break;

                case ClientState.Handshaking:
                    if (packet.id == 0x00)
                    {
                        if (packet == null)
                        {
                            throw new InvalidOperationException();
                        }

                        var handshake = await PacketSerializer.FastDeserializeAsync <Handshake>(packet.data);

                        var nextState = handshake.NextState;

                        if (nextState != ClientState.Status && nextState != ClientState.Login)
                        {
                            this.Logger.LogDebug($"Client sent unexpected state ({(int)nextState}), forcing it to disconnect");
                            await this.DisconnectAsync(ChatMessage.Simple("you seem suspicious"));
                        }

                        this.State = nextState;
                        this.Logger.LogInformation($"Handshaking with client (protocol: {handshake.Version}, server: {handshake.ServerAddress}:{handshake.ServerPort})");
                    }
                    else
                    {
                        //Handle legacy ping stuff
                    }
                    break;

                case ClientState.Login:
                    switch (packet.id)
                    {
                    default:
                        this.Logger.LogError("Client in state Login tried to send an unimplemented packet. Forcing it to disconnect.");
                        await this.DisconnectAsync("Unknown Packet Id.");

                        break;

                    case 0x00:
                        var loginStart = await PacketSerializer.FastDeserializeAsync <LoginStart>(packet.data);

                        string username = config.MulitplayerDebugMode ? $"Player{Program.Random.Next(1, 999)}" : loginStart.Username;

                        this.Logger.LogDebug($"Received login request from user {loginStart.Username}");

                        await this.Server.DisconnectIfConnectedAsync(username);

                        if (this.config.OnlineMode)
                        {
                            var user = await MinecraftAPI.GetUserAsync(loginStart.Username);

                            this.Player = new Player(Guid.Parse(user.Id), loginStart.Username, this)
                            {
                                World = this.Server.World
                            };

                            this.packetCryptography.GenerateKeyPair();

                            var values = this.packetCryptography.GeneratePublicKeyAndToken();

                            this.randomToken = values.randomToken;

                            await this.SendPacketAsync(new EncryptionRequest(values.publicKey, this.randomToken));

                            break;
                        }

                        this.Player = new Player(UUIDFactory.CreateUUID(3, 1, $"OfflinePlayer:{username}"), username, this)
                        {
                            World = this.Server.World
                        };

                        //await this.SetCompression();
                        await this.ConnectAsync();

                        break;

                    case 0x01:
                        var encryptionResponse = PacketSerializer.FastDeserialize <EncryptionResponse>(packet.data);

                        this.sharedKey = this.packetCryptography.Decrypt(encryptionResponse.SharedSecret);
                        var decryptedToken = this.packetCryptography.Decrypt(encryptionResponse.VerifyToken);

                        if (!decryptedToken.SequenceEqual(this.randomToken))
                        {
                            await this.DisconnectAsync("Invalid token..");

                            break;
                        }

                        var serverId = sharedKey.Concat(this.packetCryptography.PublicKey).ToArray().MinecraftShaDigest();

                        JoinedResponse response = await MinecraftAPI.HasJoined(this.Player.Username, serverId);

                        if (response is null)
                        {
                            this.Logger.LogWarning($"Failed to auth {this.Player.Username}");
                            await this.DisconnectAsync("Unable to authenticate..");

                            break;
                        }

                        this.encryptionEnabled = true;
                        this.minecraftStream   = new AesStream(this.debugStream ?? (Stream)this.tcp.GetStream(), this.sharedKey);

                        //await this.SetCompression();
                        await ConnectAsync();

                        break;

                    case 0x02:
                        // Login Plugin Response
                        break;
                    }
                    break;

                case ClientState.Play:

                    //await this.Logger.LogDebugAsync($"Received Play packet with Packet ID 0x{packet.id.ToString("X")}");

                    await PacketHandler.HandlePlayPackets(packet, this);

                    break;
                }

                //await Task.Delay(50);
            }

            Logger.LogInformation($"Disconnected client");

            if (this.State == ClientState.Play)
            {
                await this.Server.Events.InvokePlayerLeaveAsync(new PlayerLeaveEventArgs(this.Player));
            }

            if (tcp.Connected)
            {
                this.tcp.Close();

                if (this.Player != null)
                {
                    this.Server.OnlinePlayers.TryRemove(this.Player.Uuid, out var _);
                }
            }
        }
Пример #4
0
        private async Task ConnectAsync(Guid uuid)
        {
            await PacketHandler.CreateAsync(new LoginSuccess(uuid, this.Player.Username), this.MinecraftStream);

            this.Logger.LogDebug($"Sent Login success to user {this.Player.Username} {this.Player.Uuid.ToString()}");

            this.State           = ClientState.Play;
            this.Player.Gamemode = Gamemode.Creative;

            await PacketHandler.CreateAsync(new JoinGame((int)(EntityId.Player | (EntityId)this.PlayerId), Gamemode.Creative, 0, 0, "default", true), this.MinecraftStream);

            this.Logger.LogDebug("Sent Join Game packet.");

            await PacketHandler.CreateAsync(new SpawnPosition(new Position(0, 100, 0)), this.MinecraftStream);

            this.Logger.LogDebug("Sent Spawn Position packet.");

            await PacketHandler.CreateAsync(new PlayerPositionLook(new Transform(0, 105, 0), PositionFlags.NONE, 0), this.MinecraftStream);

            this.Logger.LogDebug("Sent Position packet.");

            using (var stream = new MinecraftStream())
            {
                await stream.WriteStringAsync("obsidian");

                await PacketHandler.CreateAsync(new PluginMessage("minecraft:brand", stream.ToArray()), this.MinecraftStream);
            }
            this.Logger.LogDebug("Sent server brand.");

            this.OriginServer.Broadcast(string.Format(this.Config.JoinMessage, this.Player.Username));
            await this.OriginServer.Events.InvokePlayerJoin(new PlayerJoinEventArgs(this, DateTimeOffset.Now));

            await this.SendDeclareCommandsAsync();

            await this.SendPlayerInfoAsync();

            await this.SendPlayerListHeaderFooterAsync(string.IsNullOrWhiteSpace(OriginServer.Config.Header)?null : ChatMessage.Simple(OriginServer.Config.Header),
                                                       string.IsNullOrWhiteSpace(OriginServer.Config.Footer)?null : ChatMessage.Simple(OriginServer.Config.Footer));

            this.Logger.LogDebug("Sent player list decoration");

            await this.SendChunkAsync(OriginServer.WorldGenerator.GenerateChunk(new Chunk(0, 0)));

            await this.SendChunkAsync(OriginServer.WorldGenerator.GenerateChunk(new Chunk(-1, 0)));

            await this.SendChunkAsync(OriginServer.WorldGenerator.GenerateChunk(new Chunk(0, -1)));

            await this.SendChunkAsync(OriginServer.WorldGenerator.GenerateChunk(new Chunk(-1, -1)));

            //await OriginServer.world.resendBaseChunksAsync(10, 0, 0, 0, 0, this);

            this.Logger.LogDebug("Sent chunk");

            /*if (this.OriginServer.Config.OnlineMode)
             * {
             *  await this.OriginServer.SendNewPlayer(this.PlayerId, this.Player.Uuid, new Transform
             *  {
             *      X = 0,
             *
             *      Y = 105,
             *
             *      Z = 0,
             *
             *      Pitch = 1,
             *
             *      Yaw = 1
             *  });
             * }
             * else
             * {
             *  await this.OriginServer.SendNewPlayer(this.PlayerId, this.Player.Uuid3, new Transform
             *  {
             *      X = 0,
             *
             *      Y = 105,
             *
             *      Z = 0,
             *
             *      Pitch = 1,
             *
             *      Yaw = 1
             *  });
             * }*/
        }
Пример #5
0
 internal async Task DisconnectAsync(ChatMessage reason)
 {
     await PacketHandler.CreateAsync(new Disconnect(reason, this.State), this.MinecraftStream);
 }
Пример #6
0
 private async Task <Packet> GetNextPacketAsync()
 {
     return(await PacketHandler.ReadFromStreamAsync(this.MinecraftStream));
 }
Пример #7
0
        public async Task StartConnectionAsync()
        {
            while (!Cancellation.IsCancellationRequested && this.Tcp.Connected)
            {
                Packet packet = this.Compressed ? await this.GetNextCompressedPacketAsync() : await this.GetNextPacketAsync();

                Packet returnPacket;

                if (this.State == ClientState.Play && packet.PacketData.Length < 1)
                {
                    this.Disconnect();
                }

                switch (this.State)
                {
                case ClientState.Status:     //server ping/list
                    switch (packet.PacketId)
                    {
                    case 0x00:
                        var status = new ServerStatus(OriginServer);
                        await PacketHandler.CreateAsync(new RequestResponse(status), this.MinecraftStream);

                        break;

                    case 0x01:
                        await PacketHandler.CreateAsync(new PingPong(packet.PacketData), this.MinecraftStream);

                        this.Disconnect();
                        break;
                    }
                    break;

                case ClientState.Handshaking:
                    if (packet.PacketId == 0x00)
                    {
                        if (packet == null)
                        {
                            throw new InvalidOperationException();
                        }

                        var handshake = await PacketHandler.CreateAsync(new Handshake(packet.PacketData));

                        var nextState = handshake.NextState;

                        if (nextState != ClientState.Status && nextState != ClientState.Login)
                        {
                            this.Logger.LogDebug($"Client sent unexpected state ({(int)nextState}), forcing it to disconnect");
                            await this.DisconnectAsync(Chat.ChatMessage.Simple("you seem suspicious"));
                        }

                        this.State = nextState;
                        this.Logger.LogMessage($"Handshaking with client (protocol: {handshake.Version}, server: {handshake.ServerAddress}:{handshake.ServerPort})");
                    }
                    else
                    {
                        //Handle legacy ping stuff
                    }
                    break;

                case ClientState.Login:
                    switch (packet.PacketId)
                    {
                    default:
                        this.Logger.LogError("Client in state Login tried to send an unimplemented packet. Forcing it to disconnect.");
                        await this.DisconnectAsync(ChatMessage.Simple("Unknown Packet Id."));

                        break;

                    case 0x00:
                        var loginStart = await PacketHandler.CreateAsync(new LoginStart(packet.PacketData));

                        string username = loginStart.Username;

                        if (Config.MulitplayerDebugMode)
                        {
                            username = $"Player{new Random().Next(1, 999)}";
                            this.Logger.LogDebug($"Overriding username from {loginStart.Username} to {username}");
                        }

                        this.Logger.LogDebug($"Received login request from user {loginStart.Username}");

                        if (this.OriginServer.CheckPlayerOnline(username))
                        {
                            await this.OriginServer.Clients.FirstOrDefault(c => c.Player.Username == username).DisconnectAsync(Chat.ChatMessage.Simple("Logged in from another location"));
                        }

                        if (this.Config.OnlineMode)
                        {
                            var users = await MinecraftAPI.GetUsersAsync(new string[] { loginStart.Username });

                            var uid = users.FirstOrDefault();

                            var uuid = Guid.Parse(uid.Id);
                            this.Player = new Player(uuid, loginStart.Username, this);

                            PacketCryptography.GenerateKeyPair();

                            var pubKey = PacketCryptography.PublicKeyToAsn();

                            this.Token = PacketCryptography.GetRandomToken();

                            returnPacket = await PacketHandler.CreateAsync(new EncryptionRequest(pubKey, this.Token), this.MinecraftStream);

                            break;
                        }

                        this.Player = new Player(Guid.NewGuid(), username, this);
                        await ConnectAsync(this.Player.Uuid);

                        break;

                    case 0x01:
                        var encryptionResponse = await PacketHandler.CreateAsync(new EncryptionResponse(packet.PacketData));

                        JoinedResponse response;

                        this.SharedKey = PacketCryptography.Decrypt(encryptionResponse.SharedSecret);

                        var dec2 = PacketCryptography.Decrypt(encryptionResponse.VerifyToken);

                        var dec2Base64 = Convert.ToBase64String(dec2);

                        var tokenBase64 = Convert.ToBase64String(this.Token);

                        if (!dec2Base64.Equals(tokenBase64))
                        {
                            await this.DisconnectAsync(Chat.ChatMessage.Simple("Invalid token.."));

                            break;
                        }

                        var encodedKey = PacketCryptography.PublicKeyToAsn();

                        var serverId = PacketCryptography.MinecraftShaDigest(SharedKey.Concat(encodedKey).ToArray());

                        response = await MinecraftAPI.HasJoined(this.Player.Username, serverId);

                        if (response is null)
                        {
                            this.Logger.LogWarning($"Failed to auth {this.Player.Username}");
                            await this.DisconnectAsync(Chat.ChatMessage.Simple("Unable to authenticate.."));

                            break;
                        }
                        this.EncryptionEnabled = true;
                        this.MinecraftStream   = new AesStream(this.Tcp.GetStream(), this.SharedKey);

                        await ConnectAsync(new Guid(response.Id));

                        break;

                    case 0x02:
                        // Login Plugin Response
                        break;
                    }
                    break;

                case ClientState.Play:

                    ///this.Logger.LogDebugAsync($"Received Play packet with Packet ID 0x{packet.PacketId.ToString("X")}");

                    await PacketHandler.HandlePlayPackets(packet, this);

                    break;
                }
            }

            Logger.LogMessage($"Disconnected client");

            if (this.IsPlaying)
            {
                await this.OriginServer.Events.InvokePlayerLeave(new PlayerLeaveEventArgs(this));
            }

            this.OriginServer.Broadcast(string.Format(this.Config.LeaveMessage, this.Player.Username));

            this.Player = null;

            if (Tcp.Connected)
            {
                this.Tcp.Close();
            }
        }
Пример #8
0
        internal async Task SendPlayerListHeaderFooterAsync(ChatMessage header, ChatMessage footer)
        {
            await PacketHandler.CreateAsync(new PlayerListHeaderFooter(header, footer), this.MinecraftStream);

            this.Logger.LogDebug("Sent Player List Footer Header packet.");
        }
Пример #9
0
        internal async Task SendDeclareCommandsAsync()
        {
            var packet = new DeclareCommands();

            var node = new CommandNode()
            {
                Type = CommandNodeType.Root
            };

            foreach (Qmmands.Command command in this.OriginServer.Commands.GetAllCommands())
            {
                var commandNode = new CommandNode()
                {
                    Name = command.Name,
                    Type = CommandNodeType.Literal
                };

                foreach (Qmmands.Parameter parameter in command.Parameters)
                {
                    var parameterNode = new CommandNode()
                    {
                        Name = parameter.Name,
                        Type = CommandNodeType.Argument,
                    };

                    Type type = parameter.Type;

                    if (type == typeof(string))
                    {
                        parameterNode.Parser = new StringCommandParser(parameter.IsRemainder ? StringType.GreedyPhrase : StringType.QuotablePhrase);
                    }
                    else if (type == typeof(double))
                    {
                        parameterNode.Parser = new EmptyFlagsCommandParser("brigadier:double");
                    }
                    else if (type == typeof(float))
                    {
                        parameterNode.Parser = new EmptyFlagsCommandParser("brigadier:float");
                    }
                    else if (type == typeof(int))
                    {
                        parameterNode.Parser = new EmptyFlagsCommandParser("brigadier:integer");
                    }
                    else if (type == typeof(bool))
                    {
                        parameterNode.Parser = new CommandParser("brigadier:bool");
                    }
                    else
                    {
                        continue;
                    }

                    commandNode.Children.Add(parameterNode);
                }

                if (commandNode.Children.Count > 0)
                {
                    commandNode.Children[0].Type |= CommandNodeType.IsExecutabe;
                }
                else
                {
                    commandNode.Type |= CommandNodeType.IsExecutabe;
                }

                node.Children.Add(commandNode);

                packet.AddNode(node);
            }

            await PacketHandler.CreateAsync(packet, this.MinecraftStream);

            this.Logger.LogDebug("Sent Declare Commands packet.");
        }
Пример #10
0
        internal async Task SendEntity(EntityPacket packet)
        {
            await PacketHandler.CreateAsync(packet, this.MinecraftStream);

            this.Logger.LogDebug($"Sent entity with id {packet.Id} for player {this.Player.Username}");
        }
Пример #11
0
        internal async Task SendSpawnMobAsync(int id, Guid uuid, int type, Transform transform, byte headPitch, Velocity velocity, Entity entity)
        {
            await PacketHandler.CreateAsync(new SpawnMob(id, uuid, type, transform, headPitch, velocity, entity), this.MinecraftStream);

            this.Logger.LogDebug($"Spawned entity with id {id} for player {this.Player.Username}");
        }
Пример #12
0
 internal async Task SendPlayerLookPositionAsync(Transform poslook, PositionFlags posflags, int tpid = 0)
 {
     await PacketHandler.CreateAsync(new PlayerPositionLook(poslook, posflags, tpid), this.MinecraftStream);
 }