C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Oxide.Core;
using UnityEngine;
using Oxide.Core.Database;
namespace Oxide.Plugins {
[Info("CustomAdventCalendar", "ThePitereq", "2.0.1")]
public class CustomAdventCalendar : RustPlugin {
private Core.MySql.Libraries.MySql sqlLibrary = Interface.Oxide.GetLibrary<Core.MySql.Libraries.MySql>();
private Connection sqlConnection;
private List<ulong> blockedRedeem = new List<ulong>();
private void OnServerInitialized() {
config = Config.ReadObject<PluginConfig>();
Config.WriteObject(config);
if (config.useSql) {
sqlConnection = sqlLibrary.OpenDb(config.sqlConfig.ip, config.sqlConfig.port, config.sqlConfig.dbName, config.sqlConfig.username, config.sqlConfig.password, this);
Sql sqlCommand1 = Sql.Builder.Append("CREATE TABLE IF NOT EXISTS redeemedRewards_user ( `id` int(11) NOT NULL AUTO_INCREMENT, `userid` VARCHAR(17) NOT NULL, `date` VARCHAR(16) NOT NULL, `hour` VARCHAR(16) NOT NULL, `count` int(3) NOT NULL, PRIMARY KEY (id) );");
sqlLibrary.Insert(sqlCommand1, sqlConnection);
if (config.rewardMethod) {
Sql sqlCommand2 = Sql.Builder.Append("CREATE TABLE IF NOT EXISTS redeemedRewards_daily ( `userid` VARCHAR(17) NOT NULL, `dayCount` int(3) NOT NULL );");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
}
string today = DateTime.Now.ToShortDateString();
Sql sqlCommand3 = Sql.Builder.Append($"DELETE FROM redeemedRewards_user WHERE date NOT LIKE '{today}';");
sqlLibrary.Delete(sqlCommand3, sqlConnection);
}
else LoadData();
if (!config.rewardMethod && config.rewards.Count <= 30) {
PrintWarning($"You've set less rewards than 31 ({config.rewards.Count}) and plugin will not work correctly in last days of the month! Add rewards for remaining days and load plugin again!");
Server.Command($"oxide.unload {this.Name}");
}
}
private void Unload() {
if (!config.useSql) SaveData();
else sqlLibrary.CloseDb(sqlConnection);
}
private bool CanBeAwardedAdventGift(AdventCalendar calendar, BasePlayer player) {
if (blockedRedeem.Contains(player.userID)) return false;
blockedRedeem.Add(player.userID);
timer.Once(5, () => blockedRedeem.Remove(player.userID));
return true;
}
private object OnAdventGiftAward(AdventCalendar calendar, BasePlayer player) {
string today = DateTime.Now.ToShortDateString();
string now = DateTime.Now.ToString("dd/MM/yyyy HH:mm");
if (config.useSql) {
int dayReward = -10;
if (config.rewardMethod) {
bool redeemed = false;
Sql sqlCommand1 = Sql.Builder.Append($"SELECT userId,dayCount FROM redeemedRewards_daily WHERE userId='{player.userID}';");
sqlLibrary.Query(sqlCommand1, sqlConnection, list1 => {
if (list1 == null) return;
else if (list1.Count == 0) {
sqlCommand1 = Sql.Builder.Append($"INSERT INTO redeemedRewards_daily (userId,dayCount) VALUES ('{player.userID}',-1);");
sqlLibrary.Insert(sqlCommand1, sqlConnection);
dayReward = -1;
}
else dayReward = Convert.ToInt32(list1[0]["dayCount"]);
Sql sqlCommand2 = Sql.Builder.Append($"SELECT userId,date,hour,count FROM redeemedRewards_user WHERE date='{today}' AND userId='{player.UserIDString}';");
int todayUses = 0;
sqlLibrary.Query(sqlCommand2, sqlConnection, list2 => {
bool increased = false;
if (list2 == null) return;
else if (list2.Count == 0) {
sqlCommand2 = Sql.Builder.Append($"INSERT INTO redeemedRewards_user (userId,date,hour,count) VALUES ('{player.userID}','{today}','{now}',0);");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
}
else {
todayUses = (int)list2[0]["count"];
if (todayUses == 0) {
dayReward++;
increased = true;
}
if ((int)list2[0]["count"] >= config.rewards[dayReward].maxDaily) {
player.SendConsoleCommand("gametip.showtoast", 1, Lang("MaxDailyReached", player.UserIDString));
redeemed = true;
return;
}
DateTime lastUsage = DateTime.ParseExact((string)list2[0]["hour"], "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);
double minDifference = DateTime.Now.Subtract(lastUsage).TotalMinutes;
if (minDifference < config.rewards[dayReward].cooldown) {
string time = Math.Ceiling(config.rewards[dayReward].cooldown - minDifference).ToString("0");
player.SendConsoleCommand("gametip.showtoast", 1, Lang("RedeemCooldown", player.UserIDString, time));
redeemed = true;
return;
}
}
if (todayUses == 0 && !increased) dayReward++;
sqlCommand2 = Sql.Builder.Append($"UPDATE redeemedRewards_user SET count={todayUses + 1},hour='{now}' WHERE date='{today}' AND userId='{player.UserIDString}';");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
if (todayUses == 0) {
sqlCommand2 = Sql.Builder.Append($"UPDATE redeemedRewards_daily SET dayCount={dayReward} WHERE userId='{player.UserIDString}';");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
}
RewardPlayer(player, dayReward, todayUses + 1, config.rewards[dayReward].maxDaily);
}
);
if (redeemed) return;
}
);
if (redeemed) return false;
}
else {
dayReward = DateTime.Now.Day - 1;
Sql sqlCommand2 = Sql.Builder.Append($"SELECT userId,date,hour,count FROM redeemedRewards_user WHERE date='{today}' AND userId='{player.UserIDString}';");
bool redeemed = false;
int todayUses = 0;
sqlLibrary.Query(sqlCommand2, sqlConnection, list => {
if (list == null) return;
else if (list.Count == 0) {
sqlCommand2 = Sql.Builder.Append($"INSERT INTO redeemedRewards_user (userId,date,hour,count) VALUES ('{player.userID}','{today}','{now}',0);");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
}
else {
todayUses = (int)list[0]["count"];
if ((int)list[0]["count"] >= config.rewards[dayReward].maxDaily) {
player.SendConsoleCommand("gametip.showtoast", 1, Lang("MaxDailyReached", player.UserIDString));
redeemed = true;
return;
}
DateTime lastUsage = DateTime.ParseExact((string)list[0]["hour"], "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);
double minDifference = DateTime.Now.Subtract(lastUsage).TotalMinutes;
if (minDifference < config.rewards[dayReward].cooldown) {
string time = Math.Ceiling(config.rewards[dayReward].cooldown - minDifference).ToString("0");
player.SendConsoleCommand("gametip.showtoast", 1, Lang("RedeemCooldown", player.UserIDString, time));
redeemed = true;
return;
}
}
sqlCommand2 = Sql.Builder.Append($"UPDATE redeemedRewards_user SET count={todayUses + 1} WHERE date='{today}' AND userId='{player.UserIDString}';");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
if (config.rewardMethod && todayUses == 0) {
sqlCommand2 = Sql.Builder.Append($"UPDATE redeemedRewards_daily SET dayCount={dayReward + 1} WHERE userId='{player.UserIDString}';");
sqlLibrary.Insert(sqlCommand2, sqlConnection);
}
RewardPlayer(player, dayReward, todayUses + 1, config.rewards[dayReward].maxDaily);
}
);
if (redeemed) return false;
}
}
else {
data.userData.TryAdd(today, new Dictionary<ulong, UserData>());
bool firstUse = false;
if (!data.userData[today].ContainsKey(player.userID)) {
firstUse = true;
data.userData[today].Add(player.userID, new UserData() {
lastUsed = now, timesUsed = 1
}
);
if (config.rewardMethod) {
if (data.redeemCounter.ContainsKey(player.userID)) data.redeemCounter[player.userID]++;
else data.redeemCounter.Add(player.userID, 0);
}
}
int dayReward = config.rewardMethod ? data.redeemCounter[player.userID] : DateTime.Now.Day - 1;
if (dayReward >= config.rewards.Count) dayReward = config.rewards.Count - 1;
if (!firstUse) {
if (data.userData[today][player.userID].timesUsed >= config.rewards[dayReward].maxDaily) {
player.SendConsoleCommand("gametip.showtoast", 1, Lang("MaxDailyReached", player.UserIDString));
return false;
}
DateTime lastUsage = DateTime.ParseExact(data.userData[today][player.userID].lastUsed, "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture);
double minDifference = DateTime.Now.Subtract(lastUsage).TotalMinutes;
if (minDifference < config.rewards[dayReward].cooldown) {
string time = Math.Ceiling(config.rewards[dayReward].cooldown - minDifference).ToString("0");
player.SendConsoleCommand("gametip.showtoast", 1, Lang("RedeemCooldown", player.UserIDString, time));
return false;
}
data.userData[today][player.userID].timesUsed++;
data.userData[today][player.userID].lastUsed = now;
}
RewardPlayer(player, dayReward, data.userData[today][player.userID].timesUsed, config.rewards[dayReward].maxDaily);
}
return false;
}
private void RewardPlayer(BasePlayer player, int dayReward, int timesUsed, int maxDaily) {
int rolledReward = Core.Random.Range(0, config.rewards[dayReward].itemRewards.Count);
foreach (var item in config.rewards[dayReward].itemRewards[rolledReward]) {
if (item.command != string.Empty) Server.Command(item.command.Replace("{userId}", player.UserIDString).Replace("{userName}", player.displayName));
if (item.shortname != string.Empty) {
Item itemReward = ItemManager.CreateByName(item.shortname, Core.Random.Range(item.minAmount, item.maxAmount), item.skin);
if (itemReward == null) {
Puts($"ERROR! Item {item.shortname} is not valid item!");
continue;
}
itemReward.name = item.displayName;
player.SendConsoleCommand("note.inv", itemReward.info.itemid, itemReward.amount, item.displayName == string.Empty ? itemReward.info.displayName.english : itemReward.name);
if (!itemReward.MoveToContainer(player.inventory.containerMain)) itemReward.Drop(player.eyes.transform.position, Vector3.zero);
}
}
if (config.consoleLogs) Puts($"Player {player.displayName} ({player.userID}) redeemed an reward no. {dayReward}!");
if (timesUsed != maxDaily) player.SendConsoleCommand("gametip.showtoast", 0, Lang("RewardRedeemedNext", player.UserIDString, config.rewards[dayReward].cooldown));
else player.SendConsoleCommand("gametip.showtoast", 0, Lang("RewardRedeemed", player.UserIDString));
Effect.server.Run("assets/prefabs/misc/easter/painted eggs/effects/gold_open.prefab", player.eyes.position);
}
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["MaxDailyReached"]
= "You've reached daily limit of rewards!", ["RedeemCooldown"] = "Daily reward on cooldown!\nYou need to wait {0} minutes...", ["RewardRedeemedNext"] = "Enjoy your reward!\nYou can redeem it again in {0} minutes!", ["RewardRedeemed"] = "Enjoy your reward!\nCome back tommorow for more!",
}
, this);
}
private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private PluginConfig config;
protected override void LoadDefaultConfig() {
Config.WriteObject(config = new PluginConfig() {
rewards = new List<RewardConfig>() {
new RewardConfig() {
itemRewards = new List<List<ItemConfig>>() {
new List<ItemConfig>() {
new ItemConfig() {
shortname = "metal.fragments", minAmount = 10000, maxAmount = 10000,
}
, new ItemConfig() {
shortname = "wood", minAmount = 15000, maxAmount = 15000,
}
}
, new List<ItemConfig>() {
new ItemConfig() {
shortname = "stones", minAmount = 10000, maxAmount = 10000,
}
, new ItemConfig() {
shortname = "sulfur", minAmount = 10000, maxAmount = 15000,
}
}
}
}
, new RewardConfig() {
maxDaily = 1, cooldown = 0, itemRewards = new List<List<ItemConfig>>() {
new List<ItemConfig>() {
new ItemConfig() {
command = "oxide.grant user {userId} permission.here"
}
, new ItemConfig() {
command = "say Player {userName} redeemed an reward!"
}
}
}
}
, new RewardConfig() {
maxDaily = 1, cooldown = 0, itemRewards = new List<List<ItemConfig>>() {
new List<ItemConfig>() {
new ItemConfig() {
shortname = "box.repair.bench", skin = 2795785961, displayName = "Recycler"
}
}
}
}
}
}
, true);
}
private class PluginConfig {
[JsonProperty("Count Rewards Per Player (true) or Count Rewards Per Day of Month (false)")]
public bool rewardMethod = true;
[JsonProperty("Enable Console Logs")]
public bool consoleLogs = true;
[JsonProperty("Use SQL Database")]
public bool useSql = false;
[JsonProperty("SQL Credentials")]
public SqlConfig sqlConfig = new SqlConfig();
[JsonProperty("Reward List")]
public List<RewardConfig> rewards = new List<RewardConfig>();
}
private class SqlConfig {
[JsonProperty("IP")]
public string ip = "127.0.0.1";
[JsonProperty("Port")]
public int port = 3306;
[JsonProperty("Database Name")]
public string dbName = "CustomAdventCalendarPro";
[JsonProperty("Username")]
public string username = "admin";
[JsonProperty("Password")]
public string password = "YourSuperSecretPassword";
}
private class RewardConfig {
[JsonProperty("Max Daily Redeems")]
public int maxDaily = 1;
[JsonProperty("Cooldown (in minutes, works only if Max Daily Redeems more than 1)")]
public int cooldown = 360;
[JsonProperty("Item Rewards")]
public List<List<ItemConfig>> itemRewards = new List<List<ItemConfig>>();
}
private class ItemConfig {
[JsonProperty("Command (Ignored if empty)")]
public string command = string.Empty;
[JsonProperty("Shortname")]
public string shortname = string.Empty;
[JsonProperty("Minimum Amount")]
public int minAmount = 1;
[JsonProperty("Maximum Amount")]
public int maxAmount = 1;
[JsonProperty("Skin")]
public ulong skin = 0;
[JsonProperty("Display Name")]
public string displayName = string.Empty;
}
private static PluginData data;
private class PluginData {
[JsonProperty("Daily User Data")]
public Dictionary<string, Dictionary<ulong, UserData>> userData = new Dictionary<string, Dictionary<ulong, UserData>>();
[JsonProperty("Reward Redeemed Counter")]
public Dictionary<ulong, int> redeemCounter = new Dictionary<ulong, int>();
}
private class UserData {
[JsonProperty("Last Used")]
public string lastUsed = string.Empty;
[JsonProperty("Times Used")]
public int timesUsed = 0;
}
private void LoadData() {
data = Interface.Oxide.DataFileSystem.ReadObject<PluginData>(this.Name);
timer.Every(Core.Random.Range(500, 700), SaveData);
}
private void SaveData() => Interface.Oxide.DataFileSystem.WriteObject(this.Name, data);
}
}