Many improvements to server stability+performance
Sorry for the vague commit message. There were a lot of changes. Here's a list of most of them: - Lighting updates are timeboxed each frame - The next environment frame is queued sooner if the current one took longer (as soon as immediately) - Issues with the physics engines and mobs using it were (mostly) fixed, mobs no longer freak out and get stuck on physics objects - Mob AI/pathfinding is done more intelligently - The player can no longer spawn in an ocean or a desert biome - Some deadlocks in Region were fixed (more remain to be fixed) The current performance bottlenecks are lighting (still) and propegating initial chunk loads to blocks that need to schedule things (such as grass blocks). I think the main culprit in the latter case is grass blocks and water blocks. The former can be improved by adding a block cache to World, but that'll take some custom work. This step is just gonna be slow no matter what, we might have to split it across several frames but it's never going to be great. There still seems to be a deadlock somewhere in all of this mess, in the world code. I'll find it later.
This commit is contained in:
parent
7f0e3338d2
commit
362c852f51
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using TrueCraft.API.AI;
|
||||
using TrueCraft.API.Physics;
|
||||
|
||||
namespace TrueCraft.API.Entities
|
||||
@ -8,6 +9,7 @@ namespace TrueCraft.API.Entities
|
||||
event EventHandler PathComplete;
|
||||
PathResult CurrentPath { get; set; }
|
||||
bool AdvancePath(TimeSpan time, bool faceRoute = true);
|
||||
IMobState CurrentState { get; set; }
|
||||
void Face(Vector3 target);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
using TrueCraft.API.Entities;
|
||||
using System.Collections.Generic;
|
||||
using TrueCraft.API.Networking;
|
||||
using TrueCraft.API.World;
|
||||
|
||||
namespace TrueCraft.API.Server
|
||||
{
|
||||
public interface IEntityManager
|
||||
{
|
||||
IWorld World { get; }
|
||||
TimeSpan TimeSinceLastUpdate { get; }
|
||||
/// <summary>
|
||||
/// Adds an entity to the world and assigns it an entity ID.
|
||||
|
@ -22,7 +22,7 @@ namespace TrueCraft.API.World
|
||||
IList<BiomeCell> BiomeCells { get; }
|
||||
void AddCell(BiomeCell cell);
|
||||
byte GetBiome(Coordinates2D location);
|
||||
byte GenerateBiome(int seed, IBiomeRepository biomes, Coordinates2D location);
|
||||
byte GenerateBiome(int seed, IBiomeRepository biomes, Coordinates2D location, bool spawn);
|
||||
BiomeCell ClosestCell(Coordinates2D location);
|
||||
double ClosestCellPoint(Coordinates2D location);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ namespace TrueCraft.API.World
|
||||
{
|
||||
public interface IBiomeProvider
|
||||
{
|
||||
bool Spawn { get; }
|
||||
byte ID { get; }
|
||||
int Elevation { get; }
|
||||
double Temperature { get; }
|
||||
|
@ -8,7 +8,7 @@ namespace TrueCraft.API.World
|
||||
public interface IBiomeRepository
|
||||
{
|
||||
IBiomeProvider GetBiome(byte id);
|
||||
IBiomeProvider GetBiome(double temperature, double rainfall);
|
||||
IBiomeProvider GetBiome(double temperature, double rainfall, bool spawn);
|
||||
void RegisterBiomeProvider(IBiomeProvider provider);
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace TrueCraft.Core.Test.Logic
|
||||
public Mock<IRemoteClient> User { get; set; }
|
||||
public Mock<IBlockRepository> BlockRepository { get; set; }
|
||||
|
||||
[TestFixtureSetUp]
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
World = new Mock<IWorld>();
|
||||
|
@ -14,7 +14,7 @@ namespace TrueCraft.Core.Test.World
|
||||
{
|
||||
public Chunk Chunk { get; set; }
|
||||
|
||||
[TestFixtureSetUp]
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
@ -12,7 +12,7 @@ namespace TrueCraft.Core.Test.World
|
||||
{
|
||||
public Region Region { get; set; }
|
||||
|
||||
[TestFixtureSetUp]
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var world = new TrueCraft.Core.World.World();
|
||||
|
@ -14,7 +14,7 @@ namespace TrueCraft.Core.Test.World
|
||||
{
|
||||
public TrueCraft.Core.World.World World { get; set; }
|
||||
|
||||
[TestFixtureSetUp]
|
||||
[OneTimeSetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using TrueCraft.API;
|
||||
using TrueCraft.API.World;
|
||||
using System.Diagnostics;
|
||||
using TrueCraft.API.Logic;
|
||||
|
||||
namespace TrueCraft.Core.AI
|
||||
{
|
||||
@ -40,8 +41,12 @@ namespace TrueCraft.Core.AI
|
||||
private bool CanOccupyVoxel(IWorld world, BoundingBox box, Coordinates3D voxel)
|
||||
{
|
||||
var id = world.GetBlockID(voxel);
|
||||
// TODO: Make this more sophisticated
|
||||
if (world.BlockRepository == null)
|
||||
return id == 0;
|
||||
var provider = world.BlockRepository.GetBlockProvider(id);
|
||||
if (provider == null)
|
||||
return true;
|
||||
return provider.BoundingBox == null;
|
||||
}
|
||||
|
||||
private IEnumerable<Coordinates3D> GetNeighbors(IWorld world, BoundingBox subject, Coordinates3D current)
|
||||
|
28
TrueCraft.Core/AI/IdleState.cs
Normal file
28
TrueCraft.Core/AI/IdleState.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using TrueCraft.API.AI;
|
||||
using TrueCraft.API.Entities;
|
||||
using TrueCraft.API.Server;
|
||||
|
||||
namespace TrueCraft.Core.AI
|
||||
{
|
||||
public class IdleState : IMobState
|
||||
{
|
||||
private DateTime Expiry { get; set; }
|
||||
private IMobState NextState { get; set; }
|
||||
|
||||
public IdleState(IMobState nextState, DateTime? expiry = null)
|
||||
{
|
||||
NextState = nextState;
|
||||
if (expiry != null)
|
||||
Expiry = expiry.Value;
|
||||
else
|
||||
Expiry = DateTime.UtcNow.AddSeconds(MathHelper.Random.Next(5, 15));
|
||||
}
|
||||
|
||||
public void Update(IMobEntity entity, IEntityManager manager)
|
||||
{
|
||||
if (DateTime.UtcNow >= Expiry)
|
||||
entity.CurrentState = NextState;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,16 +5,12 @@ using TrueCraft.API.Server;
|
||||
using TrueCraft.API;
|
||||
using TrueCraft.API.World;
|
||||
using System.Threading.Tasks;
|
||||
using TrueCraft.API.Logic;
|
||||
|
||||
namespace TrueCraft.Core.AI
|
||||
{
|
||||
public class WanderState : IMobState
|
||||
{
|
||||
/// <summary>
|
||||
/// Chance that mob will decide to move during an update when idle.
|
||||
/// Chance is equal to 1 / IdleChance.
|
||||
/// </summary>
|
||||
public int IdleChance { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum distance the mob will move in an iteration.
|
||||
/// </summary>
|
||||
@ -24,7 +20,6 @@ namespace TrueCraft.Core.AI
|
||||
|
||||
public WanderState()
|
||||
{
|
||||
IdleChance = 10;
|
||||
Distance = 25;
|
||||
PathFinder = new AStarPathFinder();
|
||||
}
|
||||
@ -33,10 +28,13 @@ namespace TrueCraft.Core.AI
|
||||
{
|
||||
var cast = entity as IEntity;
|
||||
if (entity.CurrentPath != null)
|
||||
entity.AdvancePath(manager.TimeSinceLastUpdate);
|
||||
else
|
||||
{
|
||||
if (MathHelper.Random.Next(IdleChance) == 0)
|
||||
if (entity.AdvancePath(manager.TimeSinceLastUpdate))
|
||||
{
|
||||
entity.CurrentState = new IdleState(new WanderState());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var target = new Coordinates3D(
|
||||
(int)(cast.Position.X + (MathHelper.Random.Next(Distance) - Distance / 2)),
|
||||
@ -54,5 +52,4 @@ namespace TrueCraft.Core.AI
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -97,11 +97,6 @@ namespace TrueCraft.Core.Entities
|
||||
|
||||
public IMobState CurrentState { get; set; }
|
||||
|
||||
public void ChangeState(IMobState state)
|
||||
{
|
||||
CurrentState = state;
|
||||
}
|
||||
|
||||
public void Face(Vector3 target)
|
||||
{
|
||||
var diff = target - Position;
|
||||
@ -115,14 +110,17 @@ namespace TrueCraft.Core.Entities
|
||||
{
|
||||
// Advance along path
|
||||
var target = (Vector3)CurrentPath.Waypoints[CurrentPath.Index];
|
||||
target += new Vector3(Size.Width / 2, 0, Size.Depth / 2); // Center it
|
||||
target.Y = Position.Y; // TODO: Find better way of doing this
|
||||
if (faceRoute)
|
||||
Face(target);
|
||||
var lookAt = Vector3.Forwards.Transform(Matrix.CreateRotationY(MathHelper.ToRadians(-(Yaw - 180) + 180)));
|
||||
lookAt *= modifier;
|
||||
Velocity = new Vector3(lookAt.X, Velocity.Y, lookAt.Z);
|
||||
if (Position.DistanceTo(target) < 0.1)
|
||||
if (Position.DistanceTo(target) < Velocity.Distance)
|
||||
{
|
||||
Position = target;
|
||||
Velocity = Vector3.Zero;
|
||||
CurrentPath.Index++;
|
||||
if (CurrentPath.Index >= CurrentPath.Waypoints.Count)
|
||||
{
|
||||
|
@ -300,6 +300,7 @@ namespace TrueCraft.Core.Lighting
|
||||
EnqueueOperation(new BoundingBox(new Vector3(coords.X, 0, coords.Z),
|
||||
new Vector3(coords.X + Chunk.Width, chunk.MaxHeight + 2, coords.Z + Chunk.Depth)),
|
||||
true, true);
|
||||
TryLightNext();
|
||||
while (flush && TryLightNext())
|
||||
{
|
||||
}
|
||||
|
@ -41,10 +41,21 @@ namespace TrueCraft.Core.Physics
|
||||
Entities.Remove(entity);
|
||||
}
|
||||
|
||||
private void TruncateVelocity(IPhysicsEntity entity, double multiplier)
|
||||
{
|
||||
if (Math.Abs(entity.Velocity.X) < 0.1 * multiplier)
|
||||
entity.Velocity = new Vector3(0, entity.Velocity.Y, entity.Velocity.Z);
|
||||
if (Math.Abs(entity.Velocity.Y) < 0.1 * multiplier)
|
||||
entity.Velocity = new Vector3(entity.Velocity.X, 0, entity.Velocity.Z);
|
||||
if (Math.Abs(entity.Velocity.Z) < 0.1 * multiplier)
|
||||
entity.Velocity = new Vector3(entity.Velocity.X, entity.Velocity.Y, 0);
|
||||
entity.Velocity.Clamp(entity.TerminalVelocity);
|
||||
}
|
||||
|
||||
public void Update(TimeSpan time)
|
||||
{
|
||||
double multiplier = time.TotalSeconds;
|
||||
if (multiplier == 0)
|
||||
if (multiplier == 0 || multiplier > 1)
|
||||
return;
|
||||
lock (EntityLock)
|
||||
{
|
||||
@ -55,9 +66,7 @@ namespace TrueCraft.Core.Physics
|
||||
{
|
||||
entity.Velocity -= new Vector3(0, entity.AccelerationDueToGravity * multiplier, 0);
|
||||
entity.Velocity *= 1 - entity.Drag * multiplier;
|
||||
if (entity.Velocity.Distance < 0.001)
|
||||
entity.Velocity = Vector3.Zero;
|
||||
entity.Velocity.Clamp(entity.TerminalVelocity);
|
||||
TruncateVelocity(entity, multiplier);
|
||||
|
||||
Vector3 collision, before = entity.Velocity;
|
||||
|
||||
@ -74,8 +83,8 @@ namespace TrueCraft.Core.Physics
|
||||
if (TestTerrainCollisionCylinder(aabbEntity, out collision))
|
||||
aabbEntity.TerrainCollision(collision, before);
|
||||
}
|
||||
|
||||
entity.EndUpdate(entity.Position + entity.Velocity);
|
||||
TruncateVelocity(entity, multiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ namespace TrueCraft.Core.TerrainGen
|
||||
return BiomeProviders[id];
|
||||
}
|
||||
|
||||
public IBiomeProvider GetBiome(double temperature, double rainfall)
|
||||
public IBiomeProvider GetBiome(double temperature, double rainfall, bool spawn)
|
||||
{
|
||||
List<IBiomeProvider> temperatureResults = new List<IBiomeProvider>();
|
||||
foreach (var biome in BiomeProviders)
|
||||
@ -79,7 +79,10 @@ namespace TrueCraft.Core.TerrainGen
|
||||
|
||||
foreach (var biome in BiomeProviders)
|
||||
{
|
||||
if (biome != null && biome.Rainfall.Equals(rainfall) && temperatureResults.Contains(biome))
|
||||
if (biome != null
|
||||
&& biome.Rainfall.Equals(rainfall)
|
||||
&& temperatureResults.Contains(biome)
|
||||
&& (!spawn || biome.Spawn))
|
||||
{
|
||||
return biome;
|
||||
}
|
||||
@ -92,14 +95,15 @@ namespace TrueCraft.Core.TerrainGen
|
||||
if (biome != null)
|
||||
{
|
||||
var difference = Math.Abs(temperature - biome.Temperature);
|
||||
if (biomeProvider == null || difference < rainfallDifference)
|
||||
if ((biomeProvider == null || difference < rainfallDifference)
|
||||
&& (!spawn || biome.Spawn))
|
||||
{
|
||||
biomeProvider = biome;
|
||||
rainfallDifference = (float)difference;
|
||||
}
|
||||
}
|
||||
}
|
||||
return biomeProvider;
|
||||
return biomeProvider ?? new PlainsBiome();
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,14 @@ namespace TrueCraft.Core.TerrainGen.Biomes
|
||||
/// </summary>
|
||||
public abstract double Rainfall { get; }
|
||||
|
||||
public virtual bool Spawn
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tree types generated in the biome.
|
||||
/// </summary>
|
||||
|
@ -26,6 +26,11 @@ namespace TrueCraft.Core.TerrainGen.Biomes
|
||||
get { return 0.0f; }
|
||||
}
|
||||
|
||||
public override bool Spawn
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override TreeSpecies[] Trees
|
||||
{
|
||||
get
|
||||
|
@ -12,7 +12,7 @@ namespace TrueCraft.Core.TerrainGen.Decorators
|
||||
{
|
||||
public class LiquidDecorator : IChunkDecorator
|
||||
{
|
||||
const int WaterLevel = 40;
|
||||
public static readonly int WaterLevel = 40;
|
||||
|
||||
public void Decorate(IWorld world, IChunk chunk, IBiomeRepository biomes)
|
||||
{
|
||||
|
@ -137,7 +137,9 @@ namespace TrueCraft.Core.TerrainGen
|
||||
|| cellValue.Equals(1)
|
||||
&& world.BiomeDiagram.ClosestCellPoint(location) >= featurePointDistance)
|
||||
{
|
||||
byte id = (SingleBiome) ? GenerationBiome : world.BiomeDiagram.GenerateBiome(seed, Biomes, location);
|
||||
byte id = (SingleBiome) ? GenerationBiome
|
||||
: world.BiomeDiagram.GenerateBiome(seed, Biomes, location,
|
||||
IsSpawnCoordinate(location.X, location.Z));
|
||||
var cell = new BiomeCell(id, location);
|
||||
world.BiomeDiagram.AddCell(cell);
|
||||
}
|
||||
@ -207,14 +209,23 @@ namespace TrueCraft.Core.TerrainGen
|
||||
return world.BiomeDiagram.GetBiome(location);
|
||||
}
|
||||
|
||||
bool IsSpawnCoordinate(int x, int z)
|
||||
{
|
||||
return x > -1000 && x < 1000 || z > -1000 && z < 1000;
|
||||
}
|
||||
|
||||
int GetHeight(int x, int z)
|
||||
{
|
||||
var NoiseValue = FinalNoise.Value2D(x, z) + GroundLevel;
|
||||
if (NoiseValue < 0)
|
||||
NoiseValue = GroundLevel;
|
||||
if (NoiseValue > Chunk.Height)
|
||||
NoiseValue = Chunk.Height - 1;
|
||||
return (int)NoiseValue;
|
||||
var value = FinalNoise.Value2D(x, z) + GroundLevel;
|
||||
var coords = new Coordinates2D(x, z);
|
||||
double distance = IsSpawnCoordinate(x, z) ? coords.Distance : 1000;
|
||||
if (distance < 1000) // Avoids deep water within 1km sq of spawn
|
||||
value += (1 - distance / 1000f) * 12;
|
||||
if (value < 0)
|
||||
value = GroundLevel;
|
||||
if (value > Chunk.Height)
|
||||
value = Chunk.Height - 1;
|
||||
return (int)value;
|
||||
}
|
||||
}
|
||||
}
|
@ -355,6 +355,7 @@
|
||||
<Compile Include="Windows\FurnaceWindow.cs" />
|
||||
<Compile Include="Paths.cs" />
|
||||
<Compile Include="Logic\CraftingRepository.cs" />
|
||||
<Compile Include="AI\IdleState.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -42,11 +42,11 @@ namespace TrueCraft.Core.World
|
||||
return BiomeID;
|
||||
}
|
||||
|
||||
public byte GenerateBiome(int seed, IBiomeRepository biomes, Coordinates2D location)
|
||||
public byte GenerateBiome(int seed, IBiomeRepository biomes, Coordinates2D location, bool spawn)
|
||||
{
|
||||
double temp = Math.Abs(TempNoise.Value2D(location.X, location.Z));
|
||||
double rainfall = Math.Abs(RainNoise.Value2D(location.X, location.Z));
|
||||
byte ID = biomes.GetBiome(temp, rainfall).ID;
|
||||
byte ID = biomes.GetBiome(temp, rainfall, spawn).ID;
|
||||
return ID;
|
||||
}
|
||||
|
||||
|
@ -67,14 +67,12 @@ namespace TrueCraft.Core.World
|
||||
public IChunk GetChunk(Coordinates2D position, bool generate = true)
|
||||
{
|
||||
// TODO: This could use some refactoring
|
||||
lock (Chunks)
|
||||
{
|
||||
if (!Chunks.ContainsKey(position))
|
||||
{
|
||||
if (regionFile != null)
|
||||
{
|
||||
// Search the stream for that region
|
||||
lock (regionFile)
|
||||
lock (streamLock)
|
||||
{
|
||||
var chunkData = GetChunkFromTable(position);
|
||||
if (chunkData == null)
|
||||
@ -119,7 +117,6 @@ namespace TrueCraft.Core.World
|
||||
}
|
||||
return Chunks[position];
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateChunk(Coordinates2D position)
|
||||
{
|
||||
@ -161,8 +158,6 @@ namespace TrueCraft.Core.World
|
||||
/// Saves this region to the open region file.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
lock (Chunks)
|
||||
{
|
||||
lock (streamLock)
|
||||
{
|
||||
@ -199,7 +194,6 @@ namespace TrueCraft.Core.World
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Stream Helpers
|
||||
|
||||
|
@ -210,9 +210,13 @@ namespace TrueCraft.Commands
|
||||
if (path == null)
|
||||
{
|
||||
client.SendMessage(ChatColor.Red + "It is impossible for this entity to reach you.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
client.SendMessage(string.Format(ChatColor.Blue
|
||||
+ "Executing path with {0} waypoints", path.Waypoints.Count()));
|
||||
entity.CurrentPath = path;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -222,6 +226,64 @@ namespace TrueCraft.Commands
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityInfoCommand : Command
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "entity"; }
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return "Provides information about an entity ID."; }
|
||||
}
|
||||
|
||||
public override string[] Aliases
|
||||
{
|
||||
get { return new string[0]; }
|
||||
}
|
||||
|
||||
public override void Handle(IRemoteClient client, string alias, string[] arguments)
|
||||
{
|
||||
if (arguments.Length != 1)
|
||||
{
|
||||
Help(client, alias, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
int id;
|
||||
if (!int.TryParse(arguments[0], out id))
|
||||
{
|
||||
Help(client, alias, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
var manager = client.Server.GetEntityManagerForWorld(client.World);
|
||||
var entity = manager.GetEntityByID(id);
|
||||
if (entity == null)
|
||||
{
|
||||
client.SendMessage(ChatColor.Red + "An entity with that ID does not exist in this world.");
|
||||
return;
|
||||
}
|
||||
client.SendMessage(string.Format(
|
||||
"{0} {1}", entity.GetType().Name, entity.Position));
|
||||
if (entity is MobEntity)
|
||||
{
|
||||
var mob = entity as MobEntity;
|
||||
client.SendMessage(string.Format(
|
||||
"{0}/{1} HP, {2} State, moving to to {3}",
|
||||
mob.Health, mob.MaxHealth,
|
||||
mob.CurrentState?.GetType().Name ?? "null",
|
||||
mob.CurrentPath?.Waypoints.Last().ToString() ?? "null"));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Help(IRemoteClient client, string alias, string[] arguments)
|
||||
{
|
||||
client.SendMessage("/entity [id]: Shows information about this entity.");
|
||||
}
|
||||
}
|
||||
|
||||
public class DestroyCommand : Command
|
||||
{
|
||||
public override string Name
|
||||
|
@ -75,6 +75,7 @@ namespace TrueCraft
|
||||
private TcpListener Listener;
|
||||
private readonly PacketHandler[] PacketHandlers;
|
||||
private IList<ILogProvider> LogProviders;
|
||||
private Stopwatch Time;
|
||||
private ConcurrentBag<Tuple<IWorld, IChunk>> ChunksToSchedule;
|
||||
internal object ClientLock = new object();
|
||||
|
||||
@ -109,6 +110,7 @@ namespace TrueCraft
|
||||
QueryProtocol = new TrueCraft.QueryProtocol(this);
|
||||
WorldLighters = new List<WorldLighting>();
|
||||
ChunksToSchedule = new ConcurrentBag<Tuple<IWorld, IChunk>>();
|
||||
Time = new Stopwatch();
|
||||
|
||||
AccessConfiguration = Configuration.LoadConfiguration<AccessConfiguration>("access.yaml");
|
||||
|
||||
@ -124,6 +126,8 @@ namespace TrueCraft
|
||||
public void Start(IPEndPoint endPoint)
|
||||
{
|
||||
ShuttingDown = false;
|
||||
Time.Reset();
|
||||
Time.Start();
|
||||
Listener = new TcpListener(endPoint);
|
||||
Listener.Start();
|
||||
EndPoint = (IPEndPoint)Listener.LocalEndpoint;
|
||||
@ -135,7 +139,7 @@ namespace TrueCraft
|
||||
AcceptClient(this, args);
|
||||
|
||||
Log(LogCategory.Notice, "Running TrueCraft server on {0}", EndPoint);
|
||||
EnvironmentWorker.Change(MillisecondsPerTick, 0);
|
||||
EnvironmentWorker.Change(MillisecondsPerTick, Timeout.Infinite);
|
||||
if(Program.ServerConfiguration.Query)
|
||||
QueryProtocol.Start();
|
||||
}
|
||||
@ -152,6 +156,16 @@ namespace TrueCraft
|
||||
DisconnectClient(c);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
EnvironmentWorker.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
EnvironmentWorker.Change(0, Timeout.Infinite);
|
||||
}
|
||||
|
||||
public void AddWorld(IWorld world)
|
||||
{
|
||||
Worlds.Add(world);
|
||||
@ -216,7 +230,7 @@ namespace TrueCraft
|
||||
if (Program.ServerConfiguration.EnableLighting)
|
||||
{
|
||||
var lighter = new WorldLighting(sender as IWorld, BlockRepository);
|
||||
lighter.InitialLighting(e.Chunk);
|
||||
lighter.InitialLighting(e.Chunk, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -385,6 +399,8 @@ namespace TrueCraft
|
||||
if (ShuttingDown)
|
||||
return;
|
||||
|
||||
long start = Time.ElapsedMilliseconds;
|
||||
long limit = Time.ElapsedMilliseconds + MillisecondsPerTick;
|
||||
Profiler.Start("environment");
|
||||
|
||||
Scheduler.Update();
|
||||
@ -396,15 +412,20 @@ namespace TrueCraft
|
||||
}
|
||||
Profiler.Done();
|
||||
|
||||
if (Program.ServerConfiguration.EnableLighting)
|
||||
{
|
||||
Profiler.Start("environment.lighting");
|
||||
foreach (var lighter in WorldLighters)
|
||||
{
|
||||
int attempts = 500;
|
||||
while (attempts-- > 0 && lighter.TryLightNext())
|
||||
while (Time.ElapsedMilliseconds < limit && lighter.TryLightNext())
|
||||
{
|
||||
// This space intentionally left blank
|
||||
}
|
||||
if (Time.ElapsedMilliseconds >= limit)
|
||||
Log(LogCategory.Warning, "Lighting queue is backed up");
|
||||
}
|
||||
Profiler.Done();
|
||||
}
|
||||
|
||||
Profiler.Start("environment.chunks");
|
||||
Tuple<IWorld, IChunk> t;
|
||||
@ -413,8 +434,12 @@ namespace TrueCraft
|
||||
Profiler.Done();
|
||||
|
||||
Profiler.Done(MillisecondsPerTick);
|
||||
long end = Time.ElapsedMilliseconds;
|
||||
long next = MillisecondsPerTick - (end - start);
|
||||
if (next < 0)
|
||||
next = 0;
|
||||
|
||||
EnvironmentWorker.Change(MillisecondsPerTick, 0);
|
||||
EnvironmentWorker.Change(next, Timeout.Infinite);
|
||||
}
|
||||
|
||||
public bool PlayerIsWhitelisted(string client)
|
||||
|
@ -98,6 +98,11 @@ namespace TrueCraft
|
||||
if (progress % 10 == 0)
|
||||
Server.Log(LogCategory.Notice, "{0}% complete", progress + 10);
|
||||
}
|
||||
Server.Log(LogCategory.Notice, "Lighting the world (this will take a moment)...");
|
||||
foreach (var lighter in Server.WorldLighters)
|
||||
{
|
||||
while (lighter.TryLightNext()) ;
|
||||
}
|
||||
}
|
||||
world.Save();
|
||||
CommandManager = new CommandManager();
|
||||
@ -107,10 +112,14 @@ namespace TrueCraft
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(1000 * ServerConfiguration.WorldSaveInterval);
|
||||
Server.Pause();
|
||||
Server.Log(LogCategory.Notice, "Saving world...");
|
||||
foreach (var w in Server.Worlds)
|
||||
{
|
||||
w.Save();
|
||||
}
|
||||
Server.Log(LogCategory.Notice, "Done.");
|
||||
Server.Resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user