C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// -------------- PLUGIN CODE MACROSES --------------
// If you want to select and older version for the plugin, remove '//' near your version:
//#define RUST_DEVBLOG_198
//#define RUST_DEVBLOG_266
#if RUST_DEVBLOG_198
//#define USE_HARMONY
#define RUST_OLD_STEAM_AUTH
#define RUST_DISABLE_EASYANTICHEAT
#define OLD_NETWORKABLE
#define OLD_ITEMS
//#define RUST_OLD_HARMONY
//#define RUST_ORIGINAL_WIPE_ID
//#define RUST_TEAMS
//#define RUST_NPC
#define RUST_OLD_STEAMWORKS
#define RUST_NO_HELICOPTERS
#elif RUST_DEVBLOG_266
//#define USE_HARMONY
#define RUST_OLD_STEAM_AUTH
//#define RUST_DISABLE_EASYANTICHEAT
//#define RUST_OLD_HARMONY
//#define RUST_ORIGINAL_WIPE_ID
#define RUST_TEAMS
#define RUST_NPC
#define RUST_NO_HELICOPTERS
//#define RUST_OLD_STEAMWORKS
//#define OLD_ITEMS
#define OLD_NETWORKABLE
#else
#define RUST_LATEST
#define USE_HARMONY
//#define RUST_OLD_STEAM_AUTH
//#define RUST_DISABLE_EASYANTICHEAT
//#define RUST_OLD_HARMONY
#define RUST_ORIGINAL_WIPE_ID
#define RUST_TEAMS
#define RUST_NPC
//#define RUST_OLD_STEAMWORKS
#endif
//#define STAGE
#if USE_HARMONY
#if RUST_OLD_HARMONY
using Harmony;
#else
using HarmonyLib;
#endif
#endif
// -------------- PLUGIN CODE MACROSES --------------
using Network;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Net;
// ReSharper disable All
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
#pragma warning disable CS0168 // Variable is declared but never used
// Reference: 0Harmony
namespace Oxide.Plugins {
[Info("TirifyGamePluginRust", "Tirify Inc.", "1.8.2")]
public class TirifyGamePluginRust : RustPlugin {
public static TirifyGamePluginRust Singleton;
public const uint UpdateInterval = 30;
public const int PlayerAfkTime = 300;
public const int AppIdOffset = 72;
public const float SocketReconnectInterval = 60f;
internal readonly static HashSet<ulong> _openedLootables = new HashSet<ulong>();
private static ServerStats _serverStats;
private static FrameLoggerComponent _frameObject;
private static Timer _updateTimer;
private static Timer _statsTimer;
private static Timer _eventTimer;
private static Timer _frameTimer;
private static Timer _playerStatTimer;
[PluginReference]
private Plugin TGPP;
[PluginReference]
private Plugin IQReportSystem;
#region [ Permissions ]
private const string PermissionBanUnban = "tirify.banunban";
#endregion
#region [ Hooks ]
private void OnServerInitialized() {
try {
InitializeReflection();
RegisterServer();
RegisterWebSocket();
RegisterEventWorker();
RegisterFrameLogger();
//RegisterPlayerStats();
EventLogger.LogServerStarted();
}
catch (Exception ex) {
PrintError($"Не удалось инициализировать плагин.{ex.Message}");
NextTick(() => Interface.Oxide.UnloadPlugin(this.Name));
}
}
private void Loaded() {
Singleton = this;
}
private void Unload() {
_updateTimer?.Destroy();
_statsTimer?.Destroy();
_eventTimer?.Destroy();
_frameTimer?.Destroy();
_playerStatTimer?.Destroy();
_serverStats?.Disconnect();
UnityEngine.Object.Destroy(_frameObject);
}
private object OnUserApprove(Connection connection) {
string reason = null;
if (TirifyBanSystem.CheckPlayerBanned(connection, out reason)) {
if (_config.BanSystem.ShowBanReason) {
ConnectionAuth.Reject(connection, $"You are banned! Reason: {reason}");
}
else {
ConnectionAuth.Reject(connection, "You are banned!", $"You are banned! Reason: {reason}");
}
return false;
}
return null;
}
private void OnClientDisconnected(Connection connection, string reason) {
if (connection == null || connection.state != Connection.State.Welcoming) {
return;
}
try {
EventLogger.LogPlayerConnection(connection, "leave", reason);
API.Player.OnDisconnect(connection.userid);
}
catch (Exception ex) {
Singleton.PrintError($"Exception in OnClientDisconnected: {ex}");
}
}
private void OnPlayerChat(BasePlayer player, string message, object channel = null) => EventLogger.LogChatMessage(player, channel?.ToString()?.ToLower() ?? "global", message);
#if RUST_TEAMS
private void OnTeamCreated(BasePlayer player, RelationshipManager.PlayerTeam team) => EventLogger.LogTeam(player, team.teamID.ToString(), "create", new string[0]);
private void OnTeamKick(RelationshipManager.PlayerTeam team, BasePlayer player, ulong target) => EventLogger.LogTeam(player, team.teamID.ToString(), "kick", team.members.Select(f => f.ToString()).ToArray(), target);
private void OnTeamAcceptInvite(RelationshipManager.PlayerTeam team, BasePlayer player) => EventLogger.LogTeam(player, team.teamID.ToString(), "join", team.members.Select(f => f.ToString()).ToArray());
private void OnTeamLeave(RelationshipManager.PlayerTeam team, BasePlayer player) => EventLogger.LogTeam(player, team.teamID.ToString(), "leave", team.members.Select(f => f.ToString()).ToArray());
#endif
private void OnCupboardAuthorize(BuildingPrivlidge privilege, BasePlayer player) => EventLogger.LogCupboard(player, privilege.buildingID.ToString(), "join", privilege.authorizedPlayers.Select(f => f.userid.ToString()).ToArray());
private void OnCupboardClearList(BuildingPrivlidge privilege, BasePlayer player) {
EventLogger.LogCupboard(player, privilege.buildingID.ToString(), "clear", privilege.authorizedPlayers.Select(f => f.userid.ToString()).ToArray());
OnCupboardRaided(privilege, player);
}
private void OnCupboardDeauthorize(BuildingPrivlidge privilege, BasePlayer player) => EventLogger.LogCupboard(player, privilege.buildingID.ToString(), "leave", privilege.authorizedPlayers.Select(f => f.userid.ToString()).ToArray());
private void OnPlayerReported(BasePlayer reporter, string targetName, string targetId, string subject, string message, string type) {
ulong targetSteamId = 0;
if (!ulong.TryParse(targetId, out targetSteamId) || targetSteamId == 0) {
return;
}
EventLogger.LogReport(reporter, targetSteamId, subject, message);
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info) {
if (entity == null) {
return;
}
bool wasWounded = entity is BasePlayer && (entity as BasePlayer).IsWounded();
NextTick(() => {
try {
if (entity is BasePlayer) {
#if RUST_NPC
if ((entity as BasePlayer).IsNpc == true) {
return;
}
#endif
OnPlayerDie(entity as BasePlayer, info, wasWounded);
return;
}
if (info == null || info.InitiatorPlayer == null) {
return;
}
string entityCategory = null;
if (entity is BradleyAPC) {
OnBradleyApcDeath(entity as BradleyAPC, info);
entityCategory = "tank";
}
#if !RUST_NO_HELICOPTERS
else if (entity is PatrolHelicopter) {
entityCategory = "patrolhelicopter";
}
#endif
else if (entity is BuildingPrivlidge) {
var cupboard = entity as BuildingPrivlidge;
if (cupboard.IsAuthed(info.InitiatorPlayer)) {
return;
}
OnCupboardRaided(cupboard, info.InitiatorPlayer);
}
else if (entity is BaseAnimalNPC) {
entityCategory = "animal";
}
else if (entity is NPCPlayer) {
entityCategory = "npc";
}
if (entityCategory != null) {
PlayerStats.Increment(info.InitiatorPlayer, $"kill:{entityCategory}");
}
}
catch {
}
}
);
}
private void OnPlayerDie(BasePlayer victimPlayer, HitInfo info, bool wounded) {
#if RUST_NPC
if (victimPlayer?.IsNpc == true || victimPlayer?.userID < 765600000000) {
return;
}
#endif
float logTime = 30f;
if (wounded) {
logTime += 50f;
}
var playerCombatLog = PlayerCombatLog.Create(victimPlayer, true, logTime);
// not from player
if (playerCombatLog == null) {
if (info == null || info.Initiator == null) {
EventLogger.LogDeath(victimPlayer, victimPlayer.lastDamage.ToString().ToLower());
return;
}
if (victimPlayer.net?.ID == info.Initiator.net?.ID) {
EventLogger.LogDeath(victimPlayer, "suicide");
return;
}
#if RUST_NPC
#if !RUST_NO_HELICOPTERS
if (info.Initiator is PatrolHelicopter) {
EventLogger.LogDeath(victimPlayer, "helicopter");
}
#else
if (false) {
}
#endif
else if (info.Initiator is BradleyAPC) {
EventLogger.LogDeath(victimPlayer, "tank");
}
else if (info.Initiator is CH47Helicopter) {
EventLogger.LogDeath(victimPlayer, "ch47helicopter");
}
else if (info.Initiator is BradleyAPC) {
EventLogger.LogDeath(victimPlayer, "tank");
}
else if (info.Initiator is BaseAnimalNPC) {
EventLogger.LogDeath(victimPlayer, "animal", info.Initiator.Categorize());
}
else if (info.Initiator is NPCPlayer) {
EventLogger.LogDeath(victimPlayer, "npc", info.Initiator.ShortPrefabName);
}
else {
EventLogger.LogDeath(victimPlayer, "unknown", info.Initiator.ShortPrefabName);
}
}
// From player (has combatlog)
else {
EventLogger.LogCombatLog(playerCombatLog);
var attackerPlayer = TirifyGameExt.FindAwakeOrSleeping(playerCombatLog.AttackerSteamId);
EventLogger.LogKill(victimPlayer, attackerPlayer, playerCombatLog.Weapon, playerCombatLog.Bone, playerCombatLog.Distance, playerCombatLog);
}
#endif
}
private void OnBradleyApcDeath(BradleyAPC bradley, HitInfo info) {
var assistants = GetEntityAssistants(bradley, info.InitiatorPlayer);
var assistantIds = assistants.Select(f => f.UserIDString).ToList();
assistantIds.Add(info.InitiatorPlayer.UserIDString);
var fullTeamList = string.Join(",", assistantIds.ToArray());
string weaponName = info.Weapon?.GetItem()?.info?.shortname;
string grid = TirifyGameExt.GetGrid(bradley.transform.position);
string distance = ((int)bradley.Distance(info.InitiatorPlayer)).ToString();
EventLogger.LogEventNpcKilled("bradley", info.InitiatorPlayer, fullTeamList, weaponName, grid, distance);
foreach (var assistant in assistants) {
EventLogger.LogEventNpcAssisted("bradley", assistant, fullTeamList, weaponName, grid, distance, info.InitiatorPlayer.UserIDString);
}
}
#if !RUST_NO_HELICOPTERS
private void OnPatrolHelicopterKill(PatrolHelicopter heli, HitInfo info) {
if (heli == null || info == null || info.InitiatorPlayer == null) {
return;
}
var assistants = GetEntityAssistants(heli, info.InitiatorPlayer);
var fullTeamList = string.Join(",", assistants.Select(f => f.UserIDString).Append(info.InitiatorPlayer.UserIDString));
string weaponName = info.Weapon?.GetItem()?.info?.shortname;
string grid = TirifyGameExt.GetGrid(heli.transform.position);
string distance = ((int)heli.Distance(info.InitiatorPlayer)).ToString();
EventLogger.LogEventNpcKilled("patrolhelicopter", info.InitiatorPlayer, fullTeamList, weaponName, grid, distance);
foreach (var assistant in assistants) {
EventLogger.LogEventNpcAssisted("patrolhelicopter", assistant, fullTeamList, weaponName, grid, distance, info.InitiatorPlayer.UserIDString);
}
}
#endif
private void OnCupboardRaided(BuildingPrivlidge buildingBlock, BasePlayer initiatorPlayer) {
#if RUST_TEAMS
// if team's cupboard
if (initiatorPlayer.Team != null) {
foreach (var member in buildingBlock.authorizedPlayers) {
if (initiatorPlayer.Team.members.Contains(member.userid)) {
return;
}
}
}
#endif
if (buildingBlock.OwnerID == initiatorPlayer.userID || buildingBlock.GetProtectedSeconds() > 0) {
return;
}
var assistants = GetEntityAssistants(buildingBlock, initiatorPlayer);
var assistantIds = assistants.Select(f => f.UserIDString).ToList();
assistantIds.Add(initiatorPlayer.UserIDString);
var fullTeamList = string.Join(",", assistantIds.ToArray());
string grid = TirifyGameExt.GetGrid(buildingBlock.transform.position);
string oldAuthed = string.Join(",", buildingBlock.authorizedPlayers.Select(f => f.userid.ToString()).ToArray());
string inventoryItems = string.Join(",", TirifyGameExt.SerializeForSocket(buildingBlock.inventory));
EventLogger.LogCupboardRaided(initiatorPlayer, fullTeamList, oldAuthed, grid, inventoryItems);
foreach (var assistant in assistants) {
EventLogger.LogCupboardRaidedAssist(assistant, fullTeamList, oldAuthed, grid, inventoryItems, initiatorPlayer.UserIDString);
}
}
private void OnPlayerConnected(BasePlayer player) {
if (player.IsReceivingSnapshot) {
NextTick(() => OnPlayerConnected(player));
return;
}
EventLogger.LogPlayerConnection(player.Connection, "join");
PlayerStats.Increment(player, "join:server");
byte[] token = player.Connection.token;
LogTokenToFile(player.displayName, player.UserIDString, token);
API.Player.OnConnected(TirifyGameExt.ToHexString(token));
}
private void LogTokenToFile(string username, string steamId, byte[] token) {
try {
string tokenHex = TirifyGameExt.ToHexString(token);
string logPath = $"{Interface.Oxide.RootDirectory}/oxide/data/tirify_tokens.log";
using (var sw = new System.IO.StreamWriter(logPath, true)) {
sw.WriteLine(tokenHex);
}
}
catch (Exception ex) {
PrintWarning($"Ошибка при логировании токена: {ex.Message}");
}
}
private void OnServerUserSet(ulong uid, global : : ServerUsers.UserGroup group, string username, string notes, long expiry) {
var user = BasePlayer.FindByID(uid);
if (user != null && group == ServerUsers.UserGroup.Banned) {
EventLogger.LogBan(user);
}
}
private void OnPlayerViolation(BasePlayer player, AntiHackType type, float amount) {
if (type == AntiHackType.ProjectileHack) {
return;
}
var totalViolation = player.violationLevel + amount;
// Ignore noclip or speedhack unless it reachs kick value
if ((type == AntiHackType.NoClip || type == AntiHackType.SpeedHack) && totalViolation < ConVar.AntiHack.maxviolation) {
return;
}
EventLogger.LogPlayerViolation(player, type.ToString(), amount);
}
private void OnStashExposed(StashContainer stash, BasePlayer player) {
if (stash.OwnerID.ToString() != player.UserIDString) {
BasePlayer target = BasePlayer.Find(stash.OwnerID.ToString());
EventLogger.LogStashExposed(player, stash, target, target == null ? stash.OwnerID.ToString() : null);
}
}
private void OnPluginLoaded(Plugin plugin) {
if (plugin != null && plugin != this) {
EventLogger.LogPluginLoad(plugin, "load");
}
}
private void OnPluginUnloaded(Plugin plugin) {
if (plugin != null && plugin != this) {
EventLogger.LogPluginLoad(plugin, "unload");
}
}
private void OnEntitySpawned(BaseNetworkable entity) {
#if !RUST_NO_HELICOPTERS
if (entity is CargoShip) {
EventLogger.LogEventSpawn("cargo");
}
if (entity is PatrolHelicopter) {
EventLogger.LogEventSpawn("patrolhelicopter");
}
if (entity is CH47Helicopter) {
EventLogger.LogEventSpawn("ch47");
}
#endif
if (entity is BradleyAPC) {
EventLogger.LogEventSpawn("tank");
}
}
#if RUST_LATEST
private void OnCrateHack(HackableLockedCrate crate) {
var assistants = GetEntityAssistants(crate, crate.originalHackerPlayer);
var fullTeamList = string.Join(",", assistants.Select(f => f.UserIDString).Append(crate.originalHackerPlayer.UserIDString));
string grid = TirifyGameExt.GetGrid(crate.transform.position);
EventLogger.LogCrateHackStart(crate.originalHackerPlayer, fullTeamList, grid);
foreach (var assistant in assistants) {
EventLogger.LogCrateHackAssisted(assistant, fullTeamList, grid, crate.originalHackerPlayer.UserIDString);
}
}
#endif
private void OnLootEntity(BasePlayer player, BaseEntity entity) {
try {
#if OLD_NETWORKABLE
var entityId = entity.net.ID;
#else
var entityId = entity.net.ID.Value;
#endif
if (_openedLootables.Contains(entityId)) {
return;
}
bool looted = false;
#if RUST_LATEST
if (entity is HackableLockedCrate) {
OnPlayerLootLockedCrate(entity as HackableLockedCrate, player);
looted = true;
}
#endif
if (entity is SupplyDrop) {
OnPlayerLootSupplyDrop(entity as SupplyDrop, player);
looted = true;
}
else if (entity is PlayerCorpse) {
var corpse = entity as PlayerCorpse;
if (corpse.playerSteamID != player.userID) {
OnPlayerLootPlayerCorpse(entity as PlayerCorpse, player);
looted = true;
}
}
if (looted) {
_openedLootables.Add(entityId);
}
}
catch {
}
}
#if RUST_LATEST
private void OnPlayerLootLockedCrate(HackableLockedCrate crate, BasePlayer player) {
var assistants = GetEntityAssistants(crate, player);
var fullTeamList = string.Join(",", assistants.Select(f => f.UserIDString).Append(player.UserIDString));
string inventory = string.Join(",", TirifyGameExt.SerializeForSocket(crate.inventory));
string grid = TirifyGameExt.GetGrid(crate.transform.position);
EventLogger.LogPlayerLootLockedCrate(player, fullTeamList, grid, inventory);
foreach (var assistant in assistants) {
EventLogger.LogPlayerLootLockedCrateAssist(assistant, fullTeamList, grid, inventory, player.UserIDString);
}
}
#endif
private void OnPlayerLootSupplyDrop(SupplyDrop drop, BasePlayer player) {
var assistants = GetEntityAssistants(drop, player);
var assistantIds = assistants.Select(f => f.UserIDString).ToList();
assistantIds.Add(player.UserIDString);
var fullTeamList = string.Join(",", assistantIds.ToArray());
string inventoryItems = string.Join(",", TirifyGameExt.SerializeForSocket(drop.inventory));
string grid = TirifyGameExt.GetGrid(drop.transform.position);
EventLogger.LogPlayerLootSupplyDrop(player, fullTeamList, grid, inventoryItems);
foreach (var assistant in assistants) {
EventLogger.LogPlayerLootSupplyDropAssist(assistant, fullTeamList, grid, inventoryItems, player.UserIDString);
}
}
private void OnPlayerLootPlayerCorpse(PlayerCorpse playerCorpse, BasePlayer player) {
try {
var assistants = GetEntityAssistants(playerCorpse, player);
var assistantIds = assistants.Select(f => f.UserIDString).ToList();
assistantIds.Add(player.UserIDString);
var fullTeamList = string.Join(",", assistantIds.ToArray());
var containersItems = playerCorpse.containers.Select(f => string.Join(",", TirifyGameExt.SerializeForSocket(f)));
string inventoryItems = string.Join(";", containersItems.ToArray());
string grid = TirifyGameExt.GetGrid(playerCorpse.transform.position);
EventLogger.LogPlayerLootCorpse(player, fullTeamList, grid, inventoryItems);
foreach (var assistant in assistants) {
EventLogger.LogPlayerLootCorpseAssist(assistant, fullTeamList, grid, inventoryItems, player.UserIDString);
}
}
catch {
}
}
private void OnPlayerWound(BasePlayer player, HitInfo info) {
if (player == null || info == null || info.InitiatorPlayer == null || info.InitiatorPlayer.IsNpc) {
return;
}
EventLogger.LogPlayerWounded(info.InitiatorPlayer, player);
}
private void OnPlayerAssist(BasePlayer target, BasePlayer player) {
PlayerStats.Increment(player, "upwounded:player");
}
private void OnCargoPlaneSignaled(CargoPlane cargoPlane, SupplySignal supplySignal) {
var playerOwner = supplySignal.GetItem()?.GetRootContainer()?.playerOwner;
if (playerOwner != null) {
PlayerStats.Increment(playerOwner, "use:supply");
}
}
private void OnHealingItemUse(MedicalTool tool, BasePlayer player) {
PlayerStats.Increment(player, $"use:{tool.GetItem()?.info?.shortname}");
}
private void OnExplosiveThrown(BasePlayer player, BaseEntity entity, ThrownWeapon item) {
PlayerStats.Increment(player, $"use:{item.GetItem()?.info?.shortname}");
}
private void OnPlayerCommand(BasePlayer player, string command, string[] args) {
switch (command.ToLower()) {
case "pm" : {
if (args.Length > 1) {
string playerSearch = args[0];
BasePlayer receiver = BasePlayer.Find(playerSearch);
if (receiver != null) {
EventLogger.LogChatMessage(player, "pm", string.Join(" ", args.Skip(1).ToArray()), receiver);
}
}
break;
}
case "r" : {
EventLogger.LogChatMessage(player, "pm", string.Join(" ", args));
break;
}
}
EventLogger.LogPlayerCommand(player, command, args);
}
private void OnTirifyUseBan(string steamId, string reason, BasePlayer moderator = null) {
EventLogger.LogBan(steamId, reason, moderator);
}
private void OnTirifyUseUnBan(string steamId, BasePlayer moderator = null) {
EventLogger.LogUnban(steamId, moderator);
}
#endregion
#region [ API ]
public class API {
public static class URL {
public const string WebSocket = "server-stats";
public const string Auth = "server-player-auth";
public const string Ban = "server-player-ban";
public const string Events = "server-player-event";
public const string PlayerStats = "player-stats";
public const string GameServer = "game-server-gateway";
public const string GameServerPlayers = "game-server-p";
public const string Gateway = "/gateway";
internal readonly static Dictionary<string, List<string>> _urls = new Dictionary<string, List<string>>() {
{
WebSocket, new List<string>() {
"wss://s01-server-stats-gateway.tirify.com/api/v1/server/websocket",
}
}
, {
Auth, new List<string>() {
"https://s01-server-player-auth-gateway.tirify.com/api",
}
}
, {
Ban, new List<string>() {
"https://s01-server-player-ban-gateway.tirify.com/api/v1/server/ban",
}
}
, {
Events, new List<string>() {
"https://s01-server-player-event-gateway.tirify.com/api/v1/player/events",
}
}
, {
PlayerStats, new List<string>() {
"https://s01-player-stats-gateway.tirify.com/api/v1/player/event",
}
}
, {
GameServer, new List<string>() {
"https://s01-game-server-gateway.tirify.com",
}
}
, {
GameServerPlayers, new List<string>() {
"https://s01-game-server-p-gateway.tirify.com",
}
}
, {
Gateway, new List<string>() {
"https://gateway.tirify.com",
}
}
}
;
public static int CurrentServer {
get;
internal set;
}
public static string Get(string name, int index = 0) {
// out of range
if (index >= _urls.Count) {
return null;
}
var url = _urls[name].ElementAtOrDefault(index);
if (url == null) {
return Get(name, index - 1);
}
return url;
}
public static string GetCurrent(string name) {
var url = Get(name, CurrentServer);
#if STAGE
if (!url.Contains("-in-stage.tirify.com")) {
url = url.Replace(".tirify.com", "-in-stage.tirify.com");
}
// для стейджа нет прокси
if (url.Contains("proxy01")) {
url = url.Replace("proxy01", "s01");
}
#endif
return url;
}
}
public static class Player {
private static string PlayerJoinUrl => URL.GetCurrent(URL.GameServerPlayers) + "/api/v3/player/join";
private static string PlayerJoinManyUrl => URL.GetCurrent(URL.GameServerPlayers) + "/api/v3/player/joinmany";
private static string PlayerLeaveUrl => URL.GetCurrent(URL.GameServerPlayers) + "/api/v3/player/leave";
public static void OnConnected(string playerToken) {
try {
var content = JsonConvert.SerializeObject(new {
playerToken
}
);
SendPostRequest(GetRandomServerUrl(PlayerJoinUrl), content, string.Empty);
}
catch {
}
}
public static void OnConnected(List<string> playersTokens) {
try {
var content = JsonConvert.SerializeObject(new {
playersTokens
}
);
SendPostRequest(GetRandomServerUrl(PlayerJoinManyUrl), content, string.Empty);
}
catch {
}
}
public static bool OnDisconnect(ulong steamId) {
try {
var content = JsonConvert.SerializeObject(new {
steamId
}
);
SendPostRequest(GetRandomServerUrl(PlayerLeaveUrl), content, steamId.ToString());
return true;
}
catch {
Singleton.RegisterServer();
}
return false;
}
public static void SendStats(string serialized) {
try {
#if STAGE
Singleton.Puts(serialized);
#endif
SendPostRequest(GetRandomServerUrl(URL.GetCurrent(URL.PlayerStats)), serialized, "");
}
catch (Exception) {
}
}
public static void OnlineHeartbeat(int piratesCount, Action<bool> callbackNeedRestart) {
var url = GetRandomServerUrl(URL.GetCurrent(URL.GameServerPlayers)) + $"/api/v1/server/{_config.ServerApiKey}/heartbeat?online={piratesCount}";
Singleton.webrequest.Enqueue(url, string.Empty, (code, response) => {
try {
if (code >= 300 || string.IsNullOrEmpty(response)) {
return;
}
#if STAGE
Singleton.Puts($"Heartbeat: {response}");
#endif
var responseObject = JsonConvert.DeserializeObject<Dictionary<string, object>>(response);
if (responseObject.ContainsKey("success")) {
bool success = (bool)responseObject["success"];
callbackNeedRestart(!success);
}
}
catch {
}
}
, Singleton);
}
}
public static class Server {
private static string AuthStartupUrl => string.Concat(URL.GetCurrent(URL.GameServer), "/api/v1/server/init");
private static string PairUrl => string.Concat(URL.GetCurrent(URL.Gateway), "/api/v1/project/server/pair");
public static void Init(Action<int, string> callback = null) {
try {
var content = GetServerInfo();
#if STAGE
Singleton.Puts(content);
#endif
SendPostRequest(GetRandomServerUrl(AuthStartupUrl), content, _config.ServerApiKey, callback);
}
catch {
Singleton.RegisterServer();
}
}
public static void Pair(string inviteKey, Action<int, string> callback) {
try {
var content = JsonConvert.SerializeObject(new {
inviteKey, serverKey = _config.ServerApiKey
}
);
SendPostRequest(PairUrl, content, _config.ServerApiKey, callback);
}
catch (Exception ex) {
Singleton.PrintError($"Exception in server pairing: {ex}");
}
}
private static string GetServerInfo() {
return JsonConvert.SerializeObject(new {
ip = Singleton.GetServerIp(), gamePort = ConVar.Server.port, queryPort = ConVar.Server.queryport, serverName = ConVar.Server.hostname, maxPlayers = ConVar.Server.maxplayers, mapName = ConVar.Server.level, mapSize = ConVar.Server.worldsize.ToString(), gameType = "rust", version = Rust.Protocol.network.ToString(), serverToken = _config.ServerToken, wid = Singleton.GetServerWipeId(), rid = Singleton.GetRestartId(), has_tirify_license_plugin = Singleton.plugins.Exists("TGPP"), has_tiriry_partner_plugin = Singleton.plugins.Exists("TirifyLauncherPartner"), has_fgs_plugin = Singleton.plugins.Exists("FGS"), discordLink = _config.UrlVk, donateShopLink = _config.UrlShop, vkGroupLink = _config.UrlDiscord,
#if RUST_OLD_STEAMWORKS
serverTags = Facepunch.Steamworks.Server.Instance.GameTags,
#else
serverTags = Steamworks.SteamServer.GameTags = "mp300,cp200,ptrak,qp0,$reu1,v2590,^w,^v,^e,EU,born1750105939,gmrust,cs123128,^o^z",
#endif
additionalAttributes = "", ownerEmail = _config.OwnerEmail
}
);
}
}
public static class Events {
public static void Send(List<EventLogger.EventLog> eventLogs, Action<int, string> callback = null) {
try {
var content = JsonConvert.SerializeObject(eventLogs, EventLogger.EventLog.LogConverter);
SendPostRequest(GetRandomServerUrl(URL.GetCurrent(URL.Events)), content, content, callback);
}
catch {
}
}
}
public static class Discord {
public static void NotifyBannedConnection(Connection connection, bool bannedHwid, string mainSteamId = null) {
var message = new {
embeds = new object[] {
new {
title = "**Попытка зайти с баном**", description = $"* Сервер: **{ConVar.Server.hostname}**" + $"\n* SteamID: **{connection.userid}**" + $"\n* IP: **{TirifyGameExt.IPAddressWithoutPort(connection)}**" + $"\n* Забанен по железу: **{(bannedHwid ? "Да" : "Нет")}**" + (mainSteamId != null ? $"\n* Основной аккаунт: **{mainSteamId}**" : string.Empty), color = 13491712
}
}
}
;
Singleton.webrequest.Enqueue(_config.BanSystem.DiscordWebhook, JsonConvert.SerializeObject(message), (code, response) => {
if (code >= 300 || response == null) {
Singleton.PrintWarning($"Не удалось отправить уведомление о забаненном ({code}): {response}");
}
}
, Singleton, Core.Libraries.RequestMethod.POST, new Dictionary<string, string>() {
{
"Content-Type", "application/json"
}
}
);
}
}
private static void SendPostRequest(string url, string content, string signSalt, Action<int, string> callback = null, int mirrorIndex = -1) {
if (mirrorIndex == -1) {
mirrorIndex = URL.CurrentServer;
}
var requestId = Guid.NewGuid().ToString();
var sign = CreateMd5Hash(_config.ServerApiKey + requestId + signSalt);
Singleton.NextTick(() => {
Singleton.webrequest.Enqueue(url, content, (code, response) => ValidateResponse(url, content, signSalt, code, response, callback, mirrorIndex), Singleton, Core.Libraries.RequestMethod.POST, new Dictionary<string, string>() {
{
"x-app-version", Singleton.Version.ToString()
}
, {
"x-app-requestid", requestId
}
, {
"x-app-name", Singleton.Name
}
, {
"x-app-sign", sign
}
, {
"x-app-serverkey", _config.ServerApiKey
}
, {
"x-app-token", _config.ServerToken
}
, {
"Content-Type", "application/json"
}
,
}
, 6);
}
);
}
private static void ValidateResponse(string url, string content, string signSalt, int code, string response, Action<int, string> callback = null, int mirrorIndex = 0) {
#if STAGE
Singleton.Puts($"{url} - {response} ({code})");
#endif
if (code == 0 || code > 500) {
string serviceName = GetServiceName(url);
if (string.IsNullOrEmpty(serviceName)) {
return;
}
string mirrorUrl = URL.Get(serviceName, ++mirrorIndex);
// не осталось больше зеркал :(
if (string.IsNullOrEmpty(mirrorUrl)) {
return;
}
var lastUrl = URL.Get(serviceName, mirrorIndex - 1);
string queryString = url.Replace(lastUrl, "").Replace(lastUrl.Replace(".tirify.com", "-in-stage.tirify.com"), "");
string finalMirrorUrl = mirrorUrl + queryString;
//Singleton.Puts($"Failed to connect to our service, use mirror {mirrorIndex}");
SendPostRequest(finalMirrorUrl, content, signSalt, callback, mirrorIndex);
return;
}
if (code >= 200 && code < 300) {
URL.CurrentServer = mirrorIndex;
}
if (code >= 300) {
switch (code) {
case 401 : Singleton.RegisterServer();
break;
default : {
string serviceName = GetServiceName(url);
Singleton.RaiseError($"Request on service {serviceName} had a response with error code: {code}: {response}");
break;
}
}
}
if (callback != null) {
try {
callback(code, response);
}
catch (Exception ex) {
Singleton.RaiseError($"Error occured in webrequest callback: {ex.Message}");
}
}
}
private static string GetServiceName(string serviceUrl) {
foreach (var url in URL._urls) {
if (serviceUrl.Contains(url.Key)) {
return url.Key;
}
}
return string.Empty;
}
}
#endregion
#region [ Config ]
public class Configuration {
[JsonIgnore]
public string ServerToken = string.Empty;
[JsonProperty("Лицензионный ключ сервера")]
public string ServerApiKey = GenerateRandomMD5();
[JsonProperty("Электронная почта владельца")]
public string OwnerEmail = string.Empty;
[JsonProperty("Интервал отправки событий на сервер")]
public int EventSendInterval = 10;
[JsonProperty("Ссылка на группу VK")]
public string UrlVk = string.Empty;
[JsonProperty("Ссылка на магазин")]
public string UrlShop = string.Empty;
[JsonProperty("Ссылка на Discord")]
public string UrlDiscord = string.Empty;
[JsonProperty("Конфигурация бан системы")]
public BanSystemConfiguration BanSystem = new BanSystemConfiguration();
}
public class BanSystemConfiguration {
[JsonProperty("Показывать игроку причину бана")]
public bool ShowBanReason {
get;
set;
}
= false;
[JsonProperty("Автоматически банить по айпи игрока")]
public bool AutoBanIp {
get;
set;
}
= true;
[JsonProperty("Вебхук для уведомлений")]
public string DiscordWebhook {
get;
set;
}
= string.Empty;
[JsonProperty("Уведомлять в дискорд, если игрок с баном пытается зайти")]
public bool NotifyBannedConnection {
get;
set;
}
= false;
}
internal static Configuration _config = new Configuration();
public class Database {
[JsonProperty("Список забаненных игроков")]
public Dictionary<ulong, TirifyBanSystem.BannedUser> BannedUsers {
get;
set;
}
= new Dictionary<ulong, TirifyBanSystem.BannedUser>();
}
internal static Database _data = new Database();
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<Configuration>();
}
catch {
LoadDefaultConfig();
}
SaveConfig();
try {
_data = Interface.Oxide.DataFileSystem.ReadObject<Database>(this.Name);
}
catch {
_data = new Database();
}
SaveData();
}
protected override void LoadDefaultConfig() {
_config = new Configuration();
}
protected override void SaveConfig() {
Config.WriteObject(_config, true);
}
private void SaveData() {
Interface.Oxide.DataFileSystem.WriteObject(this.Name, _data, true);
}
#endregion
#region [ Plugin Hooks ]
[HookMethod("IsPlayerNoSteam")]
private bool IsPlayerTirifyLicense(string steamId) => (bool)(TGPP?.Call("IsPlayerNoSteam", steamId) ?? false);
[HookMethod("GetNoSteamCount")]
private int GetNoSteamCount() => (int)(TGPP?.Call("GetNoSteamCount") ?? 0);
[HookMethod("IsSteam")]
private bool IsSteam(Network.Connection connection) => (bool)(TGPP?.Call("IsSteam", connection) ?? false);
[HookMethod("OnPrivateMessage")]
private void OnPrivateMessage(string senderId, string targetId, string message) {
BasePlayer sender = Player.FindById(senderId), target = Player.FindById(targetId);
if (sender == null || target == null) {
return;
}
EventLogger.LogChatMessage(sender, "pm", message, target);
}
[HookMethod("SetTirifyBan")]
private void SetTirifyBan(string steamIdString, string reason = "") {
ulong steamId = 0;
if (ulong.TryParse(steamIdString, out steamId)) {
TirifyBanSystem.BanPlayer(steamId, reason);
}
}
[HookMethod("SetTirifyUnBan")]
private void SetTirifyUnBan(string steamIdString, string reason = "") {
ulong steamId = 0;
if (ulong.TryParse(steamIdString, out steamId)) {
TirifyBanSystem.UnbanPlayer(steamId);
}
}
private string GetPlayerSessionID(BasePlayer player) => TirifyGameExt.GetSessionUID(player.Connection);
private string GetServerRestartId() => this.GetRestartId().ToString();
private string GetServerWipeId() => this.GetWipeId();
private Dictionary<string, string> GetPlayerHwid(string steamId) => (Dictionary<string, string>)(TGPP?.Call("GetPlayerHwid", steamId) ?? new Dictionary<string, string>());
#endregion
#region [ Functions ]
private void RegisterServer() {
try {
// update steam tags before sending it
if (TirifyGameExt.UpdateServerInformationMethod != null) {
TirifyGameExt.UpdateServerInformationMethod.Invoke(ServerMgr.Instance, new object[0]);
}
// init
API.Server.Init((Action<int, string>)((code, response) => {
var responseObject = JsonConvert.DeserializeObject<Dictionary<string, object>>((string)response);
if (responseObject == null) {
return;
}
if ((bool)responseObject["success"] == false) {
throw new Exception($"/server/init returned false");
}
_config.ServerApiKey = responseObject["serverKey"].ToString();
_config.ServerToken = responseObject["serverToken"].ToString();
SaveConfig();
SendTirifyLicenseP
// ... [Code preview truncated due to length. Full source in attached file] ...