From 92a6e9685b24141837e3be5cfa6dc884a6c4f0a5 Mon Sep 17 00:00:00 2001 From: unknown <_ __ _> Date: Fri, 4 Dec 2020 17:26:23 -0500 Subject: [PATCH] Update --- .gitignore | 6 +- FiveM_TimeSync.sln | 31 +++ FiveM_TimeSync/Client.cs | 13 ++ FiveM_TimeSync/FiveM_TimeSync.csproj | 62 +++++ FiveM_TimeSync/Modules/TimeSyncModule.cs | 80 +++++++ FiveM_TimeSync/Properties/AssemblyInfo.cs | 36 +++ .../FiveM_TimeSyncServer.csproj | 62 +++++ .../Modules/TimeSyncModule.cs | 217 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++ FiveM_TimeSyncServer/Server.cs | 13 ++ .../fivemtimesync/FiveM_TimeSync.net.dll | Bin 6656 -> 0 bytes .../FiveM_TimeSyncServer.net.dll | Bin 11264 -> 0 bytes Resources/fivemtimesync/__resource.lua | 2 - 13 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 FiveM_TimeSync.sln create mode 100644 FiveM_TimeSync/Client.cs create mode 100644 FiveM_TimeSync/FiveM_TimeSync.csproj create mode 100644 FiveM_TimeSync/Modules/TimeSyncModule.cs create mode 100644 FiveM_TimeSync/Properties/AssemblyInfo.cs create mode 100644 FiveM_TimeSyncServer/FiveM_TimeSyncServer.csproj create mode 100644 FiveM_TimeSyncServer/Modules/TimeSyncModule.cs create mode 100644 FiveM_TimeSyncServer/Properties/AssemblyInfo.cs create mode 100644 FiveM_TimeSyncServer/Server.cs delete mode 100644 Resources/fivemtimesync/FiveM_TimeSync.net.dll delete mode 100644 Resources/fivemtimesync/FiveM_TimeSyncServer.net.dll delete mode 100644 Resources/fivemtimesync/__resource.lua diff --git a/.gitignore b/.gitignore index a53bb84..28c4ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ *.zip -_releases \ No newline at end of file +_releases +.vs +.sln +bin +obj \ No newline at end of file diff --git a/FiveM_TimeSync.sln b/FiveM_TimeSync.sln new file mode 100644 index 0000000..0207f82 --- /dev/null +++ b/FiveM_TimeSync.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30114.105 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiveM_TimeSync", "FiveM_TimeSync\FiveM_TimeSync.csproj", "{E43819C4-7F54-408A-B097-794CBD245BAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiveM_TimeSyncServer", "FiveM_TimeSyncServer\FiveM_TimeSyncServer.csproj", "{A4E0CA53-9DCD-453F-83CD-3F7AB0F25807}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E43819C4-7F54-408A-B097-794CBD245BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E43819C4-7F54-408A-B097-794CBD245BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E43819C4-7F54-408A-B097-794CBD245BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E43819C4-7F54-408A-B097-794CBD245BAD}.Release|Any CPU.Build.0 = Release|Any CPU + {A4E0CA53-9DCD-453F-83CD-3F7AB0F25807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4E0CA53-9DCD-453F-83CD-3F7AB0F25807}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4E0CA53-9DCD-453F-83CD-3F7AB0F25807}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4E0CA53-9DCD-453F-83CD-3F7AB0F25807}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6433BD9B-8AB9-4772-BC1B-9C06B796AC4F} + EndGlobalSection +EndGlobal diff --git a/FiveM_TimeSync/Client.cs b/FiveM_TimeSync/Client.cs new file mode 100644 index 0000000..ed33e97 --- /dev/null +++ b/FiveM_TimeSync/Client.cs @@ -0,0 +1,13 @@ +using VinaFrameworkClient.Core; +using FiveM_TimeSync.Modules; + +namespace FiveM_TimeSync +{ + public class Client : BaseClient + { + public Client() + { + AddModule(typeof(TimeSyncModule)); + } + } +} diff --git a/FiveM_TimeSync/FiveM_TimeSync.csproj b/FiveM_TimeSync/FiveM_TimeSync.csproj new file mode 100644 index 0000000..ec65cf6 --- /dev/null +++ b/FiveM_TimeSync/FiveM_TimeSync.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {E43819C4-7F54-408A-B097-794CBD245BAD} + Library + Properties + FiveM_TimeSync + FiveM_TimeSync.net + v4.5.2 + 512 + true + + + false + none + false + ..\..\_resources\fivemtimesync\ + DEBUG;TRACE + prompt + 4 + Off + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\..\FiveM.app\citizen\clr2\lib\mono\4.5\CitizenFX.Core.dll + False + + + + + + + + + + + False + ..\..\_dependencies\VinaFrameworkClient.dll + + + + + + + + + + + + + \ No newline at end of file diff --git a/FiveM_TimeSync/Modules/TimeSyncModule.cs b/FiveM_TimeSync/Modules/TimeSyncModule.cs new file mode 100644 index 0000000..60836c5 --- /dev/null +++ b/FiveM_TimeSync/Modules/TimeSyncModule.cs @@ -0,0 +1,80 @@ +using System; +using System.Threading.Tasks; + +using CitizenFX.Core.Native; + +using VinaFrameworkClient.Core; + +namespace FiveM_TimeSync.Modules +{ + public class TimeSyncModule : Module + { + public TimeSyncModule(Client client) : base(client) + { + script.AddEvent("TimeSync.UpdateDateTime", new Action(OnUpdateDateTime)); + script.AddTick(OverrideTime); + } + + #region ACCESSORS + + public DateTime CurrentDate + { + get + { + return lastServerTime.AddMilliseconds(timeElapsed); + } + } + + public TimeSpan CurrentTime + { + get + { + return CurrentDate.TimeOfDay; + } + } + + private double timeElapsed + { + get + { + return DateTime.Now.Subtract(startingDate).TotalMilliseconds * timeRate; + } + } + + #endregion + #region VARIABLES + + private int timeRate; + private DateTime startingDate; + private DateTime lastServerTime; + + #endregion + #region MODULE EVENTS + + private void OnUpdateDateTime(int newTimeRate, long currentTicks) + { + timeRate = newTimeRate; + startingDate = DateTime.Now; + lastServerTime = new DateTime(currentTicks); + + script.Log($"Received update from server [TimeRate: {timeRate}, Server Time: ${lastServerTime}]"); + } + + #endregion + #region MODULES TICKS + + private async Task OverrideTime() + { + while (true) + { + await Client.Delay(33); + + if (startingDate == null || lastServerTime == null) continue; + + API.NetworkOverrideClockTime(CurrentTime.Hours, CurrentTime.Minutes, CurrentTime.Seconds); + } + } + + #endregion + } +} diff --git a/FiveM_TimeSync/Properties/AssemblyInfo.cs b/FiveM_TimeSync/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..47df24f --- /dev/null +++ b/FiveM_TimeSync/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FiveM_TimeSync")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FiveM_TimeSync")] +[assembly: AssemblyCopyright("Copyright © VinaStar 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e43819c4-7f54-408a-b097-794cbd245bad")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/FiveM_TimeSyncServer/FiveM_TimeSyncServer.csproj b/FiveM_TimeSyncServer/FiveM_TimeSyncServer.csproj new file mode 100644 index 0000000..4fde351 --- /dev/null +++ b/FiveM_TimeSyncServer/FiveM_TimeSyncServer.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {A4E0CA53-9DCD-453F-83CD-3F7AB0F25807} + Library + Properties + FiveM_TimeSyncServer + FiveM_TimeSyncServer.net + v4.5.2 + 512 + true + + + false + none + false + ..\..\_resources\fivemtimesync\ + DEBUG;TRACE + prompt + 4 + Off + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\..\fivem_server\citizen\clr2\lib\mono\4.5\CitizenFX.Core.dll + False + + + + + + + + + + + False + ..\..\_dependencies\VinaFrameworkServer.dll + + + + + + + + + + + + + \ No newline at end of file diff --git a/FiveM_TimeSyncServer/Modules/TimeSyncModule.cs b/FiveM_TimeSyncServer/Modules/TimeSyncModule.cs new file mode 100644 index 0000000..a0ea5eb --- /dev/null +++ b/FiveM_TimeSyncServer/Modules/TimeSyncModule.cs @@ -0,0 +1,217 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +using CitizenFX.Core; +using CitizenFX.Core.Native; + +using VinaFrameworkServer.Core; + +namespace FiveM_TimeSyncServer.Modules +{ + public class TimeSyncModule : Module + { + public TimeSyncModule(Server server) : base(server) + { + endingDate = LoadCurrentTime(); + lastServerTime = DateTime.Now; + + script.AddTick(NetworkResync); + script.AddTick(AutosaveTime); + script.AddTick(PeriodicConsolePrint); + + script.SetExport("CurrentDateTicks", new Func(ExportCurrentDateTicks)); + } + + #region ACCESSORS + + public DateTime RealDate + { + get + { + return DateTime.Now; + } + } + + public TimeSpan RealTime + { + get + { + return RealDate.TimeOfDay; + } + } + + public DateTime CurrentDate + { + get + { + return endingDate.AddMilliseconds(timeElapsed); + } + } + + public TimeSpan CurrentTime + { + get + { + return CurrentDate.TimeOfDay; + } + } + + private double timeElapsed + { + get + { + return DateTime.Now.Subtract(lastServerTime).TotalMilliseconds * timeRate; + } + } + + #endregion + #region VARIABLES + + private bool verbose; + private bool printEnabled; + private string printFormat; + private int printDelay; + private int clientUpdateDelay; + private int timeRate; + private DateTime endingDate; + private DateTime lastServerTime; + + #endregion + #region BASE EVENTS + + protected override void OnModuleInitialized() + { + // Print more informations + verbose = API.GetConvarInt("timesync_network_verbose", 0) == 0; + + // Peridically print current time + printEnabled = API.GetConvarInt("timesync_console_print_time", 0) == 0; + + // Console Print Time Format + printFormat = API.GetConvar("timesync_console_print_format", "MMMM d yyyy, HH:mm:ss tt"); + + // Console Print Time Delay + printDelay = API.GetConvarInt("timesync_console_print_delay", 1000 * 60 * 5); + + // Sync players every 10 secs + clientUpdateDelay = API.GetConvarInt("timesync_update_delay", 60000); + + // 10 x realtime + timeRate = API.GetConvarInt("timesync_timerate", 1); + + Debug.WriteLine($@" +===================================== +FIVEM TIME SYNC SETTINGS: +===================================== + timesync_console_print = {printEnabled} + timesync_console_print_format = {printFormat} + timesync_console_print_delay (ms) = {printDelay} + timesync_update_delay (ms) = {clientUpdateDelay} + timesync_timerate (1sec * timerate) = {timeRate} + Current Date = {CurrentDate.ToString(printFormat)} +====================================="); + } + + protected override void OnPlayerConnecting(Player player) + { + + } + + protected override void OnPlayerDropped(Player player, string reason) + { + + } + + protected override void OnPlayerClientInitialized(Player player) + { + UpdatePlayerDateTime(player); + } + + #endregion + #region MODULE TICKS + + private async Task NetworkResync() + { + await Server.Delay(clientUpdateDelay); + + UpdatePlayerDateTime(); + } + + private async Task AutosaveTime() + { + await Server.Delay(30000); + + await SaveCurrentTime(); + } + + private async Task PeriodicConsolePrint() + { + await Server.Delay(printDelay); + + if (!printEnabled) return; + + script.Log($@"SERVER CURRENT TIME: {CurrentDate.ToString(printFormat)}"); + } + + #endregion + #region MODULE METHODS + + private void UpdatePlayerDateTime(Player player = null) + { + if (player != null) + { + Server.TriggerClientEvent(player, "TimeSync.UpdateDateTime", timeRate, CurrentDate.Ticks); + if (verbose) script.Log($"FiveM TimeSync syncing player ${player.Name} time!"); + } + else + { + if (API.GetNumPlayerIndices() > 0) + { + Server.TriggerClientEvent("TimeSync.UpdateDateTime", timeRate, CurrentDate.Ticks); + if (verbose) script.Log($"FiveM TimeSync syncing all players time!"); + } + else if (verbose) script.Log($"FiveM TimeSync no online players, skipping syncing..."); + } + } + + private DateTime LoadCurrentTime() + { + try + { + string fileData = File.ReadAllText("server_time.txt"); + script.Log("Loaded time from server_time.txt"); + return DateTime.Parse(fileData); + } + catch (Exception exception) + { + script.Log("Could not get time from server_time.txt, starting with real time."); + } + + return DateTime.Now; + } + + private async Task SaveCurrentTime() + { + try + { + using (StreamWriter writer = File.CreateText("server_time.txt")) + { + await writer.WriteAsync(CurrentDate.ToString()); + writer.Close(); + } + } + catch (Exception exception) + { + script.LogError(exception); + } + } + + private long ExportCurrentDateTicks() + { + return CurrentDate.Ticks; + } + + #endregion + } +} diff --git a/FiveM_TimeSyncServer/Properties/AssemblyInfo.cs b/FiveM_TimeSyncServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f7794af --- /dev/null +++ b/FiveM_TimeSyncServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FiveM_TimeSyncServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FiveM_TimeSyncServer")] +[assembly: AssemblyCopyright("Copyright © VinaStar 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a4e0ca53-9dcd-453f-83cd-3f7ab0f25807")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/FiveM_TimeSyncServer/Server.cs b/FiveM_TimeSyncServer/Server.cs new file mode 100644 index 0000000..24707fe --- /dev/null +++ b/FiveM_TimeSyncServer/Server.cs @@ -0,0 +1,13 @@ +using VinaFrameworkServer.Core; +using FiveM_TimeSyncServer.Modules; + +namespace FiveM_TimeSyncServer +{ + public class Server : BaseServer + { + public Server() + { + AddModule(typeof(TimeSyncModule)); + } + } +} diff --git a/Resources/fivemtimesync/FiveM_TimeSync.net.dll b/Resources/fivemtimesync/FiveM_TimeSync.net.dll deleted file mode 100644 index 1de66688ad6d72372a7af68ed60e14078001b9bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6656 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