C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Database;
using Oxide.Core.Libraries;
using Oxide.Core.SQLite.Libraries;
namespace Oxide.Plugins
{
[Info("Connection DB", "Iv Misticos", "1.0.3")]
[Description("Connection database for developers.")]
class ConnectionDB : RustPlugin
{
#region Variables
// TODO: Finish auto load MySQL - SQLite
// TODO: Fix FileSystem (old LoadCustomData must be in the plugin (._.) )
// Yes i am a little bit stupid and removed it
private Dictionary<ulong, PlayerData> _data = new Dictionary<ulong, PlayerData>();
private Dictionary<string, string> _pluginData = new Dictionary<string, string>();
private Time _time = GetLibrary<Time>();
private Core.MySql.Libraries.MySql _mysql = GetLibrary<Core.MySql.Libraries.MySql>();
private SQLite _sqlite = GetLibrary<SQLite>();
private Connection _connection;
#endregion
#region Configuration
private Configuration _config;
private class Configuration
{
[JsonProperty(PropertyName = "Debug")]
public bool Debug = false;
[JsonProperty(PropertyName = "Storage: Files (0), MySQL (1), SQLite (2)")]
public int StorageType = 0;
[JsonProperty(PropertyName = "MySQL: Host")]
public string MySQLHost = "127.0.0.1";
[JsonProperty(PropertyName = "MySQL: Port")]
public int MySQLPort = 0;
[JsonProperty(PropertyName = "MySQL: Database")]
public string MySQLDatabase = "connectiondb";
[JsonProperty(PropertyName = "MySQL: User")]
public string MySQLUser = "connectiondb";
[JsonProperty(PropertyName = "MySQL: Password")]
public string MySQLPassword = "";
[JsonProperty(PropertyName = "Time Between Self-Deletion and Last Connection (sec)")]
public uint TimeToDelete = 259200;
}
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
}
catch
{
Config.WriteObject(_config, false, $"{Interface.Oxide.ConfigDirectory}/{Name}.jsonError");
PrintError("The configuration file contains an error and has been replaced with a default config.\n" +
"The error configuration file was saved in the.jsonError extension");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void LoadDefaultConfig() => _config = new Configuration();
protected override void SaveConfig() => Config.WriteObject(_config);
#endregion
#region Work with Data
private void LoadData()
{
switch (_config.StorageType)
{
case 0:
{
LoadData();
break;
}
case 1:
{
SaveDataMySQL();
break;
}
case 2:
{
SaveDataSQLite();
break;
}
}
var data = GetPluginData(Name);
if (string.IsNullOrEmpty(data))
{
var oldData = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<ulong, PlayerData>>(Name);
_data = oldData ?? new Dictionary<ulong, PlayerData>();
}
else
{
var deserializedData = JsonConvert.DeserializeObject<Dictionary<ulong, PlayerData>>(data, new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});
if (deserializedData == null)
_data = new Dictionary<ulong, PlayerData>();
}
}
private void SaveData()
{
switch (_config.StorageType)
{
case 0:
{
SaveDataFileSystem();
break;
}
case 1:
{
SaveDataMySQL();
break;
}
case 2:
{
SaveDataSQLite();
break;
}
}
}
private class PlayerData
{
public List<string> Names;
// ReSharper disable once InconsistentNaming
public List<string> IPs;
public List<uint> TimeStamps;
public uint SecondsPlayed;
}
#endregion
#region Storage Helpers
// Data Saving and Loading
private void SaveDataMySQL()
{
foreach (var kvp in _pluginData)
{
var query = $"INSERT INTO connectiondb ( title, data ) VALUES ( {kvp.Key}, {kvp.Value} ) ON DUPLICATE KEY UPDATE title=\"{kvp.Key}\", data=\"{kvp.Value}\"";
PrintDebug($"Query: {query}");
var sql = Sql.Builder.Append(query);
_mysql.ExecuteNonQuery(sql, _connection);
}
}
private void LoadDataMySQL()
{
var query = "SELECT * FROM connectiondb";
PrintDebug($"Query: {query}");
var sql = Sql.Builder.Append(query);
_mysql.Query(sql, _connection, list =>
{
});
}
private void SaveDataSQLite()
{
}
private void LoadDataSQLite()
{
}
private void SaveDataFileSystem()
{
foreach (var kvp in _pluginData)
{
Interface.Oxide.DataFileSystem.WriteObject($"ConnectionDB/{kvp.Key}", kvp.Value);
}
}
private string LoadCustomDataFileSystem(string key) => Interface.GetMod().DataFileSystem.ReadObject<string>($"ConnectionDB/{key}");
// DataBase Helpers
private void DatabaseInitialize()
{
// MySQL
if (_config.StorageType == 1)
{
var sql = Sql.Builder.Append("CREATE TABLE IF NOT EXISTS connectiondb (title TINYTEXT PRIMARY KEY, data LONGTEXT)");
_mysql.Insert(sql, _connection);
}
// SQLite
else if (_config.StorageType == 2)
{
var sql = Sql.Builder.Append("CREATE TABLE IF NOT EXISTS connectiondb (title TEXT PRIMARY KEY, data TEXT)");
_sqlite.Insert(sql, _connection);
}
}
#endregion
#region Hooks
private void OnServerInitialized()
{
if (_config.StorageType == 1)
{
_connection = _mysql.OpenDb(_config.MySQLHost, _config.MySQLPort, _config.MySQLDatabase, _config.MySQLUser, _config.MySQLPassword, this);
}
else if (_config.StorageType == 2)
{
var file = $"{Interface.GetMod().DataDirectory}/{Name}";
PrintDebug($"SQLite File Path: {file}");
_connection = _sqlite.OpenDb(file, this);
}
LoadData();
Puts($"Loaded users: {ConnectionsCount()}.");
var players = BasePlayer.activePlayerList;
var playersCount = players.Count;
for (var i = 0; i < playersCount; i++)
OnPlayerInit(players[i]);
var users = _data.ToArray();
var usersCount = users.Length;
var timeStamp = _time.GetUnixTimestamp();
var deleted = 0;
for (var i = 0; i < usersCount; i++)
{
var kvp = users[i];
if (kvp.Value.TimeStamps[kvp.Value.TimeStamps.Count - 1] + _config.TimeToDelete >
timeStamp) continue;
_data.Remove(kvp.Key);
deleted++;
}
PrintDebug($"Deleted old users: {deleted}.");
SaveData();
cmd.AddConsoleCommand("connectiondb.wipe", this, CommandConsoleWipe);
cmd.AddConsoleCommand("connectiondb.debuginfo", this, CommandConsoleDebugInfo);
}
private void OnServerSave() => SaveData();
private void Unload() => SaveData();
private void OnPlayerInit(BasePlayer player) => InitPlayer(player);
// ReSharper disable once UnusedParameter.Local
private void OnPlayerDisconnected(BasePlayer player, string reason) => InitPlayer(player, true);
#endregion
#region Commands
private bool CommandConsoleDebugInfo(ConsoleSystem.Arg arg)
{
if (!arg.IsRcon && !arg.IsConnectionAdmin)
return false;
arg.ReplyWith($"Plugin: {Name}\n" +
$"Version: {Version}\n" +
$"Debug enabled: {_config.Debug}\n" +
$"Server IP: {ConVar.Server.ip}\n" +
$"Average FPS: {Performance.current.frameRateAverage}\n" +
$"Data entries: {ConnectionsCount()}");
return true;
}
private bool CommandConsoleWipe(ConsoleSystem.Arg arg)
{
if (!arg.IsRcon && !arg.IsConnectionAdmin)
return false;
_data.Clear();
SaveData();
arg.ReplyWith("Wipe was completed");
return true;
}
#endregion
#region Helpers
private void InitPlayer(BasePlayer player, bool isDisconnect = false)
{
var id = player.userID;
var name = player.displayName;
var ip = player.net.connection.ipaddress;
var time = _time.GetUnixTimestamp();
ip = ip.Substring(0, ip.LastIndexOf(':'));
PlayerData p;
if (!_data.TryGetValue(id, out p))
{
var info = new PlayerData
{
Names = new List<string> {name},
IPs = new List<string> {ip},
TimeStamps = new List<uint> {time},
SecondsPlayed = 1
};
PrintDebug($"Added new user {name} ({id})");
_data.Add(id, info);
SaveData();
return;
}
if (!p.Names.Contains(name))
p.Names.Add(name);
if (!p.IPs.Contains(ip))
p.Names.Add(ip);
if (isDisconnect)
p.SecondsPlayed += time - p.TimeStamps.Last();
p.TimeStamps.Add(time);
PrintDebug($"Updated user {name} ({id})");
}
private void PrintDebug(string s)
{
if (_config.Debug)
Puts(s);
}
private bool ConnectionDataExists(ulong steamid) => _data.ContainsKey(steamid);
private int ConnectionsCount() => _data.Count;
private string GetStringFromData(object data) => JsonConvert.SerializeObject(data);
private string GetPluginData(string key)
{
PrintDebug($"Getting data from key '{key}'..");
return _pluginData.ContainsKey(key) ? _pluginData[key] : string.Empty;
}
#endregion
#region API
// General API
private List<string> API_GetNames(ulong id) => ConnectionDataExists(id) ? _data[id].Names : null;
private string API_GetFirstName(ulong id) => ConnectionDataExists(id) ? _data[id].Names[0] : null;
private string API_GetLastName(ulong id) => ConnectionDataExists(id) ? _data[id].Names.Last() : null;
private List<string> API_GetIPs(ulong id) => ConnectionDataExists(id) ? _data[id].IPs : null;
private string API_GetFirstIP(ulong id) => ConnectionDataExists(id) ? _data[id].IPs[0] : null;
private string API_GetLastIP(ulong id) => ConnectionDataExists(id) ? _data[id].IPs.Last() : null;
private List<uint> API_GetTimeStamps(ulong id) => ConnectionDataExists(id) ? _data[id].TimeStamps : null;
private uint API_GetFirstTimeStamp(ulong id) => ConnectionDataExists(id) ? _data[id].TimeStamps[0] : 0;
private uint API_GetLastTimeStamp(ulong id) => ConnectionDataExists(id) ? _data[id].TimeStamps.Last() : 0;
private uint API_GetSecondsPlayed(ulong id) => ConnectionDataExists(id) ? _data[id].SecondsPlayed : 0;
// Custom API
private void API_SetValue(string key, object value) => _pluginData[key] = GetStringFromData(value);
private object API_GetValue(string key) => JsonConvert.DeserializeObject(GetPluginData(key), new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});
private string API_GetValueRaw(string key) => GetPluginData(key);
#endregion
}
}