mirror of
https://github.com/tribufu/ServerManagers
synced 2026-05-06 15:17:34 +00:00
source code checkin
This commit is contained in:
parent
5f8fb2c825
commit
7e57b72e35
675 changed files with 168433 additions and 0 deletions
25
src/ArkData/ArkData.csproj
Normal file
25
src/ArkData/ArkData.csproj
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Globals">
|
||||
<SccProjectName>%24/Development/ServerManagers/Main/ArkData</SccProjectName>
|
||||
<SccProvider>{4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}</SccProvider>
|
||||
<SccAuxPath>https://dev.azure.com/bretthewitson</SccAuxPath>
|
||||
<SccLocalPath>.</SccLocalPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net462</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Data.Entity" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
112
src/ArkData/DataContainerAsync.cs
Normal file
112
src/ArkData/DataContainerAsync.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
/// <summary>
|
||||
/// The container for the data.
|
||||
/// </summary>
|
||||
public partial class DataContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates the DataContainer and parses all the user-data files
|
||||
/// </summary>
|
||||
/// <returns>The async task context containing the resulting container.</returns>
|
||||
public static async Task<DataContainer> CreateAsync(string playerFileFolder, string tribeFileFolder)
|
||||
{
|
||||
var playerFiles = new string[0];
|
||||
var tribeFiles = new string[0];
|
||||
|
||||
if (Directory.Exists(playerFileFolder))
|
||||
{
|
||||
playerFiles = Directory.GetFiles(playerFileFolder).Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(DataFileDetails.PlayerFilePrefix)
|
||||
&& Path.GetFileNameWithoutExtension(f).EndsWith(DataFileDetails.PlayerFileSuffix)
|
||||
&& Path.GetExtension(f).Equals(DataFileDetails.PlayerFileExtension)).ToArray();
|
||||
}
|
||||
if (Directory.Exists(tribeFileFolder))
|
||||
{
|
||||
tribeFiles = Directory.GetFiles(tribeFileFolder).Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(DataFileDetails.TribeFilePrefix)
|
||||
&& Path.GetFileNameWithoutExtension(f).EndsWith(DataFileDetails.TribeFileSuffix)
|
||||
&& Path.GetExtension(f).Equals(DataFileDetails.TribeFileExtension)).ToArray();
|
||||
}
|
||||
|
||||
var container = new DataContainer();
|
||||
|
||||
foreach (var file in playerFiles)
|
||||
container.Players.Add(await Parser.ParsePlayerAsync(file));
|
||||
|
||||
foreach (var file in tribeFiles)
|
||||
container.Tribes.Add(await Parser.ParseTribeAsync(file));
|
||||
|
||||
container.LinkPlayerTribe();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the profile data for all users from the steam service
|
||||
/// </summary>
|
||||
/// <returns>The async task context.</returns>
|
||||
public async Task<DateTime> LoadSteamAsync(string apiKey, int steamUpdateInterval = 0)
|
||||
{
|
||||
const int MAX_STEAM_IDS = 100;
|
||||
|
||||
// need to make multiple calls of 100 steam id's.
|
||||
var lastSteamUpdateUtc = DateTime.UtcNow;
|
||||
var startIndex = 0;
|
||||
var playerSteamIds = Players.Where(p => p.LastPlatformUpdateUtc.AddMinutes(steamUpdateInterval) < DateTime.UtcNow).Select(p => p.PlayerId).ToArray();
|
||||
|
||||
while (true)
|
||||
{
|
||||
// check if the start index has exceeded the Players list count.
|
||||
if (startIndex >= playerSteamIds.Length) break;
|
||||
// get the number of steam ids to read.
|
||||
int steamIdsCount = System.Math.Min(MAX_STEAM_IDS, playerSteamIds.Length - startIndex);
|
||||
// get a comma delimited list of the steam ids to process
|
||||
var builder = string.Join(",", playerSteamIds, startIndex, steamIdsCount);
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.BaseAddress = new System.Uri("https://api.steampowered.com/");
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
var response = await client.GetAsync(string.Format("ISteamUser/GetPlayerSummaries/v0002/?key={0}&steamids={1}", apiKey, builder));
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (var reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
|
||||
{
|
||||
LinkSteamProfiles(await reader.ReadToEndAsync(), lastSteamUpdateUtc);
|
||||
}
|
||||
else
|
||||
throw new System.Net.WebException("The Steam API request was unsuccessful. Are you using a valid key?");
|
||||
}
|
||||
|
||||
startIndex += steamIdsCount;
|
||||
}
|
||||
|
||||
SteamLoaded = true;
|
||||
return lastSteamUpdateUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the player server status. Can only be done after fetching Steam player data.
|
||||
/// </summary>
|
||||
/// <param name="ipString">The IP of the server.</param>
|
||||
/// <param name="port">The port of the server.</param>
|
||||
/// <returns>The async task context.</returns>
|
||||
public Task LoadOnlinePlayersAsync(string ipString, int port)
|
||||
{
|
||||
if (SteamLoaded)
|
||||
return Task.Run(() =>
|
||||
{
|
||||
LinkOnlinePlayers(ipString, port);
|
||||
});
|
||||
else
|
||||
throw new System.Exception("The Steam user data should be loaded before the server status can be checked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/ArkData/DataContainerBase.cs
Normal file
101
src/ArkData/DataContainerBase.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using SSQLib;
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Runtime.Remoting;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// The container for the data.
|
||||
/// </summary>
|
||||
namespace ArkData
|
||||
{
|
||||
public partial class DataContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of all players registered on the server.
|
||||
/// </summary>
|
||||
public List<PlayerData> Players { get; set; }
|
||||
/// <summary>
|
||||
/// A list of all tribes registered on the server.
|
||||
/// </summary>
|
||||
public List<TribeData> Tribes { get; set; }
|
||||
/// <summary>
|
||||
/// Indicates whether the steam user data has been loaded.
|
||||
/// </summary>
|
||||
private bool SteamLoaded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the DataContainer.
|
||||
/// </summary>
|
||||
public DataContainer()
|
||||
{
|
||||
Players = new List<PlayerData>();
|
||||
Tribes = new List<TribeData>();
|
||||
SteamLoaded = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Links the online players, to the player profiles.
|
||||
/// </summary>
|
||||
/// <param name="ipString">The server ip address.</param>
|
||||
/// <param name="port">The Steam query port.</param>
|
||||
private void LinkOnlinePlayers(string ipString, int port)
|
||||
{
|
||||
try
|
||||
{
|
||||
var online = Enumerable.OfType<PlayerInfo>(new SSQL().Players(new IPEndPoint(IPAddress.Parse(ipString), port))).ToList();
|
||||
|
||||
for (var i = 0; i < Players.Count; i++)
|
||||
{
|
||||
var online_player = online.SingleOrDefault(p => p.Name == Players[i].PlayerName);
|
||||
if (online_player != null)
|
||||
Players[i].Online = true;
|
||||
else
|
||||
Players[i].Online = false;
|
||||
}
|
||||
}
|
||||
catch (SSQLServerException)
|
||||
{
|
||||
throw new ServerException("The connection to the server failed. Please check the configured IP address and port.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Links the players to their tribes and the tribes to the players.
|
||||
/// </summary>
|
||||
private void LinkPlayerTribe()
|
||||
{
|
||||
for (var i = 0; i < Players.Count; i++)
|
||||
{
|
||||
var player = Players[i];
|
||||
player.OwnedTribes = Tribes.Where(t => t.OwnerId == player.CharacterId).ToList();
|
||||
player.Tribe = Tribes.SingleOrDefault(t => t.Id == player.TribeId);
|
||||
}
|
||||
|
||||
for (var i = 0; i < Tribes.Count; i++)
|
||||
{
|
||||
var tribe = Tribes[i];
|
||||
tribe.Owner = Players.SingleOrDefault(p => p.CharacterId == tribe.OwnerId);
|
||||
tribe.Players = Players.Where(p => p.TribeId == tribe.Id).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes JSON from Steam API and links Steam profile to player profile.
|
||||
/// </summary>
|
||||
/// <param name="jsonString">The JSON data string.</param>
|
||||
private void LinkSteamProfiles(string jsonString, DateTime lastSteamUpdateUtc)
|
||||
{
|
||||
var profiles = JsonConvert.DeserializeObject<Models.SteamResponse<Models.SteamProfile>>(jsonString).response.players;
|
||||
|
||||
for (var i = 0; i < profiles.Count; i++)
|
||||
{
|
||||
var player = Players.Single(p => p.PlayerId == profiles[i].steamid);
|
||||
player.PlayerName = profiles[i].personaname;
|
||||
player.LastPlatformUpdateUtc = lastSteamUpdateUtc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src/ArkData/DataContainerSync.cs
Normal file
108
src/ArkData/DataContainerSync.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
/// <summary>
|
||||
/// The container for the data.
|
||||
/// </summary>
|
||||
public partial class DataContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates the DataContainer and parses all the user data files
|
||||
/// </summary>
|
||||
public static DataContainer Create(string playerFileFolder, string tribeFileFolder)
|
||||
{
|
||||
var playerFiles = new string[0];
|
||||
var tribeFiles = new string[0];
|
||||
|
||||
if (Directory.Exists(playerFileFolder))
|
||||
{
|
||||
playerFiles = Directory.GetFiles(playerFileFolder).Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(DataFileDetails.PlayerFilePrefix)
|
||||
&& Path.GetFileNameWithoutExtension(f).EndsWith(DataFileDetails.PlayerFileSuffix)
|
||||
&& Path.GetExtension(f).Equals(DataFileDetails.PlayerFileExtension)).ToArray();
|
||||
}
|
||||
if (Directory.Exists(tribeFileFolder))
|
||||
{
|
||||
tribeFiles = Directory.GetFiles(tribeFileFolder).Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(DataFileDetails.TribeFilePrefix)
|
||||
&& Path.GetFileNameWithoutExtension(f).EndsWith(DataFileDetails.TribeFileSuffix)
|
||||
&& Path.GetExtension(f).Equals(DataFileDetails.TribeFileExtension)).ToArray();
|
||||
}
|
||||
|
||||
var container = new DataContainer();
|
||||
|
||||
foreach (var file in playerFiles)
|
||||
container.Players.Add(Parser.ParsePlayer(file));
|
||||
|
||||
foreach (var file in tribeFiles)
|
||||
container.Tribes.Add(Parser.ParseTribe(file));
|
||||
|
||||
container.LinkPlayerTribe();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the profile data for all users from the steam service
|
||||
/// </summary>
|
||||
/// <param name="apiKey">The Steam API key</param>
|
||||
public DateTime LoadSteam(string apiKey, int steamUpdateInterval = 0)
|
||||
{
|
||||
const int MAX_STEAM_IDS = 100;
|
||||
|
||||
// need to make multiple calls of 100 steam id's.
|
||||
var lastSteamUpdateUtc = DateTime.UtcNow;
|
||||
var startIndex = 0;
|
||||
var playerSteamIds = Players.Where(p => p.LastPlatformUpdateUtc.AddMinutes(steamUpdateInterval) < DateTime.UtcNow).Select(p => p.PlayerId).ToArray();
|
||||
|
||||
while (true)
|
||||
{
|
||||
// check if the start index has exceeded the Players list count.
|
||||
if (startIndex >= playerSteamIds.Length) break;
|
||||
// get the number of steam ids to read.
|
||||
int steamIdsCount = System.Math.Min(MAX_STEAM_IDS, playerSteamIds.Length - startIndex);
|
||||
// get a comma delimited list of the steam ids to process
|
||||
var builder = string.Join(",", playerSteamIds, startIndex, steamIdsCount);
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
client.BaseAddress = new System.Uri("https://api.steampowered.com/");
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
var response = client.GetAsync(string.Format("ISteamUser/GetPlayerSummaries/v0002/?key={0}&steamids={1}", apiKey, builder)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (var reader = new StreamReader(response.Content.ReadAsStreamAsync().Result))
|
||||
{
|
||||
LinkSteamProfiles(reader.ReadToEnd(), lastSteamUpdateUtc);
|
||||
}
|
||||
else
|
||||
throw new System.Net.WebException("The Steam API request was unsuccessful. Are you using a valid key?");
|
||||
}
|
||||
|
||||
startIndex += steamIdsCount;
|
||||
}
|
||||
|
||||
SteamLoaded = true;
|
||||
return lastSteamUpdateUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the player server status. Can only be done after fetching Steam player data.
|
||||
/// </summary>
|
||||
/// <param name="ipString">The IP of the server.</param>
|
||||
/// <param name="port">The port of the server.</param>
|
||||
public void LoadOnlinePlayers(string ipString, int port)
|
||||
{
|
||||
if (SteamLoaded)
|
||||
{
|
||||
LinkOnlinePlayers(ipString, port);
|
||||
}
|
||||
else
|
||||
throw new System.Exception("The Steam user data should be loaded before the server status can be checked.");
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/ArkData/DataFileDetails.cs
Normal file
13
src/ArkData/DataFileDetails.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
namespace ArkData
|
||||
{
|
||||
public static class DataFileDetails
|
||||
{
|
||||
public static string PlayerFilePrefix { get; set; } = string.Empty;
|
||||
public static string PlayerFileSuffix { get; set; } = string.Empty;
|
||||
public static string PlayerFileExtension { get; set; } = ".arkprofile";
|
||||
|
||||
public static string TribeFilePrefix { get; set; } = string.Empty;
|
||||
public static string TribeFileSuffix { get; set; } = string.Empty;
|
||||
public static string TribeFileExtension { get; set; } = ".arktribe";
|
||||
}
|
||||
}
|
||||
58
src/ArkData/Extensions.cs
Normal file
58
src/ArkData/Extensions.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
private static readonly int[] Empty = new int[0];
|
||||
|
||||
public static int LocateFirst(this byte[] self, byte[] candidate, int offset = 0)
|
||||
{
|
||||
if (IsEmptyLocate(self, candidate, offset))
|
||||
return -1;
|
||||
|
||||
for (int position = offset; position < self.Length; position++)
|
||||
if (IsMatch(self, position, candidate))
|
||||
return position;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int[] Locate(this byte[] self, byte[] candidate)
|
||||
{
|
||||
if (IsEmptyLocate(self, candidate, 0))
|
||||
return Empty;
|
||||
|
||||
List<int> list = new List<int>();
|
||||
for (int position = 0; position < self.Length; ++position)
|
||||
if (IsMatch(self, position, candidate))
|
||||
list.Add(position);
|
||||
|
||||
if (list.Count != 0)
|
||||
return list.ToArray();
|
||||
|
||||
return Empty;
|
||||
}
|
||||
|
||||
private static bool IsMatch(byte[] array, int position, byte[] candidate)
|
||||
{
|
||||
if (candidate.Length > array.Length - position)
|
||||
return false;
|
||||
|
||||
for (int index = 0; index < candidate.Length; ++index)
|
||||
if ((array[position + index] != candidate[index]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsEmptyLocate(byte[] array, byte[] candidate, int offset)
|
||||
{
|
||||
if (array != null && candidate != null &&
|
||||
(array.Length != 0 && candidate.Length != 0) &&
|
||||
(candidate.Length <= array.Length && offset != -1))
|
||||
return offset > array.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/ArkData/Helpers.cs
Normal file
59
src/ArkData/Helpers.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
internal class Helpers
|
||||
{
|
||||
public static int GetInt(byte[] data, string name)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes(name);
|
||||
byte[] bytes2 = Encoding.Default.GetBytes("IntProperty");
|
||||
|
||||
int offset = data.LocateFirst(bytes1, 0);
|
||||
int num = data.LocateFirst(bytes2, offset);
|
||||
|
||||
if (num > -1)
|
||||
return BitConverter.ToInt32(data, num + bytes2.Length + 9);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static ushort GetUInt16(byte[] data, string name)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes(name);
|
||||
byte[] bytes2 = Encoding.Default.GetBytes("UInt16Property");
|
||||
|
||||
int offset = data.LocateFirst(bytes1, 0);
|
||||
int num = data.LocateFirst(bytes2, offset);
|
||||
|
||||
if (num >= 0)
|
||||
return BitConverter.ToUInt16(data, num + bytes2.Length + 9);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static string GetString(byte[] data, string name)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes(name);
|
||||
byte[] bytes2 = Encoding.Default.GetBytes("StrProperty");
|
||||
int offset = data.LocateFirst(bytes1, 0);
|
||||
int num = data.LocateFirst(bytes2, offset);
|
||||
|
||||
if (num < 0)
|
||||
return string.Empty;
|
||||
|
||||
byte[] numArray = new byte[1];
|
||||
|
||||
Array.Copy(data, num + bytes2.Length + 1, numArray, 0, 1);
|
||||
|
||||
int length = (int)numArray[0] - (data[num + bytes2.Length + 12] == byte.MaxValue ? 6 : 5);
|
||||
|
||||
byte[] bytes3 = new byte[length];
|
||||
Array.Copy(data, num + bytes2.Length + 13, bytes3, 0, length);
|
||||
|
||||
if (data[num + bytes2.Length + 12] == byte.MaxValue)
|
||||
return Encoding.Unicode.GetString(bytes3);
|
||||
|
||||
return Encoding.Default.GetString(bytes3);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/ArkData/Models/SteamBan.cs
Normal file
18
src/ArkData/Models/SteamBan.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData.Models
|
||||
{
|
||||
internal class SteamBan
|
||||
{
|
||||
public string SteamId { get; set; }
|
||||
public bool CommunityBanned { get; set; }
|
||||
public bool VACBanned { get; set; }
|
||||
public int NumberOfVACBans { get; set; }
|
||||
public int DaysSinceLastBan { get; set; }
|
||||
public int NumberOfGameBans { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/ArkData/Models/SteamPlayerResponse.cs
Normal file
13
src/ArkData/Models/SteamPlayerResponse.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData.Models
|
||||
{
|
||||
internal class SteamPlayerResponse<T>
|
||||
{
|
||||
public List<T> players { get; set; }
|
||||
}
|
||||
}
|
||||
16
src/ArkData/Models/SteamProfile.cs
Normal file
16
src/ArkData/Models/SteamProfile.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData.Models
|
||||
{
|
||||
internal class SteamProfile
|
||||
{
|
||||
public string steamid { get; set; }
|
||||
public string personaname { get; set; }
|
||||
public string profileurl { get; set; }
|
||||
public string avatar { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/ArkData/Models/SteamResponse.cs
Normal file
13
src/ArkData/Models/SteamResponse.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData.Models
|
||||
{
|
||||
internal class SteamResponse<T>
|
||||
{
|
||||
public SteamPlayerResponse<T> response { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/ArkData/PlayerData.cs
Normal file
24
src/ArkData/PlayerData.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
public class PlayerData
|
||||
{
|
||||
public string PlayerId { get; set; }
|
||||
public string PlayerName { get; set; }
|
||||
public long CharacterId { get; set; }
|
||||
public string CharacterName { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string File { get; set; }
|
||||
public string Filename { get; set; }
|
||||
public DateTime FileCreated { get; set; }
|
||||
public DateTime FileUpdated { get; set; }
|
||||
public int? TribeId { get; set; }
|
||||
public short Level { get; set; }
|
||||
public virtual TribeData Tribe { get; set; }
|
||||
public virtual List<TribeData> OwnedTribes { get; set; }
|
||||
|
||||
public DateTime LastPlatformUpdateUtc { get; set; }
|
||||
}
|
||||
}
|
||||
66
src/ArkData/PlayerParser.cs
Normal file
66
src/ArkData/PlayerParser.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
internal partial class Parser
|
||||
{
|
||||
private static ulong GetId(byte[] data)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes("PlayerDataID");
|
||||
byte[] bytes2 = Encoding.Default.GetBytes("UInt64Property");
|
||||
int offset = Extensions.LocateFirst(data, bytes1, 0);
|
||||
int num = Extensions.LocateFirst(data, bytes2, offset);
|
||||
|
||||
return BitConverter.ToUInt64(data, num + bytes2.Length + 9);
|
||||
}
|
||||
|
||||
private static string GetPlatformId(byte[] data)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes("UniqueNetIdRepl");
|
||||
int num = Extensions.LocateFirst(data, bytes1, 0);
|
||||
|
||||
byte[] bytes2 = new byte[9];
|
||||
Array.Copy(data, num + bytes1.Length, bytes2, 0, 9);
|
||||
|
||||
var length = BitConverter.ToUInt32(bytes2, 5) - 1;
|
||||
byte[] bytes3 = new byte[length];
|
||||
|
||||
Array.Copy(data, num + bytes1.Length + bytes2.Length, bytes3, 0, length);
|
||||
return Encoding.Default.GetString(bytes3);
|
||||
}
|
||||
|
||||
public static PlayerData ParsePlayer(string fileName)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(fileName);
|
||||
if (!fileInfo.Exists)
|
||||
return null;
|
||||
byte[] data = File.ReadAllBytes(fileName);
|
||||
|
||||
var tribeId = Helpers.GetInt(data, "TribeId");
|
||||
|
||||
return new PlayerData()
|
||||
{
|
||||
//PlayerId = GetPlatformId(data),
|
||||
PlayerId = Path.GetFileNameWithoutExtension(fileInfo.Name),
|
||||
PlayerName = Helpers.GetString(data, "PlayerName"),
|
||||
CharacterId = Convert.ToInt64(GetId(data)),
|
||||
CharacterName = Helpers.GetString(data, "PlayerCharacterName"),
|
||||
TribeId = tribeId > -1 ? tribeId : Helpers.GetInt(data, "TribeID"),
|
||||
Level = (short)(1 + Convert.ToInt32(Helpers.GetUInt16(data, "CharacterStatusComponent_ExtraCharacterLevel"))),
|
||||
|
||||
File = fileName,
|
||||
Filename = fileInfo.Name,
|
||||
FileCreated = fileInfo.CreationTime,
|
||||
FileUpdated = fileInfo.LastWriteTime
|
||||
};
|
||||
}
|
||||
|
||||
public static Task<PlayerData> ParsePlayerAsync(string fileName)
|
||||
{
|
||||
return Task.Run(() => ParsePlayer(fileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/ArkData/Properties/AssemblyInfo.cs
Normal file
35
src/ArkData/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System.Reflection;
|
||||
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("ArkData SDK")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("AuthiQ")]
|
||||
[assembly: AssemblyProduct("ArkData SDK")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[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(true)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("d2ee1483-021f-4900-bbe8-88338d1386f4")]
|
||||
|
||||
// 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.3.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.3.0")]
|
||||
69
src/ArkData/SSQLib/Packet.cs
Normal file
69
src/ArkData/SSQLib/Packet.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
internal class Packet
|
||||
{
|
||||
internal int RequestId = 0;
|
||||
internal string Data = "";
|
||||
|
||||
internal Packet() { }
|
||||
|
||||
//Output the packet data as a byte array
|
||||
internal byte[] outputAsBytes()
|
||||
{
|
||||
byte[] data_byte = null;
|
||||
|
||||
if (Data.Length > 0)
|
||||
{
|
||||
//Create a new packet based on the length of the request
|
||||
data_byte = new byte[Data.Length + 5];
|
||||
|
||||
//Fill the first 4 bytes with 0xff
|
||||
data_byte[0] = 0xff;
|
||||
data_byte[1] = 0xff;
|
||||
data_byte[2] = 0xff;
|
||||
data_byte[3] = 0xff;
|
||||
|
||||
//Copy the data to the new request
|
||||
Array.Copy(ASCIIEncoding.UTF8.GetBytes(Data), 0, data_byte, 4, Data.Length);
|
||||
}
|
||||
//Empty request to get challenge
|
||||
else
|
||||
{
|
||||
data_byte = new byte[5];
|
||||
|
||||
//Fill the first 4 bytes with 0xff
|
||||
data_byte[0] = 0xff;
|
||||
data_byte[1] = 0xff;
|
||||
data_byte[2] = 0xff;
|
||||
data_byte[3] = 0xff;
|
||||
data_byte[4] = 0x57;
|
||||
}
|
||||
|
||||
|
||||
return data_byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
170
src/ArkData/SSQLib/PlayerInfo.cs
Normal file
170
src/ArkData/SSQLib/PlayerInfo.cs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores information about a player in the server
|
||||
/// </summary>
|
||||
public class PlayerInfo
|
||||
{
|
||||
private string name = "";
|
||||
private int index = -9999;
|
||||
private int kills = -9999;
|
||||
private int deaths = -9999;
|
||||
private int score = -9999;
|
||||
private int ping = -9999;
|
||||
private int rate = -9999;
|
||||
private float time = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PlayerInfo object with default values
|
||||
/// </summary>
|
||||
public PlayerInfo() { }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the player
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of kills the player has (default: -9999)
|
||||
/// </summary>
|
||||
public int Kills
|
||||
{
|
||||
get {
|
||||
return this.kills;
|
||||
}
|
||||
|
||||
set {
|
||||
this.kills = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The amount of deaths the player has (default: -9999)
|
||||
/// </summary>
|
||||
public int Deaths
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.deaths;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.deaths = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The score of the player (default: -9999)
|
||||
/// </summary>
|
||||
public int Score
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.score;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.score = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ping of the player (default: -9999)
|
||||
/// </summary>
|
||||
public int Ping
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ping;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.ping = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rate(?) of the player (default: -9999)
|
||||
/// </summary>
|
||||
public int Rate
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.rate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.rate = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The index of the player in the server
|
||||
/// </summary>
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.index;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.index = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The time the player has been in the server
|
||||
/// </summary>
|
||||
public float Time
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.time;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.time = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
307
src/ArkData/SSQLib/SSQL.cs
Normal file
307
src/ArkData/SSQLib/SSQL.cs
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Collections;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to retreive information from a Source server
|
||||
/// </summary>
|
||||
public class SSQL
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates an SSQL object with default values
|
||||
/// </summary>
|
||||
public SSQL()
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Pings the specified Source server to retreive information about it such as the server name, max players, current number of players, etc.
|
||||
/// </summary>
|
||||
/// <param name="ip_end">The IPEndPoint object containing the IP address and port of the server</param>
|
||||
/// <returns>Information about the server or throws an SSQLServerException if it could not be retreived</returns>
|
||||
public ServerInfo Server(IPEndPoint ip_end)
|
||||
{
|
||||
//Create a new empty server info object
|
||||
ServerInfo info = new ServerInfo();
|
||||
|
||||
//Create an empty buffer
|
||||
byte[] buf = null;
|
||||
|
||||
//Create a new packet and request
|
||||
Packet requestPacket = new Packet();
|
||||
requestPacket.Data = "TSource Engine Query";
|
||||
|
||||
try
|
||||
{
|
||||
//Attempt to get the server info
|
||||
buf = SocketUtils.getInfo(ip_end, requestPacket);
|
||||
}
|
||||
catch(SSQLServerException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
//Start past the first four bytes which are all 0xff
|
||||
int i = 4;
|
||||
|
||||
//Make sure the first character is an I
|
||||
if (buf[i++] != 'I') return null;
|
||||
|
||||
//Make sure the returned version is above 0x07
|
||||
if (buf[i++] < 0x07) return null;
|
||||
|
||||
StringBuilder srvName = new StringBuilder();
|
||||
|
||||
//Retrieve the server name
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
srvName.Append((char)buf[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
//Set the name of the server
|
||||
info.Name = srvName.ToString();
|
||||
|
||||
StringBuilder mapName = new StringBuilder();
|
||||
|
||||
//Retrieve the map name
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
mapName.Append((char)buf[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
info.Map = mapName.ToString();
|
||||
|
||||
StringBuilder gameName = new StringBuilder();
|
||||
|
||||
//Get the short name for the game
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
gameName.Append((char)buf[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
StringBuilder gameFriendly = new StringBuilder();
|
||||
|
||||
//Get the friendly game description
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
gameFriendly.Append((char)buf[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
info.Game = gameFriendly.ToString() + " (" + gameName.ToString() + ")";
|
||||
|
||||
short appID = (short)System.BitConverter.ToInt16(buf, i);
|
||||
|
||||
//Skip the next 2 bytes
|
||||
i += 2;
|
||||
|
||||
//Store the app id
|
||||
info.AppID = appID.ToString();
|
||||
|
||||
//Get the number of players
|
||||
info.PlayerCount = buf[i++].ToString();
|
||||
|
||||
//Get the number of max players
|
||||
info.MaxPlayers = buf[i++].ToString();
|
||||
|
||||
//Get the number of bots
|
||||
info.BotCount = buf[i++].ToString();
|
||||
|
||||
//Get the dedicated server type
|
||||
if ((char)buf[i] == 'l')
|
||||
info.Dedicated = ServerInfo.DedicatedType.LISTEN;
|
||||
else if ((char)buf[i] == 'd')
|
||||
info.Dedicated = ServerInfo.DedicatedType.DEDICATED;
|
||||
else if ((char)buf[i] == 'p')
|
||||
info.Dedicated = ServerInfo.DedicatedType.SOURCETV;
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
//Get the OS type
|
||||
if ((char)buf[i] == 'l')
|
||||
info.OS = ServerInfo.OSType.LINUX;
|
||||
else if ((char)buf[i] == 'w')
|
||||
info.OS = ServerInfo.OSType.WINDOWS;
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
//Check for password protection
|
||||
if (buf[i++] == 0x01) info.Password = true;
|
||||
|
||||
//Check for VAC
|
||||
if (buf[i++] == 0x01) info.VAC = true;
|
||||
|
||||
StringBuilder versionInfo = new StringBuilder();
|
||||
|
||||
//Get the game version
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
versionInfo.Append((char)buf[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
//Move to the next byte
|
||||
i++;
|
||||
|
||||
//Set the version
|
||||
info.Version = versionInfo.ToString();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retreives information about the players on a Source server
|
||||
/// </summary>
|
||||
/// <param name="ip_end">The IPEndPoint object storing the IP address and port of the server</param>
|
||||
/// <returns>An ArrayList of PlayerInfo or throws an SSQLServerException if the server could not be reached</returns>
|
||||
public ArrayList Players(IPEndPoint ip_end)
|
||||
{
|
||||
//Create a new array list to store the player array
|
||||
ArrayList players = new ArrayList();
|
||||
|
||||
//Create a new buffer to receive packets
|
||||
byte[] buf = null;
|
||||
|
||||
//Create a challenge packet
|
||||
byte[] challenge = new byte[9];
|
||||
challenge[0] = (byte)0xff;
|
||||
challenge[1] = (byte)0xff;
|
||||
challenge[2] = (byte)0xff;
|
||||
challenge[3] = (byte)0xff;
|
||||
challenge[4] = (byte)0x55;
|
||||
challenge[5] = (byte)0x00;
|
||||
challenge[6] = (byte)0x00;
|
||||
challenge[7] = (byte)0x00;
|
||||
challenge[8] = (byte)0x00;
|
||||
|
||||
try
|
||||
{
|
||||
//Attempt to get the challenge response
|
||||
buf = SocketUtils.getInfo(ip_end, challenge);
|
||||
}
|
||||
catch (SSQLServerException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
int i = 4;
|
||||
|
||||
//Make sure the response starts with A
|
||||
if (buf[i++] != 'A') return null;
|
||||
|
||||
//Create the new request with the challenge number
|
||||
byte[] requestPlayer = new byte[9];
|
||||
|
||||
requestPlayer[0] = (byte)0xff;
|
||||
requestPlayer[1] = (byte)0xff;
|
||||
requestPlayer[2] = (byte)0xff;
|
||||
requestPlayer[3] = (byte)0xff;
|
||||
requestPlayer[4] = (byte)0x55;
|
||||
requestPlayer[5] = buf[i++];
|
||||
requestPlayer[6] = buf[i++];
|
||||
requestPlayer[7] = buf[i++];
|
||||
requestPlayer[8] = buf[i++];
|
||||
|
||||
try
|
||||
{
|
||||
//Attempt to get the players response
|
||||
buf = SocketUtils.getInfo(ip_end, requestPlayer);
|
||||
}
|
||||
catch (SSQLServerException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Start past 0xffffffff
|
||||
i = 4;
|
||||
|
||||
//Make sure the response starts with D
|
||||
if (buf[i++] != 'D') return null;
|
||||
|
||||
//Get the amount of players
|
||||
byte numPlayers = buf[i++];
|
||||
|
||||
//Loop through each player and extract their stats
|
||||
for (int ii = 0; ii < numPlayers; ii++)
|
||||
{
|
||||
//Create a new player
|
||||
PlayerInfo newPlayer = new PlayerInfo();
|
||||
|
||||
//Set the index of the player (Does not work in L4D2, always returns 0)
|
||||
newPlayer.Index = buf[i++];
|
||||
|
||||
//Create a new player name
|
||||
List<byte> playerName = new List<byte>();
|
||||
|
||||
//Loop through and store the player's name
|
||||
while (buf[i] != 0x00)
|
||||
{
|
||||
playerName.Add(buf[i++]);
|
||||
}
|
||||
|
||||
//Move past the end of the string
|
||||
i++;
|
||||
|
||||
// Decode the player name
|
||||
newPlayer.Name = Encoding.UTF8.GetString(playerName.ToArray());
|
||||
|
||||
//Get the kills and store them in the player info
|
||||
newPlayer.Kills = (int)(buf[i] & 255) | ((buf[i + 1] & 255) << 8) | ((buf[i + 2] & 255) << 16) | ((buf[i + 3] & 255) << 24);
|
||||
|
||||
//Move to the next item
|
||||
i += 5;
|
||||
|
||||
//Get the time connected as a float and store it in the player info
|
||||
newPlayer.Time = (float)((int)(buf[i] & 255) | ((buf[i + 1] & 255) << 8));
|
||||
|
||||
//Move past the float
|
||||
i += 3;
|
||||
|
||||
//Add the player to the list
|
||||
players.Add(newPlayer);
|
||||
}
|
||||
|
||||
//Return the list of players
|
||||
return players;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/ArkData/SSQLib/SSQLServerException.cs
Normal file
32
src/ArkData/SSQLib/SSQLServerException.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
class SSQLServerException : System.Exception
|
||||
{
|
||||
public SSQLServerException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
317
src/ArkData/SSQLib/ServerInfo.cs
Normal file
317
src/ArkData/SSQLib/ServerInfo.cs
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores information about the Source server
|
||||
/// </summary>
|
||||
public class ServerInfo
|
||||
{
|
||||
private string name = "";
|
||||
private string ip = "";
|
||||
private string port = "";
|
||||
private string game = "";
|
||||
private string gameVersion = "";
|
||||
private string appID = "";
|
||||
private string map = "";
|
||||
private string playerCount = "";
|
||||
private string botCount = "";
|
||||
private string maxPlayers = "";
|
||||
|
||||
private bool passworded = false;
|
||||
private bool vac = false;
|
||||
private ServerInfo.DedicatedType dedicated = ServerInfo.DedicatedType.NONE;
|
||||
private ServerInfo.OSType os = ServerInfo.OSType.NONE;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new object with default values
|
||||
/// </summary>
|
||||
public ServerInfo() { }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the server
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The IP address of the server
|
||||
/// </summary>
|
||||
public string IP
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ip;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.ip = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The port the server uses
|
||||
/// </summary>
|
||||
public string Port
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.port;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.port = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game being played on the server (i.e. Team Fortress (tf))
|
||||
/// </summary>
|
||||
public string Game
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.game;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.game = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game version running on the server
|
||||
/// </summary>
|
||||
public string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.gameVersion;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.gameVersion = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The map currently being played on the server
|
||||
/// </summary>
|
||||
public string Map
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.map;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.map = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current player count on the server
|
||||
/// </summary>
|
||||
public string PlayerCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.playerCount;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.playerCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current bot count on the server
|
||||
/// </summary>
|
||||
public string BotCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.botCount;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.botCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The max amount of players allowed on the server
|
||||
/// </summary>
|
||||
public string MaxPlayers
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.maxPlayers;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.maxPlayers = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores whether the server is passworded or not
|
||||
/// </summary>
|
||||
public bool Password
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.passworded;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.passworded = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores whether the server is VAC protected or not
|
||||
/// </summary>
|
||||
public bool VAC
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.vac;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.vac = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the app ID of the game used by the server
|
||||
/// </summary>
|
||||
public string AppID
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.appID;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.appID = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the type of server running (Listen, Dedicated, SourceTV)
|
||||
/// </summary>
|
||||
public ServerInfo.DedicatedType Dedicated
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.dedicated;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.dedicated = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the operating system of the server (Windows, Linux)
|
||||
/// </summary>
|
||||
public ServerInfo.OSType OS
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.os;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.os = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe the type of server running
|
||||
/// </summary>
|
||||
public enum DedicatedType
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value
|
||||
/// </summary>
|
||||
NONE,
|
||||
/// <summary>
|
||||
/// Listen server (locally hosted)
|
||||
/// </summary>
|
||||
LISTEN,
|
||||
/// <summary>
|
||||
/// Dedicated server
|
||||
/// </summary>
|
||||
DEDICATED,
|
||||
/// <summary>
|
||||
/// SourceTV server
|
||||
/// </summary>
|
||||
SOURCETV
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe the operating system running on the server
|
||||
/// </summary>
|
||||
public enum OSType
|
||||
{
|
||||
/// <summary>
|
||||
/// Default value
|
||||
/// </summary>
|
||||
NONE,
|
||||
/// <summary>
|
||||
/// Windows server
|
||||
/// </summary>
|
||||
WINDOWS,
|
||||
/// <summary>
|
||||
/// Linux server
|
||||
/// </summary>
|
||||
LINUX
|
||||
};
|
||||
}
|
||||
}
|
||||
111
src/ArkData/SSQLib/SocketUtils.cs
Normal file
111
src/ArkData/SSQLib/SocketUtils.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* This file is part of SSQLib.
|
||||
*
|
||||
* SSQLib is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* SSQLib is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with SSQLib. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace SSQLib
|
||||
{
|
||||
internal class SocketUtils
|
||||
{
|
||||
private SocketUtils() { }
|
||||
|
||||
internal static byte[] getInfo(IPEndPoint ipe, Packet packet)
|
||||
{
|
||||
//Create the socket
|
||||
Socket srvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
|
||||
//Save the max packet size
|
||||
int packetSize = 12288;
|
||||
|
||||
//Send/Receive timeouts
|
||||
srvSocket.SendTimeout = 3000;
|
||||
srvSocket.ReceiveTimeout = 3000;
|
||||
|
||||
try
|
||||
{
|
||||
//Send the request to the server
|
||||
srvSocket.SendTo(packet.outputAsBytes(), ipe);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
throw new SSQLServerException("Could not send packet to server {" + se.Message + "}");
|
||||
}
|
||||
|
||||
//Create a new receive buffer
|
||||
byte[] rcvPacketInfo = new byte[packetSize];
|
||||
EndPoint Remote = (EndPoint)ipe;
|
||||
|
||||
try
|
||||
{
|
||||
//Receive the data from the server
|
||||
srvSocket.ReceiveFrom(rcvPacketInfo, ref Remote);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
throw new SSQLServerException("Could not receive packet from server {" + se.Message + "}");
|
||||
}
|
||||
|
||||
//Send the information back
|
||||
return rcvPacketInfo;
|
||||
}
|
||||
|
||||
internal static byte[] getInfo(IPEndPoint ipe, byte[] request)
|
||||
{
|
||||
//Create the socket
|
||||
Socket srvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
|
||||
//Save the max packet size
|
||||
int packetSize = 12288;
|
||||
|
||||
//Send/Receive timeouts
|
||||
srvSocket.SendTimeout = 3000;
|
||||
srvSocket.ReceiveTimeout = 3000;
|
||||
|
||||
try
|
||||
{
|
||||
//Send the request to the server
|
||||
srvSocket.SendTo(request, ipe);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
throw new SSQLServerException("Could not send packet to server {" + se.Message + "}");
|
||||
}
|
||||
|
||||
//Create a new receive buffer
|
||||
byte[] rcvPacketInfo = new byte[packetSize];
|
||||
EndPoint Remote = (EndPoint)ipe;
|
||||
|
||||
try
|
||||
{
|
||||
//Receive the data from the server
|
||||
srvSocket.ReceiveFrom(rcvPacketInfo, ref Remote);
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
throw new SSQLServerException("Could not receive packet from server {" + se.Message + "}");
|
||||
}
|
||||
|
||||
//Send the information back
|
||||
return rcvPacketInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/ArkData/TribeData.cs
Normal file
23
src/ArkData/TribeData.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
public class TribeData
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string File { get; set; }
|
||||
public string Filename { get; set; }
|
||||
public DateTime FileCreated { get; set; }
|
||||
public DateTime FileUpdated { get; set; }
|
||||
public int? OwnerId { get; set; }
|
||||
public virtual ICollection<PlayerData> Players { get; set; }
|
||||
public virtual PlayerData Owner { get; set; }
|
||||
|
||||
public TribeData()
|
||||
{
|
||||
this.Players = new HashSet<PlayerData>();
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/ArkData/TribeParser.cs
Normal file
50
src/ArkData/TribeParser.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArkData
|
||||
{
|
||||
internal partial class Parser
|
||||
{
|
||||
private static uint GetOwnerId(byte[] data)
|
||||
{
|
||||
byte[] bytes1 = Encoding.Default.GetBytes("OwnerPlayerDataID");
|
||||
byte[] bytes2 = Encoding.Default.GetBytes("UInt32Property");
|
||||
int offset = data.LocateFirst(bytes1, 0);
|
||||
int num = data.LocateFirst(bytes2, offset);
|
||||
return BitConverter.ToUInt32(data, num + bytes2.Length + 9);
|
||||
}
|
||||
|
||||
public static TribeData ParseTribe(string fileName)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(fileName);
|
||||
if (!fileInfo.Exists)
|
||||
return null;
|
||||
|
||||
byte[] data = File.ReadAllBytes(fileName);
|
||||
|
||||
var tribeId = Helpers.GetInt(data, "TribeId");
|
||||
|
||||
return new TribeData()
|
||||
{
|
||||
Id = tribeId > -1 ? tribeId : Helpers.GetInt(data, "TribeID"),
|
||||
Name = Helpers.GetString(data, "TribeName"),
|
||||
OwnerId = (int?)GetOwnerId(data),
|
||||
|
||||
File = fileName,
|
||||
Filename = fileInfo.Name,
|
||||
FileCreated = fileInfo.CreationTime,
|
||||
FileUpdated = fileInfo.LastWriteTime
|
||||
};
|
||||
}
|
||||
|
||||
public static Task<TribeData> ParseTribeAsync(string fileName)
|
||||
{
|
||||
return Task.Run<TribeData>(() =>
|
||||
{
|
||||
return ParseTribe(fileName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue