Add automatic server update (need testing)

This commit is contained in:
sardelka9515
2022-08-21 19:13:12 +08:00
parent 9e66762061
commit 2d2da624e4
15 changed files with 273 additions and 181 deletions

View File

@ -119,7 +119,7 @@ namespace RageCoop.Client.Installer
if (Directory.Exists("RageCoop"))
{
UpdateStatus("Installing...");
CopyFilesRecursively(new DirectoryInfo("RageCoop"),new DirectoryInfo(installPath));
CoreUtils.CopyFilesRecursively(new DirectoryInfo("RageCoop"),new DirectoryInfo(installPath));
Finish();
}
else
@ -248,12 +248,6 @@ namespace RageCoop.Client.Installer
{
return (byte[])Resource.ResourceManager.GetObject("LemonUI_SHVDN3");
}
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name),true);
}
}
}

View File

@ -16,7 +16,7 @@ using System.Resources;
// Version informationr(
[assembly: AssemblyVersion("1.5.1.48")]
[assembly: AssemblyFileVersion("1.5.1.48")]
[assembly: AssemblyVersion("1.5.1.49")]
[assembly: AssemblyFileVersion("1.5.1.49")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -182,10 +182,16 @@ namespace RageCoop.Core
return JsonConvert.DeserializeObject<IpInfo>(content);
}
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), true);
}
}
internal struct IpInfo
internal class IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;
namespace RageCoop.Core
{
@ -10,6 +11,7 @@ namespace RageCoop.Core
{
public static void DownloadFile(string url,string destination,Action<int> progressCallback)
{
if (File.Exists(destination)) { File.Delete(destination); }
AutoResetEvent ae=new AutoResetEvent(false);
WebClient client = new WebClient();
@ -18,7 +20,7 @@ namespace RageCoop.Core
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => progressCallback(e1.ProgressPercentage);
client.DownloadProgressChanged += (s, e1) => progressCallback?.Invoke(e1.ProgressPercentage);
client.DownloadFileCompleted += (s, e2) =>
{
ae.Set();
@ -26,5 +28,15 @@ namespace RageCoop.Core
client.DownloadFileAsync(new Uri(url), destination);
ae.WaitOne();
}
public static string DownloadString(string url)
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient();
return client.DownloadString(url);
}
}
}

View File

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using RageCoop.Core;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Diagnostics;
namespace RageCoop.Server
{
public partial class Server
{
const string _versionURL = "https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/main/RageCoop.Server/Properties/AssemblyInfo.cs";
private void SendPlayerUpdate()
{
foreach (var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID = c.Player.ID,
Username = c.Username,
Latency = c.Latency,
Position = c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch (Exception ex)
{
Logger?.Error(ex);
}
}
}
private IpInfo IpInfo = null;
private void Announce()
{
HttpResponseMessage response = null;
HttpClient httpClient = new();
if (IpInfo == null)
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
try
{
IpInfo = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {IpInfo.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
}
try
{
Security.GetPublicKey(out var pModulus, out var pExpoenet);
var serverInfo = new ServerInfo
{
address = IpInfo.Address,
port = Settings.Port.ToString(),
country = IpInfo.Country,
name = Settings.Name,
version = Version.ToString(),
players = MainNetServer.ConnectionsCount.ToString(),
maxPlayers = Settings.MaxPlayers.ToString(),
description = Settings.Description,
website = Settings.Website,
gameMode = Settings.GameMode,
language = Settings.Language,
useP2P = Settings.UseP2P,
useZT = Settings.UseZeroTier,
ztID = Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress = Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus = Convert.ToBase64String(pModulus),
publicKeyExponent = Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
return;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{response.Content.ReadAsStringAsync().GetAwaiter().GetResult()}]");
}
}
}
private void CheckUpdate()
{
try
{
var versionLine = HttpHelper.DownloadString(_versionURL).Split('\n', StringSplitOptions.RemoveEmptyEntries).Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
var start = versionLine.IndexOf('\"') + 1;
var end = versionLine.LastIndexOf('\"');
var latest = Version.Parse(versionLine.AsSpan(start, end - start));
if (latest <= Version) { return; }
API.SendChatMessage($"New server version found: {latest}, downloading update...");
var downloadURL = $"https://github.com/RAGECOOP/RAGECOOP-V/releases/download/nightly/RageCoop.Server-{GetRID()}.zip";
if (Directory.Exists("Update")) { Directory.Delete("Update", true); }
HttpHelper.DownloadFile(downloadURL, "Update.zip", null);
Logger?.Info("Installing update");
Directory.CreateDirectory("Update");
new FastZip().ExtractZip("Update.zip", "Update", FastZip.Overwrite.Always, null, null, null, true);
Process.Start(Path.Combine("Update", RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "RageCoop.Server.exe": "RageCoop.Server"), "update \"" + AppDomain.CurrentDomain.BaseDirectory + "\"");
Stop();
Environment.Exit(0);
}
catch(Exception ex)
{
Logger?.Error("Update",ex);
}
}
static string GetRID()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-"+RuntimeInformation.OSArchitecture.ToString().ToLower();
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-"+RuntimeInformation.OSArchitecture.ToString().ToLower();
}
return "unknown";
}
}
}

