Skip to content

Commit

Permalink
Server refactor (#403)
Browse files Browse the repository at this point in the history
* Turn world manager into a BackgroundService

* Abstractions refactor pt.1

* Update Region.cs

* Abstractions refactor pt.2

* Move world generator registering to WorldManager

* abstractions pt.3

* Use packet broadcaster

* Fix event parameters

* Simplify ConsoleApp.Program.cs

* Refactor Living.cs

* A little cleanup

* WE START WOOOOO

* Update Player.cs

* oop

* Fix up some NREs and wait for worlds to be ready

* Region loading does not need to be blocked

* Add a direct send method that won't get processed through the queue

* Just use JsonNamingPolicy.SnakeCaseLower :))

* world gen status

* Fix strange chunk unloading bug

* Forgot to save O_O

* Update OperatorList & IOperatorList

* Last bit refactor

* Make user cache a service instead

* Add sealed modifier to most classes (final commit)

* OKAY ACTUAL FINAL COMMIT

* Fix bug loading worlds from disk

* Document the non self explanatory properties in config

---------

Co-authored-by: Jon Pro <[email protected]>
  • Loading branch information
Tides and Jonpro03 authored Nov 24, 2023
1 parent 5f8dfc7 commit 0ad986f
Show file tree
Hide file tree
Showing 87 changed files with 1,119 additions and 951 deletions.
2 changes: 1 addition & 1 deletion Obsidian.API/Events/ContainerClickEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public sealed class ContainerClickEventArgs : ContainerEventArgs, ICancellable
public bool IsCancelled { get; private set; }

