Skip to content

Commit

Permalink
A lot of changes and a more reponsible engineer would break these out…
Browse files Browse the repository at this point in the history
… into concise PRs but I'm not that guy and write titles that are way too long (#413)

* 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

* Improve caves & ores generation performance

* Update OperatorList & IOperatorList

* Last bit refactor

* Make user cache a service instead

* Add sealed modifier to most classes (final commit)

* OKAY ACTUAL FINAL COMMIT

* Tweaks to rivers, mountain heights and beaches

* Fix bug loading worlds from disk

* Still mucking about

* interim

* interim

* Interim

* Personal code review

* fix biome reload issue

* Ready for review

* Later...

---------

Co-authored-by: Tides <[email protected]>
Co-authored-by: Seb-stian <[email protected]>
  • Loading branch information
3 people authored Dec 20, 2023
1 parent d8e0421 commit c65013d
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 146 deletions.
18 changes: 9 additions & 9 deletions Obsidian.API/Noise/BiomeSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,36 +53,36 @@ public override double GetValue(double x, double y, double z)
// 5 heights, 4 temps, 3 humidities
var tempIndex = (int)((SourceModules[0].GetValue(x, 0, z) + 0.999d) * 2.0d);
var humidityIndex = (int)((SourceModules[1].GetValue(x, 0, z) + 0.999d) * 1.5d);
var erosionVal = SourceModules[3].GetValue(x, 0, z) + 2.0;
var erosionVal = SourceModules[3].GetValue(x, 0, z) + 1.0;

var height = SourceModules[2].GetValue(x, 0, z);
if (height >= -0.01)
{
// Check river
var riverVal = SourceModules[4].GetValue(x, 0, z);
if (riverVal < 0)
{
return (double)Biome.River;
}
if (riverVal < 0.04)
return tempIndex < 1 ? (double)Biome.FrozenRiver : (double)Biome.River;
}
if (height >= -0.1 && height < 0.025) { return (double)Biome.Beach; }
if (height >= -0.1 && height < 0.04)
return tempIndex <= 1 ? (double)Biome.SnowyBeach : (double)Biome.Beach;

if (height > 0.1) // If above ocean, add erosion and rivers
{
erosionVal = (height - 0.1) * erosionVal;
erosionVal = (height - 0.1) * (erosionVal + 1.3);
height += erosionVal;
}
if (height >= 0.6) // Add mountain peaks/valleys
{
var peakVal = (height - 0.6) * Math.Max(SourceModules[5].GetValue(x, 0, z) + 1.6, 1.0) * 0.5;
height += peakVal;// * (erosionVal - 0.5);
height += peakVal * (erosionVal + 0.5);
}

var heightIndex = height switch
{
double v when v < -0.6 => 0,
double v when v >= -0.6 && v < 0 => 1,
double v when v >= 0 && v < 0.3 => 2,
double v when v >= 0.3 && v < 1 => 3,
double v when v >= 0.3 && v < 1.5 => 3,
_ => 4,
};

Expand Down
15 changes: 7 additions & 8 deletions Obsidian.API/Noise/OverworldTerrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public OverworldTerrain(Module height, Module squash, Module erosion, Module riv
terrainPerlin = new Perlin()
{
Frequency = 0.333 / TerrainStretch,
OctaveCount = 3,
Lacunarity = 0.8899,
Persistence = 0.1334,
OctaveCount = 5,
Lacunarity = 1.7899,
Persistence = 0.2334,
Quality = SharpNoise.NoiseQuality.Fast,
Seed = Seed + 2
};
Expand All @@ -32,13 +32,12 @@ public override double GetValue(double x, double y, double z)
{
var squash = SourceModules[1].GetValue(x, 0, z) + 1.1d; // Can't be zero
var height = SourceModules[0].GetValue(x, 0, z);
var erosionVal = SourceModules[2].GetValue(x, 0, z) + 2.0;
var erosionVal = SourceModules[2].GetValue(x, 0, z) + 1.0;


if (height > 0.1) // If above ocean, add erosion and rivers
{
erosionVal = (height - 0.1) * erosionVal;
height += erosionVal;
height += (height - 0.1) * (erosionVal + 1.3);
}

if (height > -0.1)
Expand All @@ -48,11 +47,11 @@ public override double GetValue(double x, double y, double z)
}

// Beash/Ocean flat, everything else amplified
squash = height < 0.1 ? squash * 0.3d : 1.12 * Math.Pow(squash,3);
squash = height < 0 ? squash * 0.3d : Math.Pow(-(3 * squash - 1.5), 4) + 1.0;
if (height >= 0.6) // Add mountain peaks/valleys
{
var peakVal = (height - 0.6) * Math.Max(SourceModules[4].GetValue(x, 0, z) + 1.6, 1.0)*0.5;
height += peakVal;// * (erosionVal - 0.5);
height += peakVal * (erosionVal + 0.5);
}

double yOffset = y + (height * 128 * -1);
Expand Down
3 changes: 2 additions & 1 deletion Obsidian.API/Noise/RiverSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public RiverSelector() : base(0)
public override double GetValue(double x, double y, double z)
{
var n = Math.Abs(RiverNoise.GetValue(x, y, z));
return n >= 0.432 ? 1 : Math.Max(3 * n + 0.07 * Math.Sin(40 * n) - 0.225, -0.1);
// Desmos: 5x\ +\ 0.1\sin\left(50x+1\right)\ -0.19\ \left\{0\ <\ x\right\}
return n >= 0.3484 ? 1.5 : Math.Max(5 * n + 0.1 * Math.Sin(50 * n + 0.75) - 0.19, -0.085);
}
}
1 change: 0 additions & 1 deletion Obsidian.API/_Interfaces/IServerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public interface IServerConfiguration
/// Maximum amount of players that is allowed to be connected at the same time.
/// </summary>
public int MaxPlayers { get; set; }