View File

@ -14,6 +14,7 @@ using Lidgren.Network;
using System.Timers;
using System.Security.Cryptography;
using RageCoop.Server.Scripting;
using Timer = System.Timers.Timer;
using System.Net.Sockets;
using System.Threading.Tasks;
using RageCoop.Core.Scripting;
@ -44,13 +45,14 @@ namespace RageCoop.Server
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
internal Resources Resources;
internal Logger Logger;
private Security Security;
internal Security Security;
private bool _stopping = false;
private Thread _listenerThread;
private Thread _announceThread;
private Thread _latencyThread;
private Worker _worker;
private HashSet<char> _allowedCharacterSet;
private readonly Thread _listenerThread;
private readonly Timer _announceTimer = new();
private readonly Timer _playerUpdateTimer = new();
private readonly Timer _updateTimer = new();
private readonly Worker _worker;
private readonly HashSet<char> _allowedCharacterSet;
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new();
/// <summary>
@ -80,134 +82,28 @@ namespace RageCoop.Server
_worker=new Worker("ServerWorker", Logger);
_listenerThread=new Thread(() => Listen());
_latencyThread=new Thread(() =>
_announceTimer.Interval = 1;
_announceTimer.Elapsed += (s, e) =>
{
while (!_stopping)
{
foreach(var c in ClientsByNetHandle.Values.ToArray())
{
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.Player.ID,
Username=c.Username,
Latency=c.Latency,
Position=c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch(Exception ex)
{
Logger?.Error(ex);
}
}
Thread.Sleep(1000);
}
});
_announceThread=new Thread(async () =>
_announceTimer.Interval = 10000;
_announceTimer.Stop();
Announce();
_announceTimer.Start();
};
_playerUpdateTimer.Interval = 1000;
_playerUpdateTimer.Elapsed += (s, e) => SendPlayerUpdate();
_updateTimer.Interval = 1;
_updateTimer.Elapsed += (s, e) =>
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
HttpClient httpClient = new();
IpInfo info;
try
{
info = CoreUtils.GetIPInfo();
Logger?.Info($"Your public IP is {info.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
while (!_stopping)
{
HttpResponseMessage response = null;
try
{
Security.GetPublicKey(out var pModulus,out var pExpoenet);
var serverInfo = new ServerInfo
{
address = info.Address,
port=Settings.Port.ToString(),
country=info.Country,
name=Settings.Name,
version=Version.ToString(),
players=MainNetServer.ConnectionsCount.ToString(),
maxPlayers=Settings.MaxPlayers.ToString(),
description=Settings.Description,
website=Settings.Website,
gameMode=Settings.GameMode,
language=Settings.Language,
useP2P=Settings.UseP2P,
useZT=Settings.UseZeroTier,
ztID=Settings.UseZeroTier ? Settings.ZeroTierNetworkID : "",
ztAddress=Settings.UseZeroTier ? ZeroTierHelper.Networks[Settings.ZeroTierNetworkID].Addresses.Where(x => !x.Contains(":")).First() : "0.0.0.0",
publicKeyModulus=Convert.ToBase64String(pModulus),
publicKeyExponent=Convert.ToBase64String(pExpoenet)
};
string msg = JsonConvert.SerializeObject(serverInfo);
var realUrl = Util.GetFinalRedirect(Settings.MasterServer);
response = await httpClient.PostAsync(realUrl, new StringContent(msg, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
// Sleep for 5s
Thread.Sleep(5000);
continue;
}
if (response == null)
{
Logger?.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = await response.Content.ReadAsStringAsync();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]");
}
}
// Sleep for 10s
for (int i = 0; i<10; i++)
{
if (_stopping)
{
break;
}
else
{
Thread.Sleep(1000);
}
}
}
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
}
});
_updateTimer.Interval= 1000 * 60 * 10; // 10 minutes
_updateTimer.Stop();
CheckUpdate();
_updateTimer.Start();
};
}
@ -256,10 +152,14 @@ namespace RageCoop.Server
BaseScript.OnStart();
Resources.LoadAll();
_listenerThread.Start();
_latencyThread.Start();
_playerUpdateTimer.Enabled=true;
if (Settings.AnnounceSelf)
{
_announceThread.Start();
_announceTimer.Enabled=true;
}
if (Settings.AutoUpdate)
{
_updateTimer.Enabled = true;
}
Logger?.Info("Listening for clients");
@ -269,14 +169,12 @@ namespace RageCoop.Server
/// </summary>
public void Stop()
{
_stopping = true;
Logger?.Flush();
Logger?.Dispose();
_stopping = true;
_listenerThread.Join();
_latencyThread.Join();
if (_announceThread.IsAlive)
{
_announceThread.Join();
}
_playerUpdateTimer.Enabled = false;
_announceTimer.Enabled = false;
_worker.Dispose();
}
private void Listen()
@ -546,8 +444,6 @@ namespace RageCoop.Server
}.Pack(msg);
MainNetServer.SendMessage(msg,c.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
}
Logger?.Info(name + ": " + message);
}
internal void SendChatMessage(string name, string message, Client target)
{

View File

@ -5,6 +5,8 @@ using System.Threading;
using System.Threading.Tasks;
using RageCoop.Core;
using Newtonsoft.Json;
using System.Linq;
namespace RageCoop.Server
{
class Program
@ -13,6 +15,25 @@ namespace RageCoop.Server
static Logger mainLogger;
static void Main(string[] args)
{
if (args.Length>=2 && args[0]=="update")
{
var target = args[1];
int i =0;
while (i < 10)
{
try
{
CoreUtils.CopyFilesRecursively(new(AppDomain.CurrentDomain.BaseDirectory), new(target));
Process.Start(Path.Combine(target, "RageCoop.Server"));
Environment.Exit(0);
}
catch
{
Thread.Sleep(3000);
}
}
return;
}
AppDomain.CurrentDomain.UnhandledException+=UnhandledException;
mainLogger = new Logger()
{

View File

@ -14,8 +14,8 @@ using System.Resources;
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version informationr(
[assembly: AssemblyVersion("1.5.1.34")]
[assembly: AssemblyFileVersion("1.5.1.34")]
// Version information
[assembly: AssemblyVersion("1.5.1.62")]
[assembly: AssemblyFileVersion("1.5.1.62")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -32,7 +32,7 @@ using System.Resources;
[assembly: AssemblyTrademark("RAGECOOP")]
[assembly: AssemblyCulture("")]
// Version informationr(
// Version information
[assembly: AssemblyVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: AssemblyFileVersion("<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
[assembly: NeutralResourcesLanguageAttribute( "en-US" )]

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
<_LastSelectedProfileId>M:\SandBox-Shared\repos\RAGECOOP\RAGECOOP-V\RageCoop.Server\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>

View File

@ -209,14 +209,13 @@ namespace RageCoop.Server.Scripting
raiseEvent ??= targets==null;
try
{
if (Server.MainNetServer.ConnectionsCount == 0)
if (Server.MainNetServer.ConnectionsCount != 0)
{
return;
}
targets ??= new(Server.ClientsByNetHandle.Values);
foreach(Client client in targets)
{
Server.SendChatMessage(username, message, client);
targets ??= new(Server.ClientsByNetHandle.Values);
foreach (Client client in targets)
{
Server.SendChatMessage(username, message, client);
}
}
}
catch (Exception e)

View File

@ -43,6 +43,8 @@ namespace RageCoop.Server.Scripting
{
API.SendCustomEventQueued(API.GetAllClients().Values.Where(x=>x!=e.Client).ToList(),CustomEvents.OnPlayerDied,e.Client.Username);
});
API.Events.OnChatMessage+=(s,e) =>
Server.Logger?.Info((e.Client?.Username ?? e.ClaimedSender ?? "Unknown") + ": " + e.Message);
}
public override void OnStop()
{

View File

@ -50,11 +50,11 @@ namespace RageCoop.Server.Scripting
zip.BeginUpdate();
foreach (var dir in Directory.GetDirectories(resourceFolder, "*", SearchOption.AllDirectories))
{
zip.AddDirectory(dir.Substring(resourceFolder.Length+1));
zip.AddDirectory(dir[(resourceFolder.Length + 1)..]);
}
foreach (var file in Directory.GetFiles(resourceFolder, "*", SearchOption.AllDirectories))
{
zip.Add(file, file.Substring(resourceFolder.Length+1));
zip.Add(file, file[(resourceFolder.Length + 1)..]);
}
zip.CommitUpdate();
zip.Close();

View File

@ -104,6 +104,11 @@
/// The zerotier network id to join, default value is zerotier's public Earth network.
/// </summary>
public string ZeroTierNetworkID { get; set; } = "8056c2e21c000001";
/// <summary>
/// Automatically update to nightly build when an update is avalible, check is performed every 10 minutes.
/// </summary>
public bool AutoUpdate { get; set; } = false;
/*
/// <summary>
/// Kick godmode and spamming idiots

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace RageCoop.Server
{
internal static class Updater
{
}
}