[SetsRequiredMembers]
internal ContainerClickEventArgs(IPlayer player, BaseContainer container, ItemStack item) : base(player)
internal ContainerClickEventArgs(IPlayer player, IServer server, BaseContainer container, ItemStack item) : base(player, server)
{
this.Container = container;
this.Item = item;
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/ContainerClosedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ public sealed class ContainerClosedEventArgs : ContainerEventArgs, ICancellable
{
public bool IsCancelled { get; private set; }

internal ContainerClosedEventArgs(IPlayer player) : base(player)
internal ContainerClosedEventArgs(IPlayer player, IServer server) : base(player, server)
{
}

Expand Down
6 changes: 2 additions & 4 deletions Obsidian.API/Events/ContainerEventArgs.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using System.Diagnostics.CodeAnalysis;

namespace Obsidian.API.Events;
namespace Obsidian.API.Events;

public class ContainerEventArgs : PlayerEventArgs
{
public required BaseContainer Container { get; init; }

protected ContainerEventArgs(IPlayer player) : base(player)
protected ContainerEventArgs(IPlayer player, IServer server) : base(player, server)
{
}
}
3 changes: 2 additions & 1 deletion Obsidian.API/Events/IncomingChatMessageEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public class IncomingChatMessageEventArgs : PlayerEventArgs, ICancellable
/// <param name="player">The player which sent the message.</param>
/// <param name="message">The message which was sent.</param>
/// <param name="format">Any formatting appied to the message.</param>
public IncomingChatMessageEventArgs(IPlayer player, string message, string format) : base(player)
/// <param name="server">The server this took place in.</param>
public IncomingChatMessageEventArgs(IPlayer player, IServer server, string message, string format) : base(player, server)
{
this.Message = message;
this.Format = format;
Expand Down
3 changes: 2 additions & 1 deletion Obsidian.API/Events/PacketReceivedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public sealed class PacketReceivedEventArgs : PlayerEventArgs, ICancellable
/// <param name="player">The player involved in this event.</param>
/// <param name="id">Id of the received packet.</param>
/// <param name="data">Packet data, excluding packet id and packet length.</param>
public PacketReceivedEventArgs(IPlayer player, int id, ReadOnlyMemory<byte> data) : base(player)
/// <param name="server">The server this took place in.</param>
public PacketReceivedEventArgs(IPlayer player, IServer server, int id, ReadOnlyMemory<byte> data) : base(player, server)
{
Id = id;
Data = data;
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PermissionGrantedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class PermissionGrantedEventArgs : PlayerEventArgs
{
public string Permission { get; }

public PermissionGrantedEventArgs(IPlayer player, string permission) : base(player)
public PermissionGrantedEventArgs(IPlayer player, IServer server, string permission) : base(player, server)
{
Permission = permission;
}
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PermissionRevokedEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class PermissionRevokedEventArgs : PlayerEventArgs
{
public string Permission { get; }

public PermissionRevokedEventArgs(IPlayer player, string permission) : base(player)
public PermissionRevokedEventArgs(IPlayer player, IServer server, string permission) : base(player, server)
{
Permission = permission;
}
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PlayerEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class PlayerEventArgs : BaseMinecraftEventArgs
/// </summary>
public IPlayer Player { get; }

protected PlayerEventArgs(IPlayer player) : base(player.Server)
protected PlayerEventArgs(IPlayer player, IServer server) : base(server)
{
Player = player;
}
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PlayerInteractEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public sealed class PlayerInteractEventArgs : PlayerEventArgs, ICancellable
/// <inheritdoc />
public bool IsCancelled { get; private set; }

public PlayerInteractEventArgs(IPlayer player) : base(player) { }
public PlayerInteractEventArgs(IPlayer player, IServer server) : base(player, server) { }

/// <inheritdoc />
public void Cancel()
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PlayerJoinEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class PlayerJoinEventArgs : PlayerEventArgs
/// </summary>
public DateTimeOffset JoinDate { get; }

public PlayerJoinEventArgs(IPlayer player, DateTimeOffset join) : base(player)
public PlayerJoinEventArgs(IPlayer player, IServer server, DateTimeOffset join) : base(player, server)
{
this.JoinDate = join;
}
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PlayerLeaveEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class PlayerLeaveEventArgs : PlayerEventArgs
/// </summary>
public DateTimeOffset LeaveDate { get; }

public PlayerLeaveEventArgs(IPlayer player, DateTimeOffset leave) : base(player)
public PlayerLeaveEventArgs(IPlayer player, IServer server, DateTimeOffset leave) : base(player, server)
{
this.LeaveDate = leave;
}
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Events/PlayerTeleportEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public class PlayerTeleportEventArgs : PlayerEventArgs
public VectorF OldPosition { get; }
public VectorF NewPosition { get; }

public PlayerTeleportEventArgs(IPlayer player, VectorF oldPosition, VectorF newPosition) : base(player)
public PlayerTeleportEventArgs(IPlayer player, IServer server, VectorF oldPosition, VectorF newPosition) : base(player, server)
{
OldPosition = oldPosition;
NewPosition = newPosition;
Expand Down
2 changes: 1 addition & 1 deletion Obsidian.API/Logging/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,5 @@ void PrintLinePrefix()

public bool IsEnabled(LogLevel logLevel) => logLevel >= MinimumLevel;

public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
}
30 changes: 14 additions & 16 deletions Obsidian.API/Utilities/Extensions.StringManipulation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.RegularExpressions;

namespace Obsidian.API.Utilities;

public static partial class Extensions
{
public static readonly Regex pattern = new(@"[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+");

/// <remarks>
/// This method is not and shouldn't be used in performance-critical sections.
/// Source: https://stackoverflow.com/a/1415187
Expand Down Expand Up @@ -38,7 +37,6 @@ public static string ToPascalCase(this string snakeCase)
// Alternative implementation:
// var textInfo = System.Globalization.CultureInfo.CurrentCulture.TextInfo;
// return string.Join("", snakeCase.Split('_').Select(s => textInfo.ToTitleCase(s)));

int spaceCount = 0;
for (int i = 0; i < snakeCase.Length; i++)
{
Expand Down Expand Up @@ -94,7 +92,7 @@ public static string Capitalize(this string value)

public static bool EqualsIgnoreCase(this string a, string b) => a.Equals(b, StringComparison.OrdinalIgnoreCase);

public static string ToSnakeCase(this string str) => string.Join("_", pattern.Matches(str)).ToLower();
public static string ToSnakeCase(this string str) => JsonNamingPolicy.SnakeCaseLower.ConvertName(str);

/// <summary>
/// Trims resource tag from the start and removes '_' characters.
Expand Down Expand Up @@ -132,7 +130,7 @@ public static string TrimResourceTag(this string value, bool keepUnderscores = f
});
}

[Obsolete("Do not use. Kept as a masterpiece.")]
//[Obsolete("Do not use. Kept as a masterpiece.")]
// This method is an absolute masterpiece. Its author must've entered
// the highest plane of existance when writing it. The purpose of this
// method is to make a string camelCase, but at all places where it was
Expand All @@ -153,15 +151,15 @@ public static string TrimResourceTag(this string value, bool keepUnderscores = f
// The text appears 8 times in memory, in different forms (including the
// returned string). That means that every call produces around 7 * str.Length
// * sizeof(char) bytes of garbage.
public static string ToCamelCase(this string str)
{
return new string(
new CultureInfo("en-US", false)
.TextInfo
.ToTitleCase(string.Join(" ", pattern.Matches(str)).ToLower())
.Replace(@" ", "")
.Select((x, i) => i == 0 ? char.ToLower(x) : x)
.ToArray()
);
}
//public static string ToCamelCase(this string str)
//{
// return new string(
// new CultureInfo("en-US", false)
// .TextInfo
// .ToTitleCase(string.Join(" ", pattern.Matches(str)).ToLower())
// .Replace(@" ", "")
// .Select((x, i) => i == 0 ? char.ToLower(x) : x)
// .ToArray()
// );
//}
}
2 changes: 0 additions & 2 deletions Obsidian.API/_Interfaces/IEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ namespace Obsidian.API;

public interface IEntity
{
public IServer Server { get; }

public IWorld World { get; }
public INavigator? Navigator { get; set; }

Expand Down
6 changes: 3 additions & 3 deletions Obsidian.API/_Interfaces/ILiving.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public interface ILiving : IEntity
/// <summary>
/// Clears all potion effects of the entity.
/// </summary>
public Task ClearPotionEffects();
public void ClearPotionEffects();

/// <summary>
/// Adds the given <see cref="PotionEffect"/> to the entity.
Expand All @@ -43,12 +43,12 @@ public interface ILiving : IEntity
/// <param name="showParticles">Whether to show the particles or not.</param>
/// <param name="showIcon">Whether to show the icon on the client or not.</param>
/// <param name="isAmbient">Whether the potion is emitted by ambient source e.g. the beacon. The icon has a blue border in the HUD if its ambient.</param>
public Task AddPotionEffectAsync(PotionEffect potion, int duration, byte amplifier = 0, bool showParticles = true,
public void AddPotionEffect(PotionEffect potion, int duration, byte amplifier = 0, bool showParticles = true,
bool showIcon = true, bool isAmbient = false);

/// <summary>
/// Removes the given <see cref="PotionEffect"/> from the entity.
/// </summary>
/// <param name="potion">The potion effect to be removed.</param>
public Task RemovePotionEffectAsync(PotionEffect potion);
public void RemovePotionEffect(PotionEffect potion);
}
6 changes: 2 additions & 4 deletions Obsidian.API/_Interfaces/IOperatorList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ namespace Obsidian.API;

public interface IOperatorList
{
public void AddOperator(IPlayer player);
public void AddOperator(IPlayer player, int level = 4, bool bypassesPlayerLimit = false);
public bool CreateRequest(IPlayer player);
public bool ProcessRequest(IPlayer player, string? code);
public void AddOperator(string username);
public bool ProcessRequest(IPlayer player, string code);
public void RemoveOperator(IPlayer player);
public void RemoveOperator(string username);
public bool IsOperator(IPlayer p);
public ImmutableList<IPlayer> GetOnlineOperators();
}
10 changes: 10 additions & 0 deletions Obsidian.API/_Interfaces/IRegion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Obsidian.API;
public interface IRegion : IAsyncDisposable
{
public int X { get; }
public int Z { get; }

public int LoadedChunkCount { get; }

public string RegionFolder { get; }
}
2 changes: 2 additions & 0 deletions Obsidian.API/_Interfaces/IServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public interface IServer

public IScoreboardManager ScoreboardManager { get; }

public Task RunAsync();

public bool IsPlayerOnline(string username);
public bool IsPlayerOnline(Guid uuid);
public void BroadcastMessage(string message);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
using Obsidian.API._Types.Config;
using Obsidian.API.Config;

namespace Obsidian.API;

public interface IServerConfiguration
{
public bool? Baah { get; set; }

/// <summary>
/// Returns true if <see cref="ConnectionThrottle"/> has a value greater than 0.
/// </summary>
public bool CanThrottle => this.ConnectionThrottle > 0;

/// <summary>
/// Allows the server to advertise itself as a LAN server to devices on your network.
/// </summary>
public bool AllowLan { get; set; }

public bool IpWhitelistEnabled { get; set; }

/// <summary>
/// The time in milliseconds to wait before an ip is allowed to try and connect again.
/// </summary>
public long ConnectionThrottle { get; set; }

/// <summary>
/// Server description.
/// </summary>
Expand Down Expand Up @@ -34,8 +53,20 @@ public interface IServerConfiguration
/// </summary>
public int MaxPlayers { get; set; }

public int PregenerateChunkRange { get; set; }

/// <summary>
/// The speed at which world time & rain time go by.
/// </summary>
public int TimeTickSpeedMultiplier { get; set; }

/// <summary>
/// Allow people to requests to become an operator.
/// </summary>
public bool AllowOperatorRequests { get; set; }

public bool WhitelistEnabled { get; set; }

/// <summary>
/// Whether each login/client gets a random username where multiple connections from the same host will be allowed.
/// </summary>
Expand All @@ -51,7 +82,6 @@ public interface IServerConfiguration
/// </summary>
public string Footer { get; set; }


/// <summary>
/// Interval between KeepAlive packets send by the server.
/// </summary>
Expand All @@ -73,8 +103,13 @@ public interface IServerConfiguration
/// <remarks>See more at https://wiki.vg/RCON</remarks>
public bool EnableRcon => Rcon is not null;

public bool VerboseExceptionLogging { get; set; }

/// <summary>
/// Remote Console configuration
/// </summary>
public RconConfig? Rcon { get; set; }

public List<string> WhitelistedIPs { get; set; }
public List<WhitelistedPlayer> Whitelisted { get; set; }
}
15 changes: 13 additions & 2 deletions Obsidian.API/_Interfaces/IWorld.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Concurrent;

namespace Obsidian.API;

public interface IWorld
public interface IWorld : IAsyncDisposable
{
public string Name { get; }

Expand All @@ -14,6 +16,12 @@ public interface IWorld

public Gamemode DefaultGamemode { get; }

public int RegionCount { get; }
public int LoadedChunkCount { get; }
public int ChunksToGenCount { get; }

public int GetTotalLoadedEntities();

public Task<IBlock?> GetBlockAsync(Vector location);
public Task<IBlock?> GetBlockAsync(int x, int y, int z);
public Task SetBlockAsync(Vector location, IBlock block);
Expand All @@ -26,5 +34,8 @@ public interface IWorld
public Task<int?> GetWorldSurfaceHeightAsync(int x, int z);

public Task<IEntity> SpawnEntityAsync(VectorF position, EntityType type);
public Task SpawnExperienceOrbs(VectorF position, short count);
public void SpawnExperienceOrbs(VectorF position, short count);

public Task DoWorldTickAsync();
public Task FlushRegionsAsync();
}
25 changes: 25 additions & 0 deletions Obsidian.API/_Interfaces/IWorldManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Diagnostics.CodeAnalysis;

namespace Obsidian.API;
public interface IWorldManager : IAsyncDisposable
{
public bool ReadyToJoin { get; }

public Dictionary<string, Type> WorldGenerators { get; }

public int GeneratingChunkCount { get; }
public int LoadedChunkCount { get; }

public int RegionCount { get; }

public IWorld DefaultWorld { get; }

public IReadOnlyCollection<IWorld> GetAvailableWorlds();

public Task FlushLoadedWorldsAsync();

public Task TickWorldsAsync();

public bool TryGetWorld(string name, [NotNullWhen(true)] out IWorld? world);
public bool TryGetWorld<TWorld>(string name, [NotNullWhen(true)] out TWorld? world) where TWorld : IWorld;
}
2 changes: 1 addition & 1 deletion Obsidian.API/_Types/Config/RconConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Obsidian.API._Types.Config;
namespace Obsidian.API.Config;

public class RconConfig
{
Expand Down
Loading

0 comments on commit 0ad986f

Please sign in to comment.