public int PregenerateChunkRange { get; set; }

/// <summary>
Expand Down
5 changes: 5 additions & 0 deletions Obsidian.ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
loggingBuilder.SetMinimumLevel(env.Configuration.LogLevel);
});

builder.Logging.AddFilter((provider, category, logLevel) =>
{
return !category.Contains("Microsoft") || logLevel != LogLevel.Debug;
});

builder.Services.AddObsidian(env);

// Give the server some time to shut down after CTRL-C or SIGTERM.
Expand Down
7 changes: 4 additions & 3 deletions Obsidian/Entities/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -977,9 +977,9 @@ internal async Task<bool> UpdateChunksAsync(bool unloadAll = false, int distance

clientUnneededChunks.ForEach(c => client.LoadedChunks.TryRemove(c));

await Parallel.ForEachAsync(clientNeededChunks, async (chunkLoc, _) =>
foreach (var (X, Z) in clientNeededChunks)
{
var chunk = await world.GetChunkAsync(chunkLoc.X, chunkLoc.Z);
var chunk = await world.GetChunkAsync(X, Z);
if (chunk is not null && chunk.IsGenerated)
{
await client.SendChunkAsync(chunk);
Expand All @@ -989,7 +989,8 @@ await Parallel.ForEachAsync(clientNeededChunks, async (chunkLoc, _) =>
{
sentAll = false;
}
});
}

return sentAll;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public partial class SetPlayerPositionAndRotationPacket : IServerboundPacket

public async ValueTask HandleAsync(Server server, Player player)
{
// The first time we get this packet, it doesn't make sense so we should ignore it.
if (player.LastPosition == Vector.Zero) { return; }

await player.UpdateAsync(Position, Yaw, Pitch, OnGround);
if (player.Position.ToChunkCoord() != player.LastPosition.ToChunkCoord())
{
Expand Down
39 changes: 39 additions & 0 deletions Obsidian/Utilities/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Globalization;
using System.IO;
using System.Linq.Expressions;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading;
Expand Down Expand Up @@ -127,4 +129,41 @@ public static string MinecraftShaDigest(this IEnumerable<byte> data)
JsonSerializer.DeserializeAsync<TValue>(stream, options ?? Globals.JsonOptions, cancellationToken);
public static Task ToJsonAsync(this object? value, Stream stream, CancellationToken cancellationToken = default) =>
JsonSerializer.SerializeAsync(stream, value, Globals.JsonOptions, cancellationToken);

public static bool Contains<T>(this ReadOnlySpan<T> span, T value) where T : unmanaged
{
for (int i = 0; i < span.Length; i++)
{
if (span[i].Equals<T>(value))
{
return true;
}
}
return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Equals<T>(this T a, T b) where T : unmanaged
{
if (Unsafe.SizeOf<T>() == sizeof(byte))
{
return Unsafe.As<T, byte>(ref a) == Unsafe.As<T, byte>(ref b);
}
else if (Unsafe.SizeOf<T>() == sizeof(ushort))
{
return Unsafe.As<T, ushort>(ref a) == Unsafe.As<T, ushort>(ref b);
}
else if (Unsafe.SizeOf<T>() == sizeof(uint))
{
return Unsafe.As<T, uint>(ref a) == Unsafe.As<T, uint>(ref b);
}
else if (Unsafe.SizeOf<T>() == sizeof(ulong))
{
return Unsafe.As<T, ulong>(ref a) == Unsafe.As<T, ulong>(ref b);
}
else
{
return EqualityComparer<T>.Default.Equals(a, b);
}
}
}
57 changes: 31 additions & 26 deletions Obsidian/WorldData/Generators/Overworld/ChunkBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Obsidian.ChunkData;
using Obsidian.Registries;
using System.Linq;
using System.Diagnostics;

namespace Obsidian.WorldData.Generators.Overworld;

Expand Down Expand Up @@ -54,6 +54,9 @@ internal static class ChunkBuilder
BlocksRegistry.DeepslateDiamondOre,
];

private static ReadOnlySpan<Biome> EmeraldBiomes => [Biome.WindsweptHills, Biome.WindsweptGravellyHills, Biome.Meadow, Biome.Grove, Biome.SnowySlopes, Biome.FrozenPeaks, Biome.JaggedPeaks, Biome.StonyPeaks];
private static ReadOnlySpan<OreType> OreTypes => [OreType.Coal, OreType.Iron, OreType.Copper, OreType.Gold, OreType.Lapis, OreType.Redstone, OreType.Emerald, OreType.Diamond];

internal enum OreType : int
{
Coal,
Expand All @@ -65,9 +68,10 @@ internal enum OreType : int
Emerald,
Diamond
}

internal static void Biomes(GenHelper helper, Chunk chunk)
{

for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
Expand Down Expand Up @@ -175,61 +179,62 @@ internal static void Surface(GenHelper helper, Chunk chunk)

internal static bool GenerateOreCheck(int height, OreType type) => type switch
{
OreType.Coal => height >= 0 && height <= 320,
OreType.Iron => (height >= -63 && height <= 72) || (height >= 80 && height <= 320),
OreType.Copper => height >= -16 && height <= 112,
OreType.Gold => height >= -63 && height <= 30,
OreType.Lapis => height >= -63 && height <= 64,
OreType.Redstone => height >= -63 && height <= 16,
OreType.Emerald => height >= -16 && height <= 320,
OreType.Diamond => height >= -63 && height <= 16,
OreType.Coal => height is >= 0 and <= 320,
OreType.Iron => (height is >= -63 and <= 72) || (height is >= 80 and <= 320),
OreType.Copper => height is >= -16 and <= 112,
OreType.Gold => height is >= -63 and <= 30,
OreType.Lapis => height is >= -63 and <= 64,
OreType.Redstone => height is >= -63 and <= 16,
OreType.Emerald => height is >= -16 and <= 320,
OreType.Diamond => height is >= -63 and <= 16,
_ => true
};

internal static void CavesAndOres(GenHelper helper, Chunk chunk)
{
int chunkOffsetX = chunk.X * 16;
int chunkOffsetZ = chunk.Z * 16;
List<Biome> emeraldBiomes = new List<Biome>(){Biome.WindsweptHills, Biome.WindsweptGravellyHills, Biome.Meadow, Biome.Grove, Biome.SnowySlopes, Biome.FrozenPeaks, Biome.JaggedPeaks, Biome.StonyPeaks};
for (int x = 0; x < 16; x++)

for (int z = 0; z < 16; z++)
{
for (int z = 0; z < 16; z++)
for (int x = 0; x < 16; x++)
{
int terrainY = chunk.Heightmaps[HeightmapType.WorldSurfaceWG].GetHeight(x, z);
var (worldX, worldZ) = (x + chunkOffsetX, z + chunkOffsetZ);
for (int y = -60; y <= terrainY; y++)
for (int y = -60; y <= terrainY-6; y++)
{
bool isCave = helper.Noise.Cave.GetValue(x + chunkOffsetX, y, z + chunkOffsetZ) > 1 - CaveSize;
if (isCave)
{
if (chunk.GetBlock(x, y + 1, z) is IBlock b && !b.IsLiquid)
if (chunk.GetBlock(x, y + 1, z) is { IsLiquid: false })
chunk.SetBlock(x, y, z, BlocksRegistry.CaveAir);
continue;
}

if (y > terrainY - 5) { continue; }
var orePlaced = false; //Thanks Jonpro03 for line 210 to 237!
foreach (OreType i in Enum.GetValues(typeof(OreType)))
var orePlaced = false;
foreach (OreType ore in OreTypes)
{
// Check if we should be placing a given ore at this Y level
if (!GenerateOreCheck(y, i))
if (!GenerateOreCheck(y, ore))
{
// move on to the next ore
continue;
}
var b = chunk.GetBiome(x, y, z);

var chunkBiome = chunk.GetBiome(x, y, z);
// Check that Emerald is only placed in the right biomes
if (i == OreType.Emerald && !emeraldBiomes.Contains(b))
if (ore == OreType.Emerald && !EmeraldBiomes.Contains(chunkBiome))
{
continue;
}
var oreNoise1 = helper.Noise.Ore((int)i).GetValue(worldX, y, worldZ);
var oreNoise2 = helper.Noise.Ore((int)i + ores.Length).GetValue(worldX, y, worldZ);

var oreNoise1 = helper.Noise.Ore((int)ore).GetValue(worldX, y, worldZ);
var oreNoise2 = helper.Noise.Ore((int)ore + ores.Length).GetValue(worldX, y, worldZ);
if (oreNoise1 > 1.0 - OreSize && oreNoise2 > 1.0 - OreSize)
{
// If Y is below 0, switch to deepore varients
chunk.SetBlock(worldX, y, worldZ, y > 0 ? ores[(int)i] : deepores[(int)i]);
chunk.SetBlock(worldX, y, worldZ, y > 0 ? ores[(int)ore] : deepores[(int)ore]);
orePlaced = true;
break;
}
Expand Down
Loading

0 comments on commit c65013d

Please sign in to comment.