diff --git a/.gitattributes b/.gitattributes index bdb0cab..1ff0c42 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,17 +1,63 @@ -# Auto detect text files and perform LF normalization +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### * text=auto -# Custom for Visual Studio -*.cs diff=csharp +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/workflows/rocket-plugin-release.yml b/.github/workflows/rocket-plugin-release.yml new file mode 100644 index 0000000..fa35591 --- /dev/null +++ b/.github/workflows/rocket-plugin-release.yml @@ -0,0 +1,11 @@ +name: Build & Create GitHub Release + +on: + workflow_dispatch: + push: + branches: + - master + +jobs: + call-rocket-plugin-release: + uses: RestoreMonarchyPlugins/github-actions/.github/workflows/rocket-plugin-release.yml@master diff --git a/.gitignore b/.gitignore index 7964536..9491a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,85 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user +*.userosscache *.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ -build/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ -# Roslyn cache directories -*.ide/ +# Visual Studio 2017 auto generated files +Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -#NUNIT +# NUnit *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -49,6 +89,7 @@ dlldata.c *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -64,14 +105,21 @@ _Chutzpah* ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap + +# Visual Studio Trace Files +*.e2e # TFS 2012 Local Workspace $tf/ @@ -84,18 +132,29 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding addin-in -.JustCode - # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + # NCrunch _NCrunch_* .*crunch*.local.xml +nCrunchTemp_* # MightyMoose *.mm.* @@ -123,42 +182,71 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -## TODO: Comment the next line if you want to checkin your -## web deploy settings but do note that will include unencrypted -## passwords -#*.pubxml - -# NuGet Packages Directory -packages/* -## TODO: If the tool you use requires repositories.config -## uncomment the next line -#!packages/repositories.config - -# Enable "build/" folder in the NuGet Packages folder since -# NuGet packages use it for MSBuild targets. -# This line needs to be after the ignore of the build folder -# (and the packages folder if the line above has been uncommented) -!packages/build/ - -# Windows Azure Build Output +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview +*.jfm *.pfx *.publishsettings -node_modules/ +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # RIA/Silverlight projects Generated_Code/ @@ -170,20 +258,106 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf *.ldf +*.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ -# LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml \ No newline at end of file +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/Libraries/I18N.West.dll b/Libraries/I18N.West.dll deleted file mode 100644 index bf7a5d6..0000000 Binary files a/Libraries/I18N.West.dll and /dev/null differ diff --git a/Libraries/I18N.dll b/Libraries/I18N.dll deleted file mode 100644 index 21afd5d..0000000 Binary files a/Libraries/I18N.dll and /dev/null differ diff --git a/Libraries/MySql.Data.dll b/Libraries/MySql.Data.dll deleted file mode 100644 index 03fd84a..0000000 Binary files a/Libraries/MySql.Data.dll and /dev/null differ diff --git a/Libraries/System.Data.dll b/Libraries/System.Data.dll deleted file mode 100644 index f051b61..0000000 Binary files a/Libraries/System.Data.dll and /dev/null differ diff --git a/Libraries/System.Management.dll b/Libraries/System.Management.dll deleted file mode 100644 index 0c04a72..0000000 Binary files a/Libraries/System.Management.dll and /dev/null differ diff --git a/Libraries/System.Transactions.dll b/Libraries/System.Transactions.dll deleted file mode 100644 index 0191347..0000000 Binary files a/Libraries/System.Transactions.dll and /dev/null differ diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs deleted file mode 100644 index 07cbb8f..0000000 --- a/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Uconomy")] -[assembly: AssemblyCompany("unturned.ROCKS")] -[assembly: AssemblyProduct("Rocket")] -[assembly: ComVisible(false)] -[assembly: Guid("34ca52ac-333c-45df-8a77-8c8f7c463faa")] -[assembly: AssemblyVersion("1.0.4.0")] diff --git a/Uconomy.csproj b/Uconomy.csproj deleted file mode 100644 index 5bb9d7c..0000000 --- a/Uconomy.csproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Debug - AnyCPU - {602882B6-F16C-4247-9F5D-21846BC4C09A} - Library - Properties - fr34kyn01535.Uconomy - Uconomy - v3.5 - 512 - 12.0.0 - 2.0 - - - - True - full - False - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - false - - - none - True - bin\Release\ - TRACE - prompt - 4 - false - - - ..\..\..\..\Desktop\Unturned\Unturned_Data\Managed\mods\ - false - - - - False - lib\Assembly-CSharp.dll - False - - - False - lib\Assembly-CSharp-firstpass.dll - False - - - False - Libraries\I18N.dll - False - - - False - Libraries\I18N.West.dll - False - - - False - Libraries\MySql.Data.dll - False - - - False - lib\Rocket.API.dll - False - - - lib\Rocket.Core.dll - False - - - False - lib\Rocket.Unturned.dll - False - - - - - - - - - - False - lib\UnityEngine.dll - False - - - - - - - - - - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - - - - - - - - - \ No newline at end of file diff --git a/Uconomy.sln b/Uconomy.sln index 076c947..77bd2a9 100644 --- a/Uconomy.sln +++ b/Uconomy.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uconomy", "Uconomy.csproj", "{602882B6-F16C-4247-9F5D-21846BC4C09A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uconomy", "Uconomy/Uconomy.csproj", "{602882B6-F16C-4247-9F5D-21846BC4C09A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/CommandBalance.cs b/Uconomy/Commands/CommandBalance.cs similarity index 64% rename from CommandBalance.cs rename to Uconomy/Commands/CommandBalance.cs index 745ef82..f58f3a7 100644 --- a/CommandBalance.cs +++ b/Uconomy/Commands/CommandBalance.cs @@ -1,11 +1,12 @@ -using Rocket.API; +using fr34kyn01535.Uconomy.Helpers; +using Rocket.API; using Rocket.Core.Logging; using Rocket.Unturned.Chat; using Rocket.Unturned.Player; using System; using System.Collections.Generic; -namespace fr34kyn01535.Uconomy +namespace fr34kyn01535.Uconomy.Commands { public class CommandBalance : IRocketCommand { @@ -47,8 +48,15 @@ public List Permissions public void Execute(IRocketPlayer caller, params string[] command) { - decimal balance = Uconomy.Instance.Database.GetBalance(caller.Id); - UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_balance_show", balance, Uconomy.Instance.Configuration.Instance.MoneyName)); + ThreadHelper.RunAsynchronously(() => + { + decimal balance = Uconomy.Instance.Database.GetBalance(caller.Id); + ThreadHelper.RunSynchronously(() => + { + UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_balance_show", balance, Uconomy.Instance.Configuration.Instance.MoneyName)); + }); + }); + } } } diff --git a/CommandPay.cs b/Uconomy/Commands/CommandPay.cs similarity index 50% rename from CommandPay.cs rename to Uconomy/Commands/CommandPay.cs index 85b7e03..50102dc 100644 --- a/CommandPay.cs +++ b/Uconomy/Commands/CommandPay.cs @@ -1,11 +1,10 @@ -using Rocket.API; -using Rocket.Core.Logging; +using fr34kyn01535.Uconomy.Helpers; +using Rocket.API; using Rocket.Unturned.Chat; using Rocket.Unturned.Player; -using System; using System.Collections.Generic; -namespace fr34kyn01535.Uconomy +namespace fr34kyn01535.Uconomy.Commands { public class CommandPay : IRocketCommand { @@ -54,7 +53,7 @@ public void Execute(IRocketPlayer caller, params string[] command) } UnturnedPlayer otherPlayer = UnturnedPlayer.FromName(command[0]); - if (otherPlayer !=null) + if (otherPlayer != null) { if (caller == otherPlayer) { @@ -63,36 +62,47 @@ public void Execute(IRocketPlayer caller, params string[] command) } decimal amount = 0; - if (!Decimal.TryParse(command[1], out amount) || amount <= 0) + if (!decimal.TryParse(command[1], out amount) || amount <= 0) { UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_pay_error_invalid_amount")); return; } - if (caller is ConsolePlayer) + ThreadHelper.RunAsynchronously(() => { - Uconomy.Instance.Database.IncreaseBalance(otherPlayer.Id, amount); - UnturnedChat.Say(otherPlayer.CSteamID, Uconomy.Instance.Translations.Instance.Translate("command_pay_console", amount, Uconomy.Instance.Configuration.Instance.MoneyName)); - } - else - { - - decimal myBalance = Uconomy.Instance.Database.GetBalance(caller.Id); - if ((myBalance - amount) <= 0) + if (caller is ConsolePlayer) { - UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_pay_error_cant_afford")); - return; + Uconomy.Instance.Database.IncreaseBalance(otherPlayer.Id, amount); + ThreadHelper.RunSynchronously(() => + { + UnturnedChat.Say(otherPlayer.CSteamID, Uconomy.Instance.Translations.Instance.Translate("command_pay_console", amount, Uconomy.Instance.Configuration.Instance.MoneyName)); + }); } else { - Uconomy.Instance.Database.IncreaseBalance(caller.Id, -amount); - UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_pay_private", otherPlayer.CharacterName, amount, Uconomy.Instance.Configuration.Instance.MoneyName)); - Uconomy.Instance.Database.IncreaseBalance(otherPlayer.Id, amount); - UnturnedChat.Say(otherPlayer.CSteamID, Uconomy.Instance.Translations.Instance.Translate("command_pay_other_private", amount, Uconomy.Instance.Configuration.Instance.MoneyName, caller.DisplayName)); - Uconomy.Instance.HasBeenPayed((UnturnedPlayer)caller, otherPlayer, amount); - } - } + decimal myBalance = Uconomy.Instance.Database.GetBalance(caller.Id); + if (myBalance - amount <= 0) + { + ThreadHelper.RunSynchronously(() => + { + UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_pay_error_cant_afford")); + }); + return; + } + else + { + Uconomy.Instance.Database.IncreaseBalance(caller.Id, -amount); + Uconomy.Instance.Database.IncreaseBalance(otherPlayer.Id, amount); + ThreadHelper.RunSynchronously(() => + { + UnturnedChat.Say(caller, Uconomy.Instance.Translations.Instance.Translate("command_pay_private", otherPlayer.CharacterName, amount, Uconomy.Instance.Configuration.Instance.MoneyName)); + UnturnedChat.Say(otherPlayer.CSteamID, Uconomy.Instance.Translations.Instance.Translate("command_pay_other_private", amount, Uconomy.Instance.Configuration.Instance.MoneyName, caller.DisplayName)); + Uconomy.Instance.HasBeenPayed((UnturnedPlayer)caller, otherPlayer, amount); + }); + } + } + }); } else { diff --git a/Database.cs b/Uconomy/Database.cs similarity index 70% rename from Database.cs rename to Uconomy/Database.cs index 4f2f846..a129fb5 100644 --- a/Database.cs +++ b/Uconomy/Database.cs @@ -1,6 +1,10 @@ -using MySql.Data.MySqlClient; +using fr34kyn01535.Uconomy.Helpers; +using fr34kyn01535.Uconomy.Models; +using MySql.Data.MySqlClient; using Rocket.Core.Logging; using System; +using System.Collections.Generic; +using System.Linq; namespace fr34kyn01535.Uconomy { @@ -8,7 +12,6 @@ public class DatabaseManager { internal DatabaseManager() { - new I18N.West.CP1250(); //Workaround for database encoding issues with mono CheckSchema(); } @@ -71,7 +74,10 @@ public decimal IncreaseBalance(string id, decimal increaseBy) object result = command.ExecuteScalar(); if (result != null) Decimal.TryParse(result.ToString(), out output); connection.Close(); - Uconomy.Instance.BalanceUpdated(id, increaseBy); + ThreadHelper.RunSynchronously(() => + { + Uconomy.Instance.BalanceUpdated(id, increaseBy); + }); } catch (Exception ex) { @@ -106,7 +112,54 @@ public void CheckSetupAccount(Steamworks.CSteamID id) { Logger.LogException(ex); } + } + + public List GetBalances(List ids) + { + List balances = new List(); + try + { + using (MySqlConnection connection = createConnection()) + { + string placeholders = string.Join(",", ids.Select(id => "?id" + id)); + string query = $"SELECT `steamId`, `balance` FROM `{Uconomy.Instance.Configuration.Instance.DatabaseTableName}` WHERE `steamId` IN ({placeholders})"; + + MySqlCommand command = new MySqlCommand(query, connection); + + for (int i = 0; i < ids.Count; i++) + { + command.Parameters.AddWithValue("?id" + ids[i], ids[i]); + } + + connection.Open(); + using (MySqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + balances.Add(new PlayerBalance + { + SteamId = reader.GetString("steamId"), + Balance = reader.GetDecimal("balance") + }); + } + } + } + + ThreadHelper.RunSynchronously(() => + { + // Trigger the event for each balance checked + foreach (var balance in balances) + { + Uconomy.Instance.OnBalanceChecked(balance.SteamId, balance.Balance); + } + }); + } + catch (Exception ex) + { + Logger.LogException(ex); + } + return balances; } internal void CheckSchema() @@ -121,7 +174,7 @@ internal void CheckSchema() if (test == null) { - command.CommandText = "CREATE TABLE `" + Uconomy.Instance.Configuration.Instance.DatabaseTableName + "` (`steamId` varchar(32) NOT NULL,`balance` decimal(15,2) NOT NULL DEFAULT '25.00',`lastUpdated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`steamId`)) "; + command.CommandText = "CREATE TABLE `" + Uconomy.Instance.Configuration.Instance.DatabaseTableName + "` (`steamId` varchar(32) NOT NULL,`balance` decimal(15,2) NOT NULL DEFAULT '25.00',`lastUpdated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`steamId`)) "; command.ExecuteNonQuery(); } connection.Close(); diff --git a/Uconomy/Helpers/ThreadHelper.cs b/Uconomy/Helpers/ThreadHelper.cs new file mode 100644 index 0000000..f762aac --- /dev/null +++ b/Uconomy/Helpers/ThreadHelper.cs @@ -0,0 +1,43 @@ +using Rocket.Core.Logging; +using Rocket.Core.Utils; +using SDG.Unturned; +using System; +using System.Threading; + +namespace fr34kyn01535.Uconomy.Helpers +{ + internal static class ThreadHelper + { + internal static void RunAsynchronously(System.Action action, string exceptionMessage = null) + { + if (Thread.CurrentThread != ThreadUtil.gameThread) + { + action.Invoke(); + return; + } + + ThreadPool.QueueUserWorkItem((_) => + { + try + { + action.Invoke(); + } + catch (Exception e) + { + RunSynchronously(() => Logger.LogException(e, exceptionMessage)); + } + }); + } + + internal static void RunSynchronously(System.Action action, float delaySeconds = 0) + { + if (Thread.CurrentThread == ThreadUtil.gameThread) + { + action.Invoke(); + return; + } + + TaskDispatcher.QueueOnMainThread(action, delaySeconds); + } + } +} diff --git a/Uconomy/Models/PlayerBalance.cs b/Uconomy/Models/PlayerBalance.cs new file mode 100644 index 0000000..1ced609 --- /dev/null +++ b/Uconomy/Models/PlayerBalance.cs @@ -0,0 +1,8 @@ +namespace fr34kyn01535.Uconomy.Models +{ + public class PlayerBalance + { + public string SteamId { get; set; } + public decimal Balance { get; set; } + } +} diff --git a/Uconomy/Services/ExperienceService.cs b/Uconomy/Services/ExperienceService.cs new file mode 100644 index 0000000..80d9bd0 --- /dev/null +++ b/Uconomy/Services/ExperienceService.cs @@ -0,0 +1,134 @@ +using fr34kyn01535.Uconomy.Helpers; +using fr34kyn01535.Uconomy.Models; +using Rocket.Unturned; +using Rocket.Unturned.Player; +using SDG.Unturned; +using Steamworks; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Logger = Rocket.Core.Logging.Logger; + +namespace fr34kyn01535.Uconomy.Services +{ + public class ExperienceService : MonoBehaviour + { + private Uconomy pluginInstance => Uconomy.Instance; + private UconomyConfiguration configuration => pluginInstance.Configuration.Instance; + + private Dictionary ignoreEvent = []; + public bool IsSynchronizing { get; private set; } = false; + + void Start() + { + InvokeRepeating(nameof(SyncPlayersExperience), 0, configuration.SyncIntervalSeconds); + U.Events.OnPlayerConnected += OnPlayerConnected; + U.Events.OnPlayerDisconnected += OnPlayerDisconnected; + PlayerSkills.OnExperienceChanged_Global += OnExperienceChanged; + } + + void OnDestroy() + { + U.Events.OnPlayerConnected -= OnPlayerConnected; + U.Events.OnPlayerDisconnected -= OnPlayerDisconnected; + PlayerSkills.OnExperienceChanged_Global -= OnExperienceChanged; + } + + private void OnPlayerConnected(UnturnedPlayer player) + { + ignoreEvent[player.CSteamID] = true; + player.Experience = 0; + + ThreadHelper.RunAsynchronously(() => + { + decimal balance = Uconomy.Instance.Database.GetBalance(player.CSteamID.ToString()); + ThreadHelper.RunSynchronously(() => + { + ignoreEvent[player.CSteamID] = true; + player.Experience = (uint)balance; + }); + }); + } + + private void OnPlayerDisconnected(UnturnedPlayer player) + { + ignoreEvent.Remove(player.CSteamID); + } + + private void OnExperienceChanged(PlayerSkills skills, uint lastKnownExperience) + { + CSteamID steamID = skills.player.channel.owner.playerID.steamID; + uint experience = skills.experience; + + if (!ignoreEvent.TryGetValue(steamID, out bool ignore) || ignore) + { + ignoreEvent[steamID] = false; + } else + { + int experienceDifference = (int)experience - (int)lastKnownExperience; + + if (experienceDifference != 0) + { + decimal balanceChange = experienceDifference; + ThreadHelper.RunAsynchronously(() => + { + Uconomy.Instance.Database.IncreaseBalance(steamID.ToString(), balanceChange); + }); + } + } + } + + private void SyncPlayersExperience() + { + if (IsSynchronizing) + { + pluginInstance.LogDebug("ExperienceService is already synchronizing."); + return; + } + + SteamPlayer[] players = PlayerTool.getSteamPlayers(); + if (players.Length == 0) + { + return; + } + + IsSynchronizing = true; + List playerIds = players.Select(x => x.playerID.steamID.ToString()).ToList(); + + ThreadHelper.RunAsynchronously(() => + { + List playerBalances = Uconomy.Instance.Database.GetBalances(playerIds); + pluginInstance.LogDebug($"Synchronizing {playerBalances.Count} player experiences."); + + ThreadHelper.RunSynchronously(() => + { + try + { + foreach (PlayerBalance playerBalance in playerBalances) + { + CSteamID steamID = new(Convert.ToUInt64(playerBalance.SteamId)); + Player player = PlayerTool.getPlayer(steamID); + + uint experienceBalance = (uint)playerBalance.Balance; + if (player.skills.experience == experienceBalance) + { + continue; + } + + ignoreEvent[steamID] = true; + player.skills.ServerSetExperience(experienceBalance); + } + + IsSynchronizing = false; + pluginInstance.LogDebug($"Synchronized {playerBalances.Count} player experiences."); + } catch (Exception e) + { + IsSynchronizing = false; + Logger.LogException(e); + } + }); + }); + } + } +} diff --git a/Uconomy.cs b/Uconomy/Uconomy.cs similarity index 51% rename from Uconomy.cs rename to Uconomy/Uconomy.cs index d3730aa..9def5ee 100644 --- a/Uconomy.cs +++ b/Uconomy/Uconomy.cs @@ -1,11 +1,10 @@ -using Rocket; +using fr34kyn01535.Uconomy.Helpers; +using fr34kyn01535.Uconomy.Services; using Rocket.API.Collections; +using Rocket.Core.Logging; using Rocket.Core.Plugins; using Rocket.Unturned; -using Rocket.Unturned.Events; using Rocket.Unturned.Player; -using Rocket.Unturned.Plugins; -using SDG; using Steamworks; using System; @@ -15,12 +14,28 @@ public class Uconomy : RocketPlugin { public DatabaseManager Database; public static Uconomy Instance; + public ExperienceService ExperienceService; protected override void Load() { Instance = this; Database = new DatabaseManager(); - U.Events.OnPlayerConnected+=Events_OnPlayerConnected; + + if (Configuration.Instance.SyncExperience) + { + ExperienceService = gameObject.AddComponent(); + } + + U.Events.OnPlayerConnected += Events_OnPlayerConnected; + + Logger.Log($"{Name} {Assembly.GetName().Version} has been loaded!", ConsoleColor.Yellow); + } + + protected override void Unload() + { + U.Events.OnPlayerConnected -= Events_OnPlayerConnected; + + Logger.Log($"{Name} has been unloaded!", ConsoleColor.Yellow); } public delegate void PlayerBalanceUpdate(UnturnedPlayer player, decimal amt); @@ -34,16 +49,16 @@ public override TranslationList DefaultTranslations { get { - return new TranslationList(){ - {"command_balance_show","Your current balance is: {0} {1}"}, - {"command_pay_invalid","Invalid arguments"}, - {"command_pay_error_pay_self","You cant pay yourself"}, - {"command_pay_error_invalid_amount","Invalid amount"}, - {"command_pay_error_cant_afford","Your balance does not allow this payment"}, - {"command_pay_error_player_not_found","Failed to find player"}, - {"command_pay_private","You paid {0} {1} {2}"}, - {"command_pay_console","You received a payment of {0} {1} "}, - {"command_pay_other_private","You received a payment of {0} {1} from {2}"}, + return new TranslationList() { + {"command_balance_show","Your current balance is: {0} {1}"}, + {"command_pay_invalid","Invalid arguments"}, + {"command_pay_error_pay_self","You cant pay yourself"}, + {"command_pay_error_invalid_amount","Invalid amount"}, + {"command_pay_error_cant_afford","Your balance does not allow this payment"}, + {"command_pay_error_player_not_found","Failed to find player"}, + {"command_pay_private","You paid {0} {1} {2}"}, + {"command_pay_console","You received a payment of {0} {1} "}, + {"command_pay_other_private","You received a payment of {0} {1} from {2}"}, }; } } @@ -72,10 +87,21 @@ internal void OnBalanceChecked(string SteamID, decimal balance) } } + internal void LogDebug(string message) + { + if (Configuration.Instance.Debug) + { + Logger.Log($"Debug >> {message}"); + } + } + private void Events_OnPlayerConnected(UnturnedPlayer player) { - //setup account - Database.CheckSetupAccount(player.CSteamID); + ThreadHelper.RunAsynchronously(() => + { + //setup account + Database.CheckSetupAccount(player.CSteamID); + }); } } } diff --git a/Uconomy/Uconomy.csproj b/Uconomy/Uconomy.csproj new file mode 100644 index 0000000..4fd26f4 --- /dev/null +++ b/Uconomy/Uconomy.csproj @@ -0,0 +1,17 @@ + + + + net48 + latest + fr34kyn01535.Uconomy + Uconomy + 2.0.0 + Uconomy + + + + + + + + \ No newline at end of file diff --git a/UconomyConfiguration.cs b/Uconomy/UconomyConfiguration.cs similarity index 74% rename from UconomyConfiguration.cs rename to Uconomy/UconomyConfiguration.cs index 00e1e42..c453c42 100644 --- a/UconomyConfiguration.cs +++ b/Uconomy/UconomyConfiguration.cs @@ -1,13 +1,11 @@ using Rocket.API; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace fr34kyn01535.Uconomy { public class UconomyConfiguration : IRocketPluginConfiguration { + public bool Debug; + public bool ShouldSerializeDebug() => Debug; public string DatabaseAddress; public string DatabaseUsername; public string DatabasePassword; @@ -17,9 +15,12 @@ public class UconomyConfiguration : IRocketPluginConfiguration public decimal InitialBalance; public string MoneyName; + public bool SyncExperience = false; + public float SyncIntervalSeconds = 5; public void LoadDefaults() { + Debug = false; DatabaseAddress = "localhost"; DatabaseUsername = "unturned"; DatabasePassword = "password"; @@ -28,6 +29,8 @@ public void LoadDefaults() DatabasePort = 3306; InitialBalance = 30; MoneyName = "Credits"; + SyncExperience = false; + SyncIntervalSeconds = 5; } } } diff --git a/lib/Assembly-CSharp-firstpass.dll b/lib/Assembly-CSharp-firstpass.dll deleted file mode 100644 index dcfc680..0000000 Binary files a/lib/Assembly-CSharp-firstpass.dll and /dev/null differ diff --git a/lib/Assembly-CSharp.dll b/lib/Assembly-CSharp.dll deleted file mode 100644 index 64cd121..0000000 Binary files a/lib/Assembly-CSharp.dll and /dev/null differ diff --git a/lib/Rocket.API.dll b/lib/Rocket.API.dll deleted file mode 100644 index 7099496..0000000 Binary files a/lib/Rocket.API.dll and /dev/null differ diff --git a/lib/Rocket.Core.dll b/lib/Rocket.Core.dll deleted file mode 100644 index b9a678a..0000000 Binary files a/lib/Rocket.Core.dll and /dev/null differ diff --git a/lib/Rocket.Unturned.dll b/lib/Rocket.Unturned.dll deleted file mode 100644 index e0ab221..0000000 Binary files a/lib/Rocket.Unturned.dll and /dev/null differ diff --git a/lib/UnityEngine.dll b/lib/UnityEngine.dll deleted file mode 100644 index f953314..0000000 Binary files a/lib/UnityEngine.dll and /dev/null differ