From 78e0338d628a8a4fdec4d7a58cc49e32f9f37462 Mon Sep 17 00:00:00 2001 From: GittyMac Date: Sun, 1 May 2022 17:55:28 -0400 Subject: [PATCH] Initial Commit --- .gitignore | 401 +++++++++++++++++++++++++++++ A1Emu.sln | 25 ++ A1Emu/A1Emu.csproj | 13 + A1Emu/A1_FKPlugins/A1_Galaxy.cs | 170 +++++++++++++ A1Emu/A1_FKPlugins/A1_System.cs | 429 ++++++++++++++++++++++++++++++++ A1Emu/A1_FKPlugins/A1_Trunk.cs | 170 +++++++++++++ A1Emu/A1_FKPlugins/A1_User.cs | 237 ++++++++++++++++++ A1Emu/A1_Utils/A1_Parser.cs | 70 ++++++ A1Emu/FKObjects/FKUser.cs | 19 ++ A1Emu/Program.cs | 71 ++++++ LICENSE | 21 ++ NOTICE | 51 ++++ README | 13 + 13 files changed, 1690 insertions(+) create mode 100644 .gitignore create mode 100644 A1Emu.sln create mode 100644 A1Emu/A1Emu.csproj create mode 100644 A1Emu/A1_FKPlugins/A1_Galaxy.cs create mode 100644 A1Emu/A1_FKPlugins/A1_System.cs create mode 100644 A1Emu/A1_FKPlugins/A1_Trunk.cs create mode 100644 A1Emu/A1_FKPlugins/A1_User.cs create mode 100644 A1Emu/A1_Utils/A1_Parser.cs create mode 100644 A1Emu/FKObjects/FKUser.cs create mode 100644 A1Emu/Program.cs create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f5bdc1a --- /dev/null +++ b/.gitignore @@ -0,0 +1,401 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +# Visual Studio Code config +.vscode/ \ No newline at end of file diff --git a/A1Emu.sln b/A1Emu.sln new file mode 100644 index 0000000..1b3d35e --- /dev/null +++ b/A1Emu.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "A1Emu", "A1Emu\A1Emu.csproj", "{2A9F6B9B-635E-4BE3-8CE6-CC1DB1D2DFAF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2A9F6B9B-635E-4BE3-8CE6-CC1DB1D2DFAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A9F6B9B-635E-4BE3-8CE6-CC1DB1D2DFAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A9F6B9B-635E-4BE3-8CE6-CC1DB1D2DFAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A9F6B9B-635E-4BE3-8CE6-CC1DB1D2DFAF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DF7176DC-53DE-4DDE-A9E9-F1107E35D26C} + EndGlobalSection +EndGlobal diff --git a/A1Emu/A1Emu.csproj b/A1Emu/A1Emu.csproj new file mode 100644 index 0000000..b0fc61e --- /dev/null +++ b/A1Emu/A1Emu.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0 + + + + + + + + diff --git a/A1Emu/A1_FKPlugins/A1_Galaxy.cs b/A1Emu/A1_FKPlugins/A1_Galaxy.cs new file mode 100644 index 0000000..d856b9a --- /dev/null +++ b/A1Emu/A1_FKPlugins/A1_Galaxy.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using NetCoreServer; + +//ArkONE Plugin 7 +//Galaxy + +class A1_Galaxy : TcpSession +{ + A1_Parser a1parser; + + FKUser a1user; + + int sessionPort = 80; + + public A1_Galaxy(TcpServer server, int port) : base(server){a1parser = new A1_Parser(); a1user = new FKUser(); sessionPort = port;} + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + string message = Encoding.UTF8.GetString(buffer, (int)offset, (int)size); + Console.WriteLine("[7 - Galaxy] Recieved: " + message); + + bool isMultiOutput = false; + string response = ""; + + string[] commands = a1parser.ParseReceivedMessage(message); + foreach(string command in commands) + { + string[] commandInfo = a1parser.ParseCommand(command); + switch(commandInfo[0]){ + case "a_lgu": + response += LoginGuestUser(commandInfo[1],commandInfo[2],commandInfo[3]); + break; + case "a_gpd": + //TODO - start other A1 plugins + response += GetPluginDetails(commandInfo[1]); + break; + } + } + + //Message Output + List d = new List(); + Byte[] reply = Encoding.ASCII.GetBytes(response); + byte[] b2 = new byte[] {0x00}; + d.Add(reply); + d.Add(b2); + byte[] b3 = d.SelectMany(a => a).ToArray(); + if(!isMultiOutput) + SendAsync(b3,0,b3.Length); + else Server.Multicast(b3,0,b3.Length); SendAsync(b3,0,b3.Length); + + // Multicast message to all connected sessions + //Server.Multicast(message); + } + + string LoginGuestUser(string d, string a, string c){ + var responseStream = new MemoryStream(); + using (XmlWriter writer = XmlWriter.Create(responseStream)) + { + writer.WriteStartElement("a_lgu"); + + //Result + writer.WriteElementString("r", "0"); + + //User ID + writer.WriteElementString("u", "0"); + + //Username + writer.WriteElementString("n", "GUESTUSER"); + + //Password + writer.WriteElementString("p", ""); + + //Service ID + writer.WriteElementString("s", "1"); + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetPluginDetails(string p){ + var responseStream = new MemoryStream(); + + string serviceID = p; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + switch(p){ + //Galaxy + case "7": + xPort = (sessionPort + 7).ToString(); + bPort = (sessionPort + 7).ToString(); + break; + + //Trunk + case "10": + xPort = (sessionPort + 10).ToString(); + bPort = (sessionPort + 10).ToString(); + break; + } + + using (XmlWriter writer = XmlWriter.Create(responseStream)) + { + writer.WriteStartElement("a_gpd"); + + //Service ID + writer.WriteElementString("s", serviceID); + + //xIPAddress + writer.WriteElementString("xi", xIPAddress); + + //xPort + writer.WriteElementString("xp", xPort); + + //bIPAddress + writer.WriteElementString("bi", bIPAddress); + + //Plugin ID + writer.WriteElementString("p", p); + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + protected override void OnConnected() + { + Console.WriteLine($"[7 - Galaxy] TCP session with Id {Id} connected!"); + } + + protected override void OnDisconnected() + { + Console.WriteLine($"[7 - Galaxy] TCP session with Id {Id} disconnected!"); + } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[7 - Galaxy] Error - {error}"); + } +} + +class ServerGalaxy : TcpServer +{ + int serverPort = 80; + + public ServerGalaxy(IPAddress address, int port) : base(address, port) {serverPort = port;} + + protected override TcpSession CreateSession() { return new A1_Galaxy(this, serverPort); } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[7 - Galaxy] TCP server caught an error with code {error}"); + } +} \ No newline at end of file diff --git a/A1Emu/A1_FKPlugins/A1_System.cs b/A1Emu/A1_FKPlugins/A1_System.cs new file mode 100644 index 0000000..5338598 --- /dev/null +++ b/A1Emu/A1_FKPlugins/A1_System.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Security.Cryptography; +using MySqlConnector; +using System.Xml; +using NetCoreServer; + +//ArkONE Plugin 0 +//System + +class A1_System : TcpSession +{ + A1_Parser a1parser; + + FKUser a1user; + + int sessionPort = 80; + + string sqServer = ""; + + public A1_System(TcpServer server, int port, string sqServerInput) : base(server){a1parser = new A1_Parser(); a1user = new FKUser(); sessionPort = port; sqServer = sqServerInput;} + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + string message = Encoding.ASCII.GetString(buffer, (int)offset, (int)size - 1); + Console.WriteLine("[0 - System] Recieved: " + message); + + bool isMultiOutput = false; + string response = ""; + + string[] commands = a1parser.ParseReceivedMessage(message); + foreach (string command in commands) + { + string[] commandInfo = a1parser.ParseCommand(command); + switch (commandInfo[0]) + { + case "a_lgu": + response += LoginGuestUser(commandInfo[1], commandInfo[2], commandInfo[3]).Remove(0, 1); + break; + case "a_gpd": + response += GetPluginDetails(commandInfo[1]).Remove(0, 1); + break; + case "a_gsd": + response += GetServiceDetails(commandInfo[1]).Remove(0, 1); + break; + case "a_lru": + response += LoginRegisteredUser(commandInfo[1], commandInfo[2], commandInfo[3]).Remove(0, 1); + break; + case "u_reg": + response += RegisterUser(commandInfo[1], commandInfo[2], commandInfo[3], commandInfo[4], commandInfo[5], commandInfo[6], commandInfo[7]).Remove(0, 1); + break; + } + } + + + Console.WriteLine(response); + + //Message Output + List d = new List(); + Byte[] reply = Encoding.ASCII.GetBytes(response); + byte[] b2 = new byte[] {0x00}; + d.Add(reply); + d.Add(b2); + byte[] b3 = d.SelectMany(a => a).ToArray(); + if(!isMultiOutput) + SendAsync(b3, 0, b3.Length); + else Server.Multicast(b3,0,b3.Length); SendAsync(b3,0,b3.Length); + + // Multicast message to all connected sessions + //Server.Multicast(message); + } + + string LoginGuestUser(string d, string a, string c){ + var responseStream = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_lgu"); + + //Result + writer.WriteAttributeString("r", "0"); + + //User ID + writer.WriteAttributeString("u", "0"); + + //Username + writer.WriteAttributeString("n", "GUESTUSER"); + + //Password + writer.WriteAttributeString("p", ""); + + //Service ID + writer.WriteAttributeString("s", "1"); + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetPluginDetails(string p){ + var responseStream = new MemoryStream(); + + string serviceID = p; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + switch(p){ + //User + case "1": + xPort = (sessionPort + 1).ToString(); + bPort = (sessionPort + 1).ToString(); + break; + + //Galaxy + case "7": + xPort = (sessionPort + 7).ToString(); + bPort = (sessionPort + 7).ToString(); + break; + + //Trunk + case "10": + xPort = (sessionPort + 10).ToString(); + bPort = (sessionPort + 10).ToString(); + break; + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_gpd"); + + //Service ID + writer.WriteAttributeString("s", serviceID); + + //xIPAddress + writer.WriteAttributeString("xi", xIPAddress); + + //xPort + writer.WriteAttributeString("xp", xPort); + + //bIPAddress + writer.WriteAttributeString("bi", bIPAddress); + + //bPort + writer.WriteAttributeString("bp", bPort); + + //Plugin ID + writer.WriteAttributeString("p", p); + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetServiceDetails(string s) + { + var responseStream = new MemoryStream(); + + string serviceID = s; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + switch(s){ + //User + case "1": + xPort = (sessionPort + 1).ToString(); + bPort = (sessionPort + 1).ToString(); + break; + + //Galaxy + case "7": + xPort = (sessionPort + 7).ToString(); + bPort = (sessionPort + 7).ToString(); + break; + + //Trunk + case "10": + xPort = (sessionPort + 10).ToString(); + bPort = (sessionPort + 10).ToString(); + break; + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_gsd"); + + //Service ID + writer.WriteAttributeString("s", serviceID); + + //xIPAddress + writer.WriteAttributeString("xi", xIPAddress); + + //xPort + writer.WriteAttributeString("xp", xPort); + + //bIPAddress + writer.WriteAttributeString("bi", bIPAddress); + + //bPort + writer.WriteAttributeString("bp", bPort); + + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string RegisterUser(string sa, string sq, string d, string a, string c, string p, string l) + { + var responseStream = new MemoryStream(); + + int resultCode = 0; + + //Making the password more secure. + byte[] salt; + new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); + var encryptedP = new Rfc2898DeriveBytes(p,salt,10000); + + byte[] hash = encryptedP.GetBytes(20); + byte[] hashBytes = new byte[36]; + + Array.Copy(salt,0,hashBytes,0,16); + Array.Copy(hash,0,hashBytes,16,20); + string password = Convert.ToBase64String(hashBytes); + + //Setting up SQL connection. + FKUser user = new FKUser(); + var con1 = new MySqlConnection(sqServer); + + string sql1 = "SELECT * FROM user WHERE u=@Uname"; + MySqlCommand sqCommand = new MySqlCommand(sql1,con1); + sqCommand.Parameters.AddWithValue("@Uname", l); + con1.Open(); + using (MySqlDataReader sqReader = sqCommand.ExecuteReader()) + { + while (sqReader.Read()) + { + user.username = sqReader["u"].ToString(); + } + con1.Close(); + } + if(user.username != null){ + //If username is claimed. + resultCode = 1; + }else{ + //Adds user to the DB. + int uID = 0; + using (var con = new MySqlConnection(sqServer)){ + con.Open(); + + using (var cmd = new MySqlCommand("SELECT COUNT(*) FROM user", con)) + { + uID = Convert.ToInt32(cmd.ExecuteScalar()); + } + + var sql = "INSERT INTO user(u, p, sq, sa, uID) VALUES(@user, @pass, @secQ, @secA, @userID)"; + using (var cmd = new MySqlCommand(sql, con)){ + cmd.Parameters.AddWithValue("@user", l); + cmd.Parameters.AddWithValue("@pass", password); + cmd.Parameters.AddWithValue("@secQ", sq); + cmd.Parameters.AddWithValue("@secA", sa); + cmd.Parameters.AddWithValue("@userID", uID); + cmd.Prepare(); + + cmd.ExecuteNonQuery(); + cmd.Dispose(); + } + con.Close(); + } + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("u_reg"); + + //Result + writer.WriteAttributeString("r", resultCode.ToString()); + + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string LoginRegisteredUser(string l, string p, string n) + { + var responseStream = new MemoryStream(); + + int resultCode = 0; + + //Setting up SQL connection. + var con = new MySqlConnection(sqServer); + + string sql = "SELECT * FROM user WHERE u=@Uname"; + MySqlCommand sqCommand = new MySqlCommand(sql,con); + sqCommand.Parameters.AddWithValue("@Uname", n); + con.Open(); + using (MySqlDataReader sqReader = sqCommand.ExecuteReader()) + { + while (sqReader.Read()) + { + a1user.username = sqReader["u"].ToString(); + a1user.password = sqReader["p"].ToString(); + a1user.userID = Int32.Parse(sqReader["uID"].ToString()); + } + con.Close(); + } + if(a1user.username != null){ + //If user exists. + try{ + //Getting password ready for check. + byte[] hashBytes = Convert.FromBase64String(a1user.password); + byte[] salt = new byte[16]; + Array.Copy(hashBytes,0,salt,0,16); + var encryptedP = new Rfc2898DeriveBytes(p,salt,10000); + byte[] hash = encryptedP.GetBytes(20); + + bool correct = true; + for (int iC = 0; iC < 20; iC++){ + if(hashBytes[iC + 16] != hash[iC]){ + correct = false; + } + } + if(correct){ + //Password correct. + resultCode = 0; + }else{ + //Password incorrect. + resultCode = 4; + } + }catch{ + //If the check fails. + resultCode = 6; + } + }else{ + //If no user is found. + resultCode = 5; + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_lru"); + + //Result + writer.WriteAttributeString("r", resultCode.ToString()); + + if(resultCode == 0){ + //User ID + writer.WriteAttributeString("u", a1user.userID.ToString()); + } + + //service? + writer.WriteAttributeString("s", "1"); + + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + protected override void OnConnected() + { + Console.WriteLine($"[0 - System] TCP session with Id {Id} connected!"); + } + + protected override void OnDisconnected() + { + Console.WriteLine($"[0 - System] TCP session with Id {Id} disconnected!"); + } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[0 - System] Error - {error}"); + } + +} + +class ServerSystem : TcpServer +{ + int serverPort = 80; + + string sqServer = ""; + + public ServerSystem(IPAddress address, int port, string sqServerInput) : base(address, port) {serverPort = port; sqServer = sqServerInput;} + + protected override TcpSession CreateSession() { return new A1_System(this, serverPort, sqServer); } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[0 - System] TCP server caught an error with code {error}"); + } +} \ No newline at end of file diff --git a/A1Emu/A1_FKPlugins/A1_Trunk.cs b/A1Emu/A1_FKPlugins/A1_Trunk.cs new file mode 100644 index 0000000..3f99489 --- /dev/null +++ b/A1Emu/A1_FKPlugins/A1_Trunk.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using NetCoreServer; + +//ArkONE Plugin 10 +//Trunk + +class A1_Trunk : TcpSession +{ + A1_Parser a1parser; + + FKUser a1user; + + int sessionPort = 80; + + public A1_Trunk(TcpServer server, int port) : base(server){a1parser = new A1_Parser(); a1user = new FKUser(); sessionPort = port;} + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + string message = Encoding.UTF8.GetString(buffer, (int)offset, (int)size); + Console.WriteLine("[10 - Trunk] Recieved: " + message); + + bool isMultiOutput = false; + string response = ""; + + string[] commands = a1parser.ParseReceivedMessage(message); + foreach(string command in commands) + { + string[] commandInfo = a1parser.ParseCommand(command); + switch(commandInfo[0]){ + case "a_lgu": + response += LoginGuestUser(commandInfo[1],commandInfo[2],commandInfo[3]); + break; + case "a_gpd": + //TODO - start other A1 plugins + response += GetPluginDetails(commandInfo[1]); + break; + } + } + + //Message Output + List d = new List(); + Byte[] reply = Encoding.ASCII.GetBytes(response); + byte[] b2 = new byte[] {0x00}; + d.Add(reply); + d.Add(b2); + byte[] b3 = d.SelectMany(a => a).ToArray(); + if(!isMultiOutput) + SendAsync(b3,0,b3.Length); + else Server.Multicast(b3,0,b3.Length); SendAsync(b3,0,b3.Length); + + // Multicast message to all connected sessions + //Server.Multicast(message); + } + + string LoginGuestUser(string d, string a, string c){ + var responseStream = new MemoryStream(); + using (XmlWriter writer = XmlWriter.Create(responseStream)) + { + writer.WriteStartElement("a_lgu"); + + //Result + writer.WriteElementString("r", "0"); + + //User ID + writer.WriteElementString("u", "0"); + + //Username + writer.WriteElementString("n", "GUESTUSER"); + + //Password + writer.WriteElementString("p", ""); + + //Service ID + writer.WriteElementString("s", "1"); + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetPluginDetails(string p){ + var responseStream = new MemoryStream(); + + string serviceID = p; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + switch(p){ + //Galaxy + case "7": + xPort = (sessionPort + 7).ToString(); + bPort = (sessionPort + 7).ToString(); + break; + + //Trunk + case "10": + xPort = (sessionPort + 10).ToString(); + bPort = (sessionPort + 10).ToString(); + break; + } + + using (XmlWriter writer = XmlWriter.Create(responseStream)) + { + writer.WriteStartElement("a_gpd"); + + //Service ID + writer.WriteElementString("s", serviceID); + + //xIPAddress + writer.WriteElementString("xi", xIPAddress); + + //xPort + writer.WriteElementString("xp", xPort); + + //bIPAddress + writer.WriteElementString("bi", bIPAddress); + + //Plugin ID + writer.WriteElementString("p", p); + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + protected override void OnConnected() + { + Console.WriteLine($"[10 - Trunk] TCP session with Id {Id} connected!"); + } + + protected override void OnDisconnected() + { + Console.WriteLine($"[10 - Trunk] TCP session with Id {Id} disconnected!"); + } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[10 - Trunk] Error - {error}"); + } +} + +class ServerTrunk : TcpServer +{ + int serverPort = 80; + + public ServerTrunk(IPAddress address, int port) : base(address, port) {serverPort = port;} + + protected override TcpSession CreateSession() { return new A1_Trunk(this, serverPort); } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[10 - Trunk] TCP server caught an error with code {error}"); + } +} \ No newline at end of file diff --git a/A1Emu/A1_FKPlugins/A1_User.cs b/A1Emu/A1_FKPlugins/A1_User.cs new file mode 100644 index 0000000..5dd9057 --- /dev/null +++ b/A1Emu/A1_FKPlugins/A1_User.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using NetCoreServer; + +//ArkONE Plugin 1 +//User + +class A1_User : TcpSession +{ + A1_Parser a1parser; + + FKUser a1user; + + int sessionPort = 80; + + public A1_User(TcpServer server, int port) : base(server){a1parser = new A1_Parser(); a1user = new FKUser(); sessionPort = port;} + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + string message = Encoding.ASCII.GetString(buffer, (int)offset, (int)size - 1); + Console.WriteLine("[1 - User] Recieved: " + message); + + bool isMultiOutput = false; + string response = ""; + + string[] commands = a1parser.ParseReceivedMessage(message); + foreach (string command in commands) + { + string[] commandInfo = a1parser.ParseCommand(command); + switch (commandInfo[0]) + { + case "a_lgu": + response += LoginGuestUser(commandInfo[1], commandInfo[2], commandInfo[3]).Remove(0, 1); + break; + case "a_gpd": + response += GetPluginDetails(commandInfo[1]).Remove(0, 1); + break; + case "a_gsd": + response += GetServiceDetails(commandInfo[1]).Remove(0, 1); + break; + } + } + + + Console.WriteLine(response); + + //Message Output + List d = new List(); + Byte[] reply = Encoding.ASCII.GetBytes(response); + byte[] b2 = new byte[] {0x00}; + d.Add(reply); + d.Add(b2); + byte[] b3 = d.SelectMany(a => a).ToArray(); + if(!isMultiOutput) + SendAsync(b3, 0, b3.Length); + else Server.Multicast(b3,0,b3.Length); SendAsync(b3,0,b3.Length); + + // Multicast message to all connected sessions + //Server.Multicast(message); + } + + string LoginGuestUser(string d, string a, string c){ + var responseStream = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_lgu"); + + //Result + writer.WriteAttributeString("r", "0"); + + //User ID + writer.WriteAttributeString("u", "0"); + + //Username + writer.WriteAttributeString("n", "GUESTUSER"); + + //Password + writer.WriteAttributeString("p", ""); + + //Service ID + writer.WriteAttributeString("s", "1"); + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetPluginDetails(string p){ + var responseStream = new MemoryStream(); + + string serviceID = "1"; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + switch(p){ + //User + case "1": + xPort = (sessionPort + 1).ToString(); + bPort = (sessionPort + 1).ToString(); + break; + + //Galaxy + case "7": + xPort = (sessionPort + 7).ToString(); + bPort = (sessionPort + 7).ToString(); + break; + + //Trunk + case "10": + xPort = (sessionPort + 10).ToString(); + bPort = (sessionPort + 10).ToString(); + break; + } + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_gpd"); + + //Service ID + writer.WriteAttributeString("s", serviceID); + + //xIPAddress + writer.WriteAttributeString("xi", xIPAddress); + + //xPort + writer.WriteAttributeString("xp", xPort); + + //bIPAddress + writer.WriteAttributeString("bi", bIPAddress); + + //bPort + writer.WriteAttributeString("bp", bPort); + + //Plugin ID + writer.WriteAttributeString("p", p); + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + string GetServiceDetails(string s) + { + var responseStream = new MemoryStream(); + + string serviceID = s; + + string xIPAddress = "localhost"; + string xPort = "80"; + + string bIPAddress = "localhost"; + string bPort = "80"; + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.OmitXmlDeclaration = true; + settings.ConformanceLevel = ConformanceLevel.Fragment; + using (XmlWriter writer = XmlWriter.Create(responseStream, settings)) + { + writer.WriteStartElement("a_gsd"); + + //Service ID + writer.WriteAttributeString("s", serviceID); + + //xIPAddress + writer.WriteAttributeString("xi", xIPAddress); + + //xPort + writer.WriteAttributeString("xp", xPort); + + //bIPAddress + writer.WriteAttributeString("bi", bIPAddress); + + //bPort + writer.WriteAttributeString("bp", bPort); + + writer.WriteEndElement(); + writer.Flush(); + writer.Close(); + } + + return System.Text.ASCIIEncoding.UTF8.GetString(responseStream.ToArray()); + } + + protected override void OnConnected() + { + Console.WriteLine($"[1 - User] TCP session with Id {Id} connected!"); + } + + protected override void OnDisconnected() + { + Console.WriteLine($"[1 - User] TCP session with Id {Id} disconnected!"); + } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[1 - User] Error - {error}"); + } + +} + +class ServerUser : TcpServer +{ + int serverPort = 80; + + public ServerUser(IPAddress address, int port) : base(address, port) {serverPort = port;} + + protected override TcpSession CreateSession() { return new A1_User(this, serverPort); } + + protected override void OnError(SocketError error) + { + Console.WriteLine($"[1 - User] TCP server caught an error with code {error}"); + } +} \ No newline at end of file diff --git a/A1Emu/A1_Utils/A1_Parser.cs b/A1Emu/A1_Utils/A1_Parser.cs new file mode 100644 index 0000000..62230be --- /dev/null +++ b/A1Emu/A1_Utils/A1_Parser.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml; +using System.Xml.Linq; + +public class A1_Parser { + + public string[] ParseReceivedMessage(string xmlCommand){ + //TODO - RoutingStrings (#x|x|x|x) + + List commandsList = new List(); + + //Splits raw commands by the null character placed in between. To parse Routing Strings. + string[] rawCommandsList = xmlCommand.Split("\0").Where(x => !string.IsNullOrWhiteSpace(x)).ToArray(); + + xmlCommand = xmlCommand.Replace("\0", string.Empty); + + XDocument breakableXMLMessage = XDocument.Parse(@"" + xmlCommand + @""); + + foreach(var comElement in breakableXMLMessage.Descendants("A1Command").Elements()) + { + commandsList.Add(comElement.ToString()); + } + + return rawCommandsList.ToArray(); + } + + public string[] ParseCommand(string command){ + List commandInfo = new List(); + + string substring = ""; + + if(command.EndsWith("#")){ + substring = command.Substring(command.LastIndexOf(">") + 1, command.LastIndexOf("#") - command.LastIndexOf(">") - 1); + command = command.Substring(0, command.LastIndexOf(">") + 1); + } + + XDocument commandXML = XDocument.Parse(command); + XElement commandRoot = commandXML.Root; + + commandInfo.Add(commandRoot.Name.LocalName); + + var attrNames = ( + from a in commandRoot.Attributes() + select a.Value + ); + + foreach(string value in attrNames){ + commandInfo.Add(value); + } + + if(substring != ""){ + string[] substringData = substring.Split("|"); + foreach(string substringID in substringData){ + commandInfo.Add(substringID); + } + } + + return commandInfo.ToArray(); + } + +} \ No newline at end of file diff --git a/A1Emu/FKObjects/FKUser.cs b/A1Emu/FKObjects/FKUser.cs new file mode 100644 index 0000000..5e64e7d --- /dev/null +++ b/A1Emu/FKObjects/FKUser.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +public class FKUser{ + public string connectionID; + + //A1Data + + //User ID in Database + public int userID; + //Username + public string username; + //Password (SENT VIA PLAIN TEXT --- FK USES REALLY BAD SECURITY!!!) + public string password; + public int status; + //Buddy List + public List buddies; +} \ No newline at end of file diff --git a/A1Emu/Program.cs b/A1Emu/Program.cs new file mode 100644 index 0000000..b457951 --- /dev/null +++ b/A1Emu/Program.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading; +using System.IO; +using System.Reflection; +using System.Data; + +class Program +{ + static void Main(string[] args) + { + //Configuration + int port = 2000; + string directory = ""; + string sqServer = ""; + string ip = "localhost"; + if(!File.Exists(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/config")){ + Console.WriteLine("[ERROR] A1Emu did not find a config file in the application's directory."); + } + try{ + foreach (string line in File.ReadLines(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/config")) + { + if(line.Contains("port=")){ + port = Int32.Parse(line.Replace("port=","")); + }else if(line.Contains("directory=")){ + directory = line.Replace("directory=",""); + }else if(line.Contains("sq=")){ + sqServer = line.Replace("sq=",""); + }else if(line.Contains("ip=")){ + ip = line.Replace("ip=",""); + } + } + }catch{ + Console.WriteLine("[A1Emu] ERROR! Failed to load the config file."); + } + + //TODO - Generate a SQL DB. + + //Plugin 0/1/7 + //Thread t = new Thread(delegate () + //{ + //replace the IP with your system IP Address... + //ServerRewrite myserver = new ServerRewrite(ip, port, directory, sqServer); + //}); + //t.Start(); + + var A1_Plugin0 = new ServerSystem(System.Net.IPAddress.Parse(ip), port, sqServer); + A1_Plugin0.Start(); + + var A1_Plugin1 = new ServerUser(System.Net.IPAddress.Parse(ip), port + 1); + A1_Plugin1.Start(); + + var A1_Plugin7 = new ServerGalaxy(System.Net.IPAddress.Parse(ip), port + 7); + A1_Plugin7.Start(); + + var A1_Plugin10 = new ServerTrunk(System.Net.IPAddress.Parse(ip), port + 10); + A1_Plugin10.Start(); + + Console.WriteLine("[A1Emu] Started!"); + + for(;;){ + string line = Console.ReadLine(); + if (string.IsNullOrEmpty(line)) + break; + } + + A1_Plugin0.Stop(); + A1_Plugin1.Stop(); + A1_Plugin7.Stop(); + A1_Plugin10.Stop(); + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a503df5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Lako + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..ddab2e5 --- /dev/null +++ b/NOTICE @@ -0,0 +1,51 @@ +A1Emu uses the open source libraries listed below. + +----- + +NetCoreServer (https://github.com/chronoxor/NetCoreServer) +MIT License + +Copyright (c) 2019-2022 Ivan Shynkarenka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + +MySqlConnector (https://github.com/mysql-net/MySqlConnector) +MIT License + +Copyright (c) 2016-2022 Bradley Grainger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..e6fcc22 --- /dev/null +++ b/README @@ -0,0 +1,13 @@ +# A1Emu +An open source recreation of the ArkONE servers used for the 2007 game, U.B. Funkeys. + +## Installation +A1Emu requires .NET 6 and MariaDB to run properly. + +To get these dependencies on Ubuntu/Debian: + +```apt install dotnet-sdk-6.0 dotnet-runtime-6.0 mariadb-server``` + +To get these dependencies on RHEL/CentOS/Fedora: + +```dnf install dotnet-sdk-6.0 dotnet-runtime-6.0 mariadb-server``` \ No newline at end of file