source
Created by: Andrew
This commit is contained in:
75
trunk/Common/Common.csproj
Normal file
75
trunk/Common/Common.csproj
Normal file
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F163DAAB-BDD2-4510-9A20-604D73D334D0}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>KartExtreme</RootNamespace>
|
||||
<AssemblyName>Common</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MySql.Data">
|
||||
<HintPath>..\..\MySql.Data.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Data\Database.cs" />
|
||||
<Compile Include="Data\Datum.cs" />
|
||||
<Compile Include="Data\Meta.cs" />
|
||||
<Compile Include="Data\RowNotUniqueException.cs" />
|
||||
<Compile Include="IO\Packet\InPacket.cs" />
|
||||
<Compile Include="IO\Log.cs" />
|
||||
<Compile Include="IO\Packet\OutPacket.cs" />
|
||||
<Compile Include="IO\Packet\PacketReadException.cs" />
|
||||
<Compile Include="IO\SettingReadException.cs" />
|
||||
<Compile Include="IO\Settings.cs" />
|
||||
<Compile Include="Net\Acceptor.cs" />
|
||||
<Compile Include="IO\Packet\PacketBase.cs" />
|
||||
<Compile Include="Net\Cryptography.cs" />
|
||||
<Compile Include="Net\SendPacketException.cs" />
|
||||
<Compile Include="Net\Session.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
19
trunk/Common/Constants.cs
Normal file
19
trunk/Common/Constants.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace KartExtreme
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly Version Version = new Version()
|
||||
{
|
||||
Localisation = 5002,
|
||||
Major = 1,
|
||||
Minor = 26
|
||||
};
|
||||
}
|
||||
|
||||
public struct Version
|
||||
{
|
||||
public short Localisation { get; set; }
|
||||
public short Major { get; set; }
|
||||
public short Minor { get; set; }
|
||||
}
|
||||
}
|
||||
206
trunk/Common/Data/Database.cs
Normal file
206
trunk/Common/Data/Database.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
using KartExtreme.IO;
|
||||
|
||||
namespace KartExtreme.Data
|
||||
{
|
||||
public class TemporaryConnection : IDisposable
|
||||
{
|
||||
private string oldHost;
|
||||
private string oldSchema;
|
||||
private string oldUsername;
|
||||
private string oldPassword;
|
||||
|
||||
internal TemporaryConnection(string host, string schema, string username, string password)
|
||||
{
|
||||
this.oldHost = Database.Host;
|
||||
this.oldSchema = Database.Schema;
|
||||
this.oldUsername = Database.Username;
|
||||
this.oldPassword = Database.Password;
|
||||
|
||||
Database.Host = host;
|
||||
Database.Schema = schema;
|
||||
Database.Username = username;
|
||||
Database.Password = password;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Database.Host = this.oldHost;
|
||||
Database.Schema = this.oldSchema;
|
||||
Database.Username = this.oldUsername;
|
||||
Database.Password = this.oldPassword;
|
||||
}
|
||||
}
|
||||
|
||||
public class TemporarySchema : IDisposable
|
||||
{
|
||||
private string oldSchema;
|
||||
|
||||
internal TemporarySchema(string schema)
|
||||
{
|
||||
this.oldSchema = Database.Schema;
|
||||
Database.Schema = schema;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Database.Schema = this.oldSchema;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Database
|
||||
{
|
||||
public static string Host { get; set; }
|
||||
public static string Schema { get; set; }
|
||||
public static string Username { get; set; }
|
||||
public static string Password { get; set; }
|
||||
|
||||
internal static string CorrectFields(string fields)
|
||||
{
|
||||
string final = string.Empty;
|
||||
string[] tokens = fields.Replace(",", " ").Replace(";", " ").Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int processed = 0;
|
||||
|
||||
foreach (string field in tokens)
|
||||
{
|
||||
final += field;
|
||||
processed++;
|
||||
|
||||
if (processed < tokens.Length)
|
||||
{
|
||||
final += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
internal static string ConnectionString
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("server={0}; database={1}; uid={2}; password={3}; convertzerodatetime=yes;",
|
||||
Database.Host,
|
||||
Database.Schema,
|
||||
Database.Username,
|
||||
Database.Password);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Execute(string nonQuery, params object[] args)
|
||||
{
|
||||
MySqlHelper.ExecuteNonQuery(Database.ConnectionString, string.Format(nonQuery, args));
|
||||
}
|
||||
|
||||
public static string DefaultSchema
|
||||
{
|
||||
get
|
||||
{
|
||||
string name = Assembly.GetEntryAssembly().GetName().Name;
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "WvsCenter":
|
||||
return "wvsmaple-center";
|
||||
|
||||
case "WvsLogin":
|
||||
return "wvsmaple-login";
|
||||
|
||||
default:
|
||||
return "wvsmaple-game";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Test()
|
||||
{
|
||||
using (MySqlConnection connection = new MySqlConnection(Database.ConnectionString))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
Log.Inform("Able to connect to database '{0}'.", connection.Database);
|
||||
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Analyze()
|
||||
{
|
||||
using (Database.TemporarySchema("information_schema"))
|
||||
{
|
||||
Meta.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Delete(string table, string constraints, params object[] args)
|
||||
{
|
||||
Database.Execute("DELETE FROM {0} WHERE {1}", table, string.Format(constraints, args));
|
||||
}
|
||||
|
||||
public static bool Exists(string table, string constraints, params object[] args)
|
||||
{
|
||||
using (MySqlDataReader reader = MySqlHelper.ExecuteReader(Database.ConnectionString, string.Format("SELECT * FROM {0} WHERE {1}", table, string.Format(constraints, args))))
|
||||
{
|
||||
return reader.HasRows;
|
||||
}
|
||||
}
|
||||
|
||||
public static dynamic Fetch(string table, string field, string constraints, params object[] args)
|
||||
{
|
||||
object value = new Datum(table).PopulateWith(field, constraints, args).Dictionary[field];
|
||||
|
||||
if (value is DBNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value is byte && Meta.IsBool(table, field))
|
||||
{
|
||||
return (byte)value > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExecuteScript(string host, string username, string password, string query, params object[] args)
|
||||
{
|
||||
using (MySqlConnection connection = new MySqlConnection(string.Format("SERVER={0}; UID={1}; PASSWORD={2};", host, username, password)))
|
||||
{
|
||||
connection.Open();
|
||||
new MySqlScript(connection, string.Format(query, args)).Execute();
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExecuteFile(string host, string username, string password, string path)
|
||||
{
|
||||
using (MySqlConnection connection = new MySqlConnection(string.Format("SERVER={0}; UID={1}; PASSWORD={2};", host, username, password)))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
using (TextReader reader = new StreamReader(path))
|
||||
{
|
||||
new MySqlScript(connection, reader.ReadToEnd()).Execute();
|
||||
}
|
||||
|
||||
connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static TemporaryConnection TemporaryConnection(string host, string schema, string username, string password)
|
||||
{
|
||||
return new TemporaryConnection(host, schema, username, password);
|
||||
}
|
||||
|
||||
public static TemporarySchema TemporarySchema(string schema)
|
||||
{
|
||||
return new TemporarySchema(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
307
trunk/Common/Data/Datum.cs
Normal file
307
trunk/Common/Data/Datum.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Dynamic;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace KartExtreme.Data
|
||||
{
|
||||
public class Datums : IEnumerable<Datum>
|
||||
{
|
||||
private List<Datum> Values { get; set; }
|
||||
private string Table { get; set; }
|
||||
|
||||
public Datums(string table = null)
|
||||
{
|
||||
this.Table = table;
|
||||
}
|
||||
|
||||
internal void PopulateInternal(string fields, string constraints)
|
||||
{
|
||||
this.Values = new List<Datum>();
|
||||
|
||||
string query = string.Format("SELECT {0} FROM {1}{2}", fields == null ? "*" : Database.CorrectFields(fields), this.Table, constraints != null ? " WHERE " + constraints : string.Empty);
|
||||
|
||||
using (MySqlDataReader reader = MySqlHelper.ExecuteReader(Database.ConnectionString, query))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Dictionary<string, object> dictionary = new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
dictionary.Add(reader.GetName(i), reader.GetValue(i));
|
||||
}
|
||||
|
||||
this.Values.Add(new Datum(this.Table, dictionary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void PopulateInternalFree(string query)
|
||||
{
|
||||
this.Values = new List<Datum>();
|
||||
|
||||
using (MySqlDataReader reader = MySqlHelper.ExecuteReader(Database.ConnectionString, query))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Dictionary<string, object> dictionary = new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
dictionary.Add(reader.GetName(i), reader.GetValue(i));
|
||||
}
|
||||
|
||||
this.Values.Add(new Datum(this.Table, dictionary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dynamic Populate()
|
||||
{
|
||||
this.PopulateInternal(null, null);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public dynamic Populate(string constraints, params object[] args)
|
||||
{
|
||||
this.PopulateInternal(null, string.Format(constraints, args));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public dynamic PopulateWith(string fields)
|
||||
{
|
||||
this.PopulateInternal(fields, null);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public dynamic PopulateWith(string fields, string constraints, params object[] args)
|
||||
{
|
||||
this.PopulateInternal(fields, string.Format(constraints, args));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public dynamic PopulateFree(string query)
|
||||
{
|
||||
this.PopulateInternalFree(query);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEnumerator<Datum> GetEnumerator()
|
||||
{
|
||||
foreach (Datum loopDatum in this.Values)
|
||||
{
|
||||
yield return loopDatum;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return (IEnumerator)this.GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
public class Datum : DynamicObject
|
||||
{
|
||||
public string Table { get; private set; }
|
||||
|
||||
internal Dictionary<string, object> Dictionary { get; set; }
|
||||
|
||||
public Datum(string table)
|
||||
{
|
||||
this.Table = table;
|
||||
this.Dictionary = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
internal Datum(string table, Dictionary<string, object> values)
|
||||
{
|
||||
this.Table = table;
|
||||
this.Dictionary = values;
|
||||
}
|
||||
|
||||
internal void Populate(string query)
|
||||
{
|
||||
using (MySqlDataReader reader = MySqlHelper.ExecuteReader(Database.ConnectionString, query))
|
||||
{
|
||||
if (reader.RecordsAffected > 1)
|
||||
{
|
||||
throw new RowNotUniqueException();
|
||||
}
|
||||
|
||||
if (!reader.HasRows)
|
||||
{
|
||||
throw new RowNotInTableException();
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
string name = reader.GetName(i);
|
||||
object value = reader.GetValue(i);
|
||||
|
||||
if (this.Dictionary.ContainsKey(name))
|
||||
{
|
||||
this.Dictionary[name] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Dictionary.Add(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public dynamic Populate(string constraints, params object[] args)
|
||||
{
|
||||
this.Populate(string.Format("SELECT * FROM {0} WHERE {1}", this.Table, string.Format(constraints, args)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public dynamic PopulateWith(string fields, string constraints, params object[] args)
|
||||
{
|
||||
this.Populate(string.Format("SELECT {0} FROM {1} WHERE {2}", Database.CorrectFields(fields), this.Table, string.Format(constraints, args)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Insert()
|
||||
{
|
||||
string fields = "( ";
|
||||
|
||||
int processed = 0;
|
||||
|
||||
foreach (KeyValuePair<string, object> loopPair in this.Dictionary)
|
||||
{
|
||||
fields += loopPair.Key;
|
||||
processed++;
|
||||
|
||||
if (processed < this.Dictionary.Count)
|
||||
{
|
||||
fields += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
fields += " ) VALUES ( ";
|
||||
|
||||
processed = 0;
|
||||
|
||||
foreach (KeyValuePair<string, object> loopPair in this.Dictionary)
|
||||
{
|
||||
fields += string.Format("'{0}'", loopPair.Value is bool ? (((bool)loopPair.Value) ? 1 : 0) : loopPair.Value);
|
||||
processed++;
|
||||
|
||||
if (processed < this.Dictionary.Count)
|
||||
{
|
||||
fields += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
fields += " )";
|
||||
|
||||
Database.Execute("INSERT INTO {0} {1}", this.Table, fields);
|
||||
}
|
||||
|
||||
public void Update(string constraints, params object[] args)
|
||||
{
|
||||
int processed = 0;
|
||||
|
||||
string fields = string.Empty;
|
||||
|
||||
foreach (KeyValuePair<string, object> loopPair in this.Dictionary)
|
||||
{
|
||||
fields += string.Format("{0}='{1}'", loopPair.Key, loopPair.Value is bool ? (((bool)loopPair.Value) ? 1 : 0) : loopPair.Value);
|
||||
processed++;
|
||||
|
||||
if (processed < this.Dictionary.Count)
|
||||
{
|
||||
fields += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
Database.Execute("UPDATE {0} SET {1} WHERE {2}", this.Table, fields, string.Format(constraints, args));
|
||||
}
|
||||
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
if (this.Dictionary.ContainsKey(binder.Name))
|
||||
{
|
||||
if (this.Dictionary[binder.Name] is DBNull)
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
else if (this.Dictionary[binder.Name] is byte && Meta.IsBool(this.Table, binder.Name))
|
||||
{
|
||||
result = (byte)this.Dictionary[binder.Name] > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = this.Dictionary[binder.Name];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default(object);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool TrySetMember(SetMemberBinder binder, object value)
|
||||
{
|
||||
if (value is DateTime)
|
||||
{
|
||||
if (Meta.IsDate(this.Table, binder.Name))
|
||||
{
|
||||
value = ((DateTime)value).ToString("yyyy-MM-dd");
|
||||
}
|
||||
else if (Meta.IsDateTime(this.Table, binder.Name))
|
||||
{
|
||||
value = ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.Dictionary.ContainsKey(binder.Name))
|
||||
{
|
||||
this.Dictionary[binder.Name] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Dictionary.Add(binder.Name, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = this.Table + " [ ";
|
||||
|
||||
int processed = 0;
|
||||
|
||||
foreach (KeyValuePair<string, object> value in this.Dictionary)
|
||||
{
|
||||
result += value.Key;
|
||||
processed++;
|
||||
|
||||
if (processed < this.Dictionary.Count)
|
||||
{
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
result += " ]";
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
trunk/Common/Data/Meta.cs
Normal file
70
trunk/Common/Data/Meta.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace KartExtreme.Data
|
||||
{
|
||||
internal static class Meta
|
||||
{
|
||||
public static Dictionary<string, Dictionary<string, Column>> Tables = new Dictionary<string, Dictionary<string, Column>>();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
Meta.Tables.Clear();
|
||||
|
||||
foreach (dynamic datum in new Datums("COLUMNS").Populate("TABLE_SCHEMA = '{0}'", Database.DefaultSchema))
|
||||
{
|
||||
Meta.Add(datum);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Add(dynamic datum)
|
||||
{
|
||||
Dictionary<string, Column> table;
|
||||
|
||||
string tableName = datum.TABLE_NAME;
|
||||
|
||||
if (Meta.Tables.ContainsKey(tableName))
|
||||
{
|
||||
table = Meta.Tables[tableName];
|
||||
}
|
||||
else
|
||||
{
|
||||
table = new Dictionary<string, Column>();
|
||||
Meta.Tables.Add(tableName, table);
|
||||
}
|
||||
|
||||
table.Add(datum.COLUMN_NAME, new Column(datum));
|
||||
}
|
||||
|
||||
public static bool IsBool(String tableName, String fieldName)
|
||||
{
|
||||
return Meta.Tables[tableName][fieldName].ColumnType == "tinyint(1) unsigned";
|
||||
}
|
||||
|
||||
public static bool IsDate(String tableName, String fieldName)
|
||||
{
|
||||
return Meta.Tables[tableName][fieldName].ColumnType == "date";
|
||||
}
|
||||
|
||||
public static bool IsDateTime(String tableName, String fieldName)
|
||||
{
|
||||
return Meta.Tables[tableName][fieldName].ColumnType == "datetime";
|
||||
}
|
||||
}
|
||||
|
||||
internal class Column
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public bool IsPrimaryKey { get; private set; }
|
||||
public bool IsUniqueKey { get; private set; }
|
||||
public string ColumnType { get; private set; }
|
||||
|
||||
public Column(dynamic datum)
|
||||
{
|
||||
this.Name = datum.COLUMN_NAME;
|
||||
this.IsPrimaryKey = datum.COLUMN_KEY == "PRI";
|
||||
this.IsUniqueKey = datum.COLUMN_KEY == "UNI";
|
||||
this.ColumnType = datum.COLUMN_TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
trunk/Common/Data/RowNotUniqueException.cs
Normal file
13
trunk/Common/Data/RowNotUniqueException.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace System.Data
|
||||
{
|
||||
public class RowNotUniqueException : DataException
|
||||
{
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Obtained row is not unique.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
151
trunk/Common/IO/Packet/InPacket.cs
Normal file
151
trunk/Common/IO/Packet/InPacket.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace KartExtreme.IO.Packet
|
||||
{
|
||||
public class InPacket : PacketBase
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private int _index;
|
||||
|
||||
public override int Position
|
||||
{
|
||||
get { return _index; }
|
||||
}
|
||||
public override int Length
|
||||
{
|
||||
get { return _buffer.Length; }
|
||||
}
|
||||
|
||||
public int Available
|
||||
{
|
||||
get
|
||||
{
|
||||
return _buffer.Length - _index;
|
||||
}
|
||||
}
|
||||
|
||||
public InPacket(byte[] packet)
|
||||
{
|
||||
_buffer = packet;
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
private void CheckLength(int length)
|
||||
{
|
||||
if (_index + length > _buffer.Length || length < 0)
|
||||
throw new PacketReadException("Not enough space");
|
||||
}
|
||||
|
||||
public bool ReadBool()
|
||||
{
|
||||
return ReadByte() == 1;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
CheckLength(1);
|
||||
return _buffer[_index++];
|
||||
}
|
||||
public sbyte ReadSByte()
|
||||
{
|
||||
return (sbyte)ReadByte();
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int count)
|
||||
{
|
||||
CheckLength(count);
|
||||
var temp = new byte[count];
|
||||
Buffer.BlockCopy(_buffer, _index, temp, 0, count);
|
||||
_index += count;
|
||||
return temp;
|
||||
}
|
||||
|
||||
public unsafe short ReadShort()
|
||||
{
|
||||
CheckLength(2);
|
||||
|
||||
short value;
|
||||
|
||||
fixed (byte* ptr = _buffer)
|
||||
{
|
||||
value = *(short*)(ptr + _index);
|
||||
}
|
||||
|
||||
_index += 2;
|
||||
|
||||
return value;
|
||||
}
|
||||
public ushort ReadUShort()
|
||||
{
|
||||
return (ushort)ReadShort();
|
||||
}
|
||||
|
||||
public unsafe int ReadInt()
|
||||
{
|
||||
CheckLength(4);
|
||||
|
||||
int value;
|
||||
|
||||
fixed (byte* ptr = _buffer)
|
||||
{
|
||||
value = *(int*)(ptr + _index);
|
||||
}
|
||||
|
||||
_index += 4;
|
||||
|
||||
return value;
|
||||
}
|
||||
public uint ReadUInt()
|
||||
{
|
||||
return (uint)ReadInt();
|
||||
}
|
||||
|
||||
public unsafe long ReadLong()
|
||||
{
|
||||
CheckLength(8);
|
||||
|
||||
long value;
|
||||
|
||||
fixed (byte* ptr = _buffer)
|
||||
{
|
||||
value = *(long*)(ptr + _index);
|
||||
}
|
||||
|
||||
_index += 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
public ulong ReadULong()
|
||||
{
|
||||
return (ulong)ReadLong();
|
||||
}
|
||||
|
||||
public string ReadString(int count)
|
||||
{
|
||||
CheckLength(count);
|
||||
|
||||
char[] final = new char[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
final[i] = (char)ReadByte();
|
||||
}
|
||||
|
||||
return new string(final);
|
||||
}
|
||||
|
||||
public void Skip(int count)
|
||||
{
|
||||
CheckLength(count);
|
||||
_index += count;
|
||||
}
|
||||
|
||||
public override byte[] ToArray()
|
||||
{
|
||||
var final = new byte[_buffer.Length];
|
||||
Buffer.BlockCopy(_buffer, 0, final, 0, _buffer.Length);
|
||||
return final;
|
||||
}
|
||||
}
|
||||
}
|
||||
167
trunk/Common/IO/Packet/OutPacket.cs
Normal file
167
trunk/Common/IO/Packet/OutPacket.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace KartExtreme.IO.Packet
|
||||
{
|
||||
public class OutPacket : PacketBase, IDisposable
|
||||
{
|
||||
private MemoryStream m_stream;
|
||||
private bool m_disposed;
|
||||
|
||||
public override int Length
|
||||
{
|
||||
get { return (int)m_stream.Position; }
|
||||
}
|
||||
public override int Position
|
||||
{
|
||||
get { return (int)m_stream.Position; }
|
||||
}
|
||||
|
||||
public bool Disposed
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_disposed;
|
||||
}
|
||||
}
|
||||
|
||||
public OutPacket(int size = 64)
|
||||
{
|
||||
m_stream = new MemoryStream(size);
|
||||
m_disposed = false;
|
||||
}
|
||||
|
||||
//From LittleEndianByteConverter by Shoftee
|
||||
private void Append(long value, int byteCount)
|
||||
{
|
||||
for (int i = 0; i < byteCount; i++)
|
||||
{
|
||||
m_stream.WriteByte((byte)value);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteBool(bool value)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
WriteByte(value ? (byte)1 : (byte)0);
|
||||
}
|
||||
|
||||
public void WriteByte(byte value = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
m_stream.WriteByte(value);
|
||||
}
|
||||
public void WriteSByte(sbyte value = 0)
|
||||
{
|
||||
WriteByte((byte)value);
|
||||
}
|
||||
|
||||
public void WriteBytes(params byte[] value)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
m_stream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
public void WriteShort(short value = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Append(value, 2);
|
||||
}
|
||||
public void WriteUShort(ushort value = 0)
|
||||
{
|
||||
WriteShort((short)value);
|
||||
}
|
||||
|
||||
public void WriteInt(int value = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Append(value, 4);
|
||||
}
|
||||
public void WriteUInt(uint value = 0)
|
||||
{
|
||||
WriteInt((int)value);
|
||||
}
|
||||
|
||||
public void WriteLong(long value = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
Append(value, 8);
|
||||
}
|
||||
public void WriteULong(ulong value = 0)
|
||||
{
|
||||
WriteLong((long)value);
|
||||
}
|
||||
|
||||
public void WriteString(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
WriteInt(value.Length);
|
||||
WriteString(value, value.Length);
|
||||
}
|
||||
public void WriteString(string value, int length)
|
||||
{
|
||||
if (value == null ||
|
||||
length < 1 ||
|
||||
length > value.Length)
|
||||
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
var bytes = Encoding.Unicode.GetBytes(value);
|
||||
var i = 0;
|
||||
|
||||
for (; i < value.Length & i < length; i++)
|
||||
{
|
||||
var offset = i * 2;
|
||||
this.WriteByte(bytes[offset]);
|
||||
this.WriteByte(bytes[offset + 1]);
|
||||
}
|
||||
|
||||
for (; i < length; i++)
|
||||
{
|
||||
this.WriteShort();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteHexString(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
value = value.Replace(" ", "");
|
||||
|
||||
for (int i = 0; i < value.Length; i += 2)
|
||||
{
|
||||
WriteByte(byte.Parse(value.Substring(i, 2), NumberStyles.HexNumber));
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (m_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public override byte[] ToArray()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return m_stream.ToArray();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_disposed = true;
|
||||
|
||||
if (m_stream != null)
|
||||
m_stream.Dispose();
|
||||
|
||||
m_stream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
trunk/Common/IO/Packet/PacketBase.cs
Normal file
25
trunk/Common/IO/Packet/PacketBase.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace KartExtreme.IO.Packet
|
||||
{
|
||||
public abstract class PacketBase
|
||||
{
|
||||
public abstract int Length { get; }
|
||||
public abstract int Position { get; }
|
||||
|
||||
public abstract byte[] ToArray();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
foreach (byte b in this.ToArray())
|
||||
{
|
||||
sb.AppendFormat("{0:X2} ", b);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
trunk/Common/IO/Packet/PacketReadException.cs
Normal file
16
trunk/Common/IO/Packet/PacketReadException.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KartExtreme.IO.Packet
|
||||
{
|
||||
public sealed class PacketReadException : Exception
|
||||
{
|
||||
public PacketReadException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
9
trunk/Common/IO/SettingReadException.cs
Normal file
9
trunk/Common/IO/SettingReadException.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace KartExtreme.IO
|
||||
{
|
||||
public class SettingReadException : Exception
|
||||
{
|
||||
public SettingReadException(string key) : base(string.Format("Failed to read setting '{0}'.", key)) { }
|
||||
}
|
||||
}
|
||||
188
trunk/Common/IO/Settings.cs
Normal file
188
trunk/Common/IO/Settings.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using KartExtreme.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace KartExtreme.IO
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
private const string Comment = "#";
|
||||
|
||||
public static string Path { get; private set; }
|
||||
|
||||
private static Dictionary<string, string> Dictionary;
|
||||
|
||||
public static void Initialize(string path = null)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
path = "Configuration.ini";
|
||||
}
|
||||
|
||||
if (Settings.Dictionary != null)
|
||||
{
|
||||
Settings.Dictionary.Clear();
|
||||
}
|
||||
|
||||
Settings.Path = path;
|
||||
Settings.Dictionary = new Dictionary<string, string>();
|
||||
|
||||
string[] array = Settings.Path.Split('\\');
|
||||
string name = array[array.Length - 1];
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("Unable to find configuration file '{0}'.", name));
|
||||
}
|
||||
else
|
||||
{
|
||||
string line;
|
||||
string currentSection = string.Empty;
|
||||
|
||||
using (StreamReader file = new StreamReader(path))
|
||||
{
|
||||
while ((line = file.ReadLine()) != null)
|
||||
{
|
||||
if (line.StartsWith(Settings.Comment))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith("[") && line.EndsWith("]"))
|
||||
{
|
||||
currentSection = line.Trim('[', ']');
|
||||
}
|
||||
else if (line.Contains("="))
|
||||
{
|
||||
Settings.Dictionary.Add(string.Format("{0}{1}{2}",
|
||||
currentSection,
|
||||
(currentSection != string.Empty) ? "/" : string.Empty,
|
||||
line.Split('=')[0]),
|
||||
line.Split('=')[1].Split(';')[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Database.Host = Settings.GetString("Database/Host");
|
||||
Database.Schema = Settings.GetString("Database/Schema");
|
||||
Database.Username = Settings.GetString("Database/Username");
|
||||
Database.Password = Settings.GetString("Database/Password");
|
||||
}
|
||||
|
||||
public static int GetInt(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return int.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static short GetShort(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return short.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static ushort GetUShort(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ushort.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte GetByte(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return byte.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static sbyte GetSByte(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return sbyte.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetBool(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return bool.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetString(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Settings.Dictionary[string.Format(key, args)];
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static IPAddress GetIPAddress(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Settings.Dictionary[string.Format(key, args)] == "localhost")
|
||||
{
|
||||
return IPAddress.Loopback;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IPAddress.Parse(Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetEnum<T>(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), Settings.Dictionary[string.Format(key, args)]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new SettingReadException(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
294
trunk/Common/IO/lOG.cs
Normal file
294
trunk/Common/IO/lOG.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace KartExtreme.IO
|
||||
{
|
||||
public static class Log
|
||||
{
|
||||
private const byte LabelWidth = 11;
|
||||
private static bool Entitled = false;
|
||||
|
||||
public static bool Running { get; private set; }
|
||||
|
||||
public static string Margin
|
||||
{
|
||||
get
|
||||
{
|
||||
return new string(' ', Log.LabelWidth);
|
||||
}
|
||||
}
|
||||
|
||||
public static string MaskString(string input, char mask = '*')
|
||||
{
|
||||
return new string(mask, input.Length);
|
||||
}
|
||||
|
||||
static Log()
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.Clear();
|
||||
|
||||
Log.Running = true;
|
||||
}
|
||||
|
||||
public static string Input(string label)
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
Log.WriteItem("Input", ConsoleColor.Cyan, string.Empty);
|
||||
Console.Write(label);
|
||||
return Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static string Input(string label, string defaultValue)
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
Log.WriteItem("Input", ConsoleColor.Cyan, string.Empty);
|
||||
Console.Write(label);
|
||||
string result = Console.ReadLine();
|
||||
|
||||
if (result == string.Empty)
|
||||
{
|
||||
result = defaultValue;
|
||||
Console.CursorTop--;
|
||||
Console.CursorLeft = Log.Margin.Length + label.Length;
|
||||
|
||||
Console.WriteLine(defaultValue == string.Empty ? "(None)" : result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a labeled item to the output.
|
||||
/// </summary>
|
||||
/// <param name="label">The label</param>
|
||||
/// <param name="labelColor">The label's color</param>
|
||||
/// <param name="value">The text</param>
|
||||
/// <param name="args">Arguments</param>
|
||||
private static void WriteItem(string label, ConsoleColor labelColor, string value, params object[] args)
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(' ', Log.LabelWidth - label.Length - 3);
|
||||
sb.Append("[");
|
||||
sb.Append(label);
|
||||
sb.Append("]");
|
||||
sb.Append(" ");
|
||||
|
||||
label = sb.ToString();
|
||||
value = string.Format(value, args);
|
||||
|
||||
Console.ForegroundColor = labelColor;
|
||||
Console.Write(label);
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
|
||||
bool first = true;
|
||||
|
||||
foreach (string s in value.Split('\n'))
|
||||
{
|
||||
string[] lines = new string[(int)Math.Ceiling((float)s.Length / (float)(Console.BufferWidth - Log.LabelWidth))];
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (i == lines.Length - 1)
|
||||
{
|
||||
lines[i] = s.Substring((Console.BufferWidth - Log.LabelWidth) * i);
|
||||
}
|
||||
else
|
||||
{
|
||||
lines[i] = s.Substring((Console.BufferWidth - Log.LabelWidth) * i, (Console.BufferWidth - Log.LabelWidth));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
Console.Write(Log.Margin);
|
||||
}
|
||||
|
||||
if ((line.Length + Log.LabelWidth) < Console.BufferWidth)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
else if ((line.Length + Log.LabelWidth) == Console.BufferWidth)
|
||||
{
|
||||
Console.Write(line);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SkipLine()
|
||||
{
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public static void Entitle(string value, params object[] args)
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("\n");
|
||||
|
||||
//for (int i = 0; i < Console.WindowWidth / 2 - value.Length / 2; i++)
|
||||
//{
|
||||
// sb.Append(' ');
|
||||
//}
|
||||
|
||||
sb.Append('\t');
|
||||
|
||||
sb.Append((Log.Entitled ? "" : "") + string.Format(value, args) + '\n');
|
||||
Console.WriteLine(sb.ToString());
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
Console.Title = string.Format(value, args);
|
||||
|
||||
Log.Entitled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Inform(string value, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Info", ConsoleColor.White, value, args);
|
||||
}
|
||||
|
||||
public static void Inform(object value)
|
||||
{
|
||||
Log.Inform(value.ToString());
|
||||
}
|
||||
|
||||
public static void Warn(string value, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Warning", ConsoleColor.Yellow, value, args);
|
||||
}
|
||||
|
||||
public static void Warn(object value)
|
||||
{
|
||||
Log.Warn(value.ToString());
|
||||
}
|
||||
|
||||
public static void Error(string value, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Error", ConsoleColor.Red, value, args);
|
||||
}
|
||||
|
||||
public static bool ShowStackTrace { get; set; }
|
||||
|
||||
public static void Error(Exception exception)
|
||||
{
|
||||
Log.WriteItem("Error", ConsoleColor.Red, Log.ShowStackTrace ? exception.ToString() : exception.Message);
|
||||
}
|
||||
|
||||
public static void Error(string label, Exception exception, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Error", ConsoleColor.Red, "{0}\n{1}", string.Format(label, args), Log.ShowStackTrace ? exception.ToString() : exception.Message);
|
||||
}
|
||||
|
||||
public static void Debug(string value, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Debug", ConsoleColor.Green, value, args);
|
||||
}
|
||||
|
||||
public static void Success(object value)
|
||||
{
|
||||
Log.Debug(value.ToString());
|
||||
}
|
||||
|
||||
public static void Sql(string value, params object[] args)
|
||||
{
|
||||
Log.WriteItem("Sql", ConsoleColor.Magenta, value, args);
|
||||
}
|
||||
|
||||
public static void Hex(string label, byte[] value, params object[] args)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(string.Format(label, args));
|
||||
sb.Append('\n');
|
||||
|
||||
if (value == null || value.Length == 0)
|
||||
{
|
||||
sb.Append("(Empty)");
|
||||
}
|
||||
else
|
||||
{
|
||||
int lineSeparation = 0;
|
||||
|
||||
foreach (byte b in value)
|
||||
{
|
||||
if (lineSeparation == 16)
|
||||
{
|
||||
sb.Append('\n');
|
||||
lineSeparation = 0;
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0:X2} ", b);
|
||||
lineSeparation++;
|
||||
}
|
||||
}
|
||||
|
||||
Log.WriteItem("Hex", ConsoleColor.Magenta, sb.ToString());
|
||||
}
|
||||
|
||||
public static void Hex(string label, byte b, params object[] args)
|
||||
{
|
||||
Log.Hex(label, new byte[] { b }, args);
|
||||
}
|
||||
|
||||
public static void Quit()
|
||||
{
|
||||
Log.Running = false;
|
||||
|
||||
Log.Inform("Press any key to quit . . .");
|
||||
|
||||
Console.Read();
|
||||
}
|
||||
|
||||
public static LoadingIndicator Load(string header)
|
||||
{
|
||||
return new LoadingIndicator(header);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoadingIndicator : IDisposable
|
||||
{
|
||||
public static bool ShowTime { get; set; }
|
||||
|
||||
private DateTime LoadStartTime { get; set; }
|
||||
|
||||
internal LoadingIndicator(string header)
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
Console.Write("{0} {1}... ", Log.Margin, header);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (typeof(Log))
|
||||
{
|
||||
if (LoadingIndicator.ShowTime)
|
||||
{
|
||||
Console.WriteLine("({0}ms)", (DateTime.Now - this.LoadStartTime).Milliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
65
trunk/Common/Net/Acceptor.cs
Normal file
65
trunk/Common/Net/Acceptor.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using KartExtreme.IO;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace KartExtreme.Net
|
||||
{
|
||||
public sealed class Acceptor
|
||||
{
|
||||
public const int Backlog = 25;
|
||||
|
||||
public ushort Port { get; private set; }
|
||||
|
||||
private readonly TcpListener m_listener;
|
||||
|
||||
private bool m_disposed;
|
||||
|
||||
public Action<Socket> OnClientAccepted;
|
||||
|
||||
public Acceptor(ushort port) : this(IPAddress.Any, port) { }
|
||||
|
||||
public Acceptor(IPAddress ip, ushort port)
|
||||
{
|
||||
Port = port;
|
||||
m_listener = new TcpListener(IPAddress.Any, port);
|
||||
OnClientAccepted = null;
|
||||
m_disposed = false;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
m_listener.Start(Backlog);
|
||||
m_listener.BeginAcceptSocket(EndAccept, null);
|
||||
|
||||
Log.Inform("Initialized clients listener on {0}.", this.Port);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void EndAccept(IAsyncResult iar)
|
||||
{
|
||||
if (m_disposed) { return; }
|
||||
|
||||
Socket client = m_listener.EndAcceptSocket(iar);
|
||||
|
||||
if (OnClientAccepted != null)
|
||||
OnClientAccepted(client);
|
||||
|
||||
if (!m_disposed)
|
||||
m_listener.BeginAcceptSocket(EndAccept, null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_disposed = true;
|
||||
m_listener.Server.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
trunk/Common/Net/Cryptography.cs
Normal file
74
trunk/Common/Net/Cryptography.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace KartExtreme.Net
|
||||
{
|
||||
class Cryptography
|
||||
{
|
||||
public static uint HashPacket(byte[] pData, uint pKey, bool pEncrypt, uint npacketLength)
|
||||
{
|
||||
uint hash1 = (pKey ^ 0x14B307C8);
|
||||
uint hash2 = (pKey ^ 0x8CBF12AC);
|
||||
uint hash3 = (pKey ^ 0x240397C1);
|
||||
uint hash4 = (pKey ^ 0xF3BD29C0);
|
||||
|
||||
uint checksum = 0;
|
||||
int packetLength = (int)npacketLength;//pData.Length;
|
||||
|
||||
|
||||
Action<int, uint> doHash = (index, hash) =>
|
||||
{
|
||||
pData[index + 0] ^= (byte)(hash & 0xFF);
|
||||
pData[index + 1] ^= (byte)((hash << 8) & 0xFF);
|
||||
pData[index + 2] ^= (byte)((hash << 16) & 0xFF);
|
||||
pData[index + 3] ^= (byte)((hash << 24) & 0xFF);
|
||||
};
|
||||
|
||||
int offset = 0;
|
||||
int i = 0;
|
||||
for (i = 0; i < (packetLength >> 4); ++i)
|
||||
{
|
||||
if (pEncrypt)
|
||||
checksum ^= (uint)(BitConverter.ToUInt32(pData, offset + 12) ^ BitConverter.ToUInt32(pData, offset + 8) ^ BitConverter.ToUInt32(pData, offset + 4) ^ BitConverter.ToUInt32(pData, offset + 0));
|
||||
|
||||
doHash(offset + 0, hash1);
|
||||
doHash(offset + 4, hash2);
|
||||
doHash(offset + 8, hash3);
|
||||
doHash(offset + 12, hash4);
|
||||
|
||||
if (!pEncrypt)
|
||||
checksum ^= (uint)(BitConverter.ToUInt32(pData, offset + 12) ^ BitConverter.ToUInt32(pData, offset + 8) ^ BitConverter.ToUInt32(pData, offset + 4) ^ BitConverter.ToUInt32(pData, offset + 0));
|
||||
|
||||
offset += 16;
|
||||
}
|
||||
|
||||
|
||||
i *= 16;
|
||||
int sub = 0;
|
||||
|
||||
///Note: it has a problem with Checksum Calculation
|
||||
while (i < packetLength)
|
||||
{
|
||||
if (pEncrypt) checksum ^= (uint)(pData[i] << sub);
|
||||
pData[i] ^= (byte)(hash1 << (sub * 8));
|
||||
if (!pEncrypt) checksum ^= (uint)(pData[i] << sub);
|
||||
sub++;
|
||||
i++;
|
||||
}
|
||||
|
||||
/*
|
||||
for (int i = offset; i < packetLength; i++)
|
||||
{
|
||||
if (pEncrypt) checksum ^= (uint)(pData[i] << sub);
|
||||
pData[i] ^= (byte)(hash1 << (sub * 8));
|
||||
if (!pEncrypt) checksum ^= (uint)(pData[i] << sub);
|
||||
sub++;
|
||||
}*/
|
||||
|
||||
return checksum;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
trunk/Common/Net/SendPacketException.cs
Normal file
12
trunk/Common/Net/SendPacketException.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace KartExtreme.Net
|
||||
{
|
||||
public sealed class SendPacketException : Exception
|
||||
{
|
||||
public SendPacketException()
|
||||
: base("Disconnected while sending packet")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
294
trunk/Common/Net/Session.cs
Normal file
294
trunk/Common/Net/Session.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using KartExtreme.IO;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Sockets;
|
||||
using KartExtreme.IO.Packet;
|
||||
|
||||
namespace KartExtreme.Net
|
||||
{
|
||||
public abstract class Session
|
||||
{
|
||||
private Socket _socket;
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _bufferIndex;
|
||||
|
||||
private bool _header;
|
||||
private bool _connected;
|
||||
|
||||
private string _label;
|
||||
|
||||
private uint riv;
|
||||
private uint siv;
|
||||
|
||||
private object _lock;
|
||||
|
||||
public string Label
|
||||
{
|
||||
get
|
||||
{
|
||||
return _label;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _connected;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Session class.
|
||||
/// </summary>
|
||||
/// <param name="socket"></param>
|
||||
public Session(Socket socket)
|
||||
{
|
||||
this._socket = socket;
|
||||
this._socket.NoDelay = true;
|
||||
|
||||
this._label = this._socket.RemoteEndPoint.ToString();
|
||||
|
||||
this._connected = true;
|
||||
|
||||
this._lock = new object();
|
||||
|
||||
this.InitiateReceive(4, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the receiving mechanism.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the data</param>
|
||||
/// <param name="header">Indicates if a header is received</param>
|
||||
private void InitiateReceive(uint length, bool header = false)
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._header = header;
|
||||
this._buffer = new byte[length];
|
||||
this._bufferIndex = 0;
|
||||
|
||||
this.BeginReceive();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins to asynchronously receive data from the socket.
|
||||
/// </summary>
|
||||
private void BeginReceive()
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var error = SocketError.Success;
|
||||
|
||||
this._socket.BeginReceive(this._buffer,
|
||||
this._bufferIndex,
|
||||
this._buffer.Length - this._bufferIndex,
|
||||
SocketFlags.None, out error,
|
||||
EndReceive,
|
||||
null);
|
||||
|
||||
if (error != SocketError.Success)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data from the callback and handles it.
|
||||
/// </summary>
|
||||
/// <param name="iar"></param>
|
||||
private void EndReceive(IAsyncResult iar)
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var error = SocketError.Success;
|
||||
int received = this._socket.EndReceive(iar, out error);
|
||||
|
||||
if (received == 0 || error != SocketError.Success)
|
||||
{
|
||||
this.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
this._bufferIndex += received;
|
||||
|
||||
if (this._bufferIndex == this._buffer.Length)
|
||||
{
|
||||
if (this._header)
|
||||
{
|
||||
uint header = BitConverter.ToUInt32(this._buffer, 0);
|
||||
|
||||
if(riv != 0)
|
||||
header = (riv ^ header ^ 0xF834A608);
|
||||
|
||||
this.InitiateReceive(header, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (riv != 0)
|
||||
{
|
||||
int bufferLength = _buffer.Length - 4;
|
||||
|
||||
uint originalChecksum = (uint)(riv ^ BitConverter.ToUInt32(this._buffer, bufferLength) ^ 0xC9F84A90);
|
||||
|
||||
Array.Resize<byte>(ref this._buffer, bufferLength);
|
||||
|
||||
uint checksum = Cryptography.HashPacket(this._buffer, riv, false, (uint)bufferLength);
|
||||
|
||||
if (originalChecksum != checksum)
|
||||
{
|
||||
Log.Warn("Decrypt Checksum different!");
|
||||
}
|
||||
|
||||
riv += 21446425;
|
||||
|
||||
if (riv == 0)
|
||||
riv = 1;
|
||||
}
|
||||
|
||||
this.OnPacket(new InPacket(this._buffer));
|
||||
this.InitiateReceive(4, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.BeginReceive();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the initial patch data packet to the socket.
|
||||
/// </summary>
|
||||
/// <param name="version">The version's information</param>
|
||||
/// <param name="patchLocation">The patch data URL</param>
|
||||
public void SendPatchData(Version version, string patchLocation)
|
||||
{
|
||||
//Fuck!!!!!
|
||||
Random rnd = new Random();
|
||||
|
||||
uint val_first = 0;//(uint)rnd.Next();
|
||||
uint val_second = 0;//(uint)rnd.Next() ;
|
||||
|
||||
using (OutPacket outPacket = new OutPacket())
|
||||
{
|
||||
outPacket.WriteHexString("80 05 2B 28"); // NOTE: Header. Do not change. Probably typeid() of handler.
|
||||
outPacket.WriteShort(version.Localisation);
|
||||
outPacket.WriteShort(version.Major);
|
||||
outPacket.WriteShort(version.Minor);
|
||||
outPacket.WriteString(patchLocation);
|
||||
outPacket.WriteInt((int)val_first); // NOTE: IV Keys. Should we do random stuffs?
|
||||
outPacket.WriteInt((int)val_second);
|
||||
outPacket.WriteByte(0xE0); // NOTE: Some stuff used on Region / Service Area.
|
||||
outPacket.WriteHexString("03 00 09 03 00 00 03 00 00 00 00 00 83 A3 E5 47 00 00 15 00 0D 00 00 00 03 00 00 00 00 00 65");
|
||||
|
||||
this.Send(outPacket);
|
||||
}
|
||||
|
||||
riv = val_first ^ val_second;
|
||||
siv = val_first ^ val_second;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a KartExtreme.IO.OutPacket array to the socket.
|
||||
/// </summary>
|
||||
/// <param name="outPacket"></param>
|
||||
public void Send(OutPacket outPacket)
|
||||
{
|
||||
this.Send(outPacket.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends data to the socket.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public void Send(byte[] buffer)
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
|
||||
byte[] data = new byte[buffer.Length + (siv != 0 ? 8 : 4)];
|
||||
|
||||
if (siv == 0)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes((int)buffer.Length), 0, data, 0, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint newHash = Cryptography.HashPacket(buffer, siv, true, (uint)buffer.Length);
|
||||
|
||||
// NOTE: Length at beginning.
|
||||
Buffer.BlockCopy(BitConverter.GetBytes((int)(siv ^ (buffer.Length + 4) ^ 0xF834A608)), 0, data, 0, 4);
|
||||
// NOTE: Checksum at end.
|
||||
Buffer.BlockCopy(BitConverter.GetBytes((uint)(siv ^ newHash ^ 0xC9F84A90)), 0, data, data.Length - 4, 4);
|
||||
|
||||
siv += 0x1473F19;
|
||||
|
||||
if (siv == 0)
|
||||
siv = 1;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(buffer, 0, data, 4, buffer.Length);
|
||||
|
||||
this.SendRaw(data);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendRaw(byte[] data)
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while (offset < data.Length)
|
||||
{
|
||||
SocketError error = SocketError.Success;
|
||||
int sent = this._socket.Send(data, offset, data.Length - offset, SocketFlags.None, out error);
|
||||
|
||||
if (sent == 0 || error != SocketError.Success)
|
||||
{
|
||||
throw new SendPacketException();
|
||||
}
|
||||
|
||||
offset += sent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the socket.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (!this._connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._socket.Shutdown(SocketShutdown.Both);
|
||||
this._socket.Close();
|
||||
|
||||
this.OnDisconnect();
|
||||
}
|
||||
|
||||
public abstract void OnDisconnect();
|
||||
public abstract void OnPacket(InPacket inPacket);
|
||||
}
|
||||
}
|
||||
36
trunk/Common/Properties/AssemblyInfo.cs
Normal file
36
trunk/Common/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
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("Common")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Common")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[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(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("740eb84a-eada-43e8-b6ed-a278afc3b1a1")]
|
||||
|
||||
// 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.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user