Refactored server stop and world save operations fixing race conditions so as to ensure operations always happen in a predicable order. This fixes output not appearing in the console / log for example. This adds TShock.Utils.StopServer method used by IGA, rcon and the RestAPI.
Fixed console title set not working Optimised command line parsing Made Utils a singleton to enforce the fact that only one copy should ever exist Added name to /v2/user/read output as users can be found by id
This commit is contained in:
parent
84789ff4d5
commit
d34199b17d
7 changed files with 228 additions and 121 deletions
123
TShockAPI/SaveManager.cs
Normal file
123
TShockAPI/SaveManager.cs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using Terraria;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
class SaveManager : IDisposable
|
||||
{
|
||||
// Singleton
|
||||
private static readonly SaveManager instance = new SaveManager();
|
||||
private SaveManager()
|
||||
{
|
||||
_saveThread = new Thread(SaveWorker);
|
||||
_saveThread.Name = "TShock SaveManager Worker";
|
||||
_saveThread.Start();
|
||||
}
|
||||
public static SaveManager Instance { get { return instance; } }
|
||||
|
||||
// Producer Consumer
|
||||
private EventWaitHandle _wh = new AutoResetEvent(false);
|
||||
private Object _saveLock = new Object();
|
||||
private Queue<SaveTask> _saveQueue = new Queue<SaveTask>();
|
||||
private Thread _saveThread;
|
||||
private int saveQueueCount { get { lock (_saveLock) return _saveQueue.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// SaveWorld event handler which notifies users that the server may lag
|
||||
/// </summary>
|
||||
public void OnSaveWorld(bool resettime = false, HandledEventArgs e = null)
|
||||
{
|
||||
TShock.Utils.Broadcast("Saving world. Momentary lag might result from this.", Color.Red);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the map data
|
||||
/// </summary>
|
||||
/// <param name="wait">wait for all pending saves to finish (default: true)</param>
|
||||
/// <param name="resetTime">reset the last save time counter (default: false)</param>
|
||||
/// <param name="direct">use the realsaveWorld method instead of saveWorld event (default: false)</param>
|
||||
public void SaveWorld(bool wait = true, bool resetTime = false, bool direct = false)
|
||||
{
|
||||
EnqueueTask(new SaveTask(resetTime, direct));
|
||||
if (!wait)
|
||||
return;
|
||||
|
||||
// Wait for all outstanding saves to complete
|
||||
int count = saveQueueCount;
|
||||
while (0 != count)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
count = saveQueueCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes any outstanding saves, shutsdown the save thread and returns
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
EnqueueTask(null);
|
||||
_saveThread.Join();
|
||||
_wh.Close();
|
||||
}
|
||||
|
||||
private void EnqueueTask(SaveTask task)
|
||||
{
|
||||
lock (_saveLock)
|
||||
{
|
||||
_saveQueue.Enqueue(task);
|
||||
}
|
||||
_wh.Set();
|
||||
}
|
||||
|
||||
private void SaveWorker()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (_saveLock)
|
||||
{
|
||||
// NOTE: lock for the entire process so wait works in SaveWorld
|
||||
if (_saveQueue.Count > 0)
|
||||
{
|
||||
SaveTask task = _saveQueue.Dequeue();
|
||||
if (null == task)
|
||||
return;
|
||||
else
|
||||
{
|
||||
if (task.direct)
|
||||
{
|
||||
OnSaveWorld();
|
||||
WorldGen.realsaveWorld(task.resetTime);
|
||||
}
|
||||
else
|
||||
WorldGen.saveWorld(task.resetTime);
|
||||
TShock.Utils.Broadcast("World saved.", Color.Yellow);
|
||||
Log.Info(string.Format("World saved at ({0})", Main.worldPathName));
|
||||
}
|
||||
}
|
||||
}
|
||||
_wh.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
class SaveTask
|
||||
{
|
||||
public bool resetTime { get; set; }
|
||||
public bool direct { get; set; }
|
||||
public SaveTask(bool resetTime, bool direct)
|
||||
{
|
||||
this.resetTime = resetTime;
|
||||
this.direct = direct;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("resetTime {0}, direct {1}", resetTime, direct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue