INACTIVE Hariak Emulator - From Scratch - Clean&Stable - MongoDB + MySQL

Status
Not open for further replies.

Seriosk

Programmer;
Oct 29, 2016
256
105
200a4ede0927474db0ba0022e7c5d8c0.png

Hello to the members of DevBest, today I am starting a new development from scratch. Today I'll be starting an emulator from scratch in the C# languages that will be clean, stable and contain good quality code. I know I have already started a previous development thread on emulators from scratch but as some of you know, I wasn't happy with the structure and the code inside of that project, therefor I have decided to start fresh, learn from my past mistakes on other projects, and hopefully write better, maintainable code.

Sirius was very well written in compartment to other emulators, but there were things I never really followed through with doing such as properly disposing of classes, initializing things correctly, and I never really had much experience in the MySQL driver for C# and how it worked.

In this new development, I will learn from my mistakes, write good quality code as I always do on public releases, and also keep you constantly updated with new code, screenshots, even gifs and other things. I kind of went quiet on my old development and worked behind the scenes and just posted updates when I felt like it.

If anyone wants a copy of Sirius (Completed) to improve, extend, or anything developer-based then you can inbox me, but I won't be publicly releasing it to just anyone, the role-play edition wasn't completed therefor I won't be releasing that either but feel free to message me if you're a developer looking to improve it or anything like that, the only people with a copy right now are myself and @JynX.

Main Focus
Write a clean, good quality emulator that comes with MySQL and MongoDB support, along with Mono support running on the latest PRODUCTION build of Habbo's client.

Anything Unique?
Along with scheduled database backups, support for MongoDB as well as MySQL, and a clean code base, there isn't much unique, but then there isn't usually anything unique about Habbo. I am sure I'll add unique features once the base has been completed and its in a production environment working well. An emulator can have millions of features, but them features don't matter if the base of the emulator is badly designed or badly written by a sloppy programmer who doesn't understand the language in-depth.

For the people who says I'll just give up on this
I finished Sirius, and I started a roleplay edition which admittedly I got bored with, but as you can see I am pretty motivated to my large projects that interest me, the roleplay version of Sirius didn't.

Have I used any of Sirius's code in this?
Well, the only thing I have kept the same (with minor edits) is the Configuration as it didn't really need rewriting, and it was pretty well coded. If you're a developer you'll understand that Configuration isn't really a large amount of code in Emulators, 70-80 lines if that.

Want to help?
I am actually looking for developers to help on this emulator, as I have previously said I want this emulator to be coded with good quality code, so if you're a kid that just codes basic commands please don't even ask. If you actually understand C# in-depth and know how to write key parts of emulators from scratch using clean code then send me a PM and I'll be sure to give you access to a repository with it on.

Awaiting List
  • Configuration [100%]
  • MongoDB [20%]
  • MySQL [0%]
  • Utilities [100%]
  • Logging (NLog) [100%]
  • Humanizer [100%]
  • GameSockets [100%]
  • Player Authentication [80%]

Library's
/ Packages used?
  • Humanizer
  • NLog
  • MongoDB driver
  • MySQL driver

Credits
  • @JynX => Revision Updating
  • Myself => Everything Else

Code Snippets?
Code:
namespace Hariak_Emulator.Emulator.Core.Config
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using NLog;
    using Utilities;

    internal sealed class ConfigManager : IDisposable
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        private Dictionary<string, string> _configItems;
        private string _configFile;

        internal ConfigManager(string configFile)
        {
            _configItems = new Dictionary<string, string>();
            _configFile = configFile;
        }

        internal void LoadConfig()
        {
            try
            {
                if (!File.Exists(_configFile) || Utilities.IsFileLocked(new FileInfo(_configFile)))
                {
                    Logger.Error("Either your config file couldn't be found, or its open somewhere else.");

                    Console.ReadKey(true);
                    Environment.Exit(0);
                }

                _configItems = File.ReadLines(_configFile).Where(IsConfigurationLine).Select(line => line.Split('=')).ToDictionary(line => line[0], line => line[1]);
            }
            catch (Exception exception)
            {
                Logger.Fatal("Error while loading config: " + exception.Message);
                Logger.Fatal(exception.StackTrace);

                Console.ReadKey(true);
                Environment.Exit(0);
            }
        }

        private static bool IsConfigurationLine(string line)
        {
            return !line.StartsWith("#") && line.Contains("=");
        }

        internal string GetConfigValueByKey(string key)
        {
            string value;

            if (!_configItems.TryGetValue(key, out value))
            {
                Logger.Warn("Missing configuration key `" + key + "`");
            }

            return value;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing)
            {
                return;
            }
 
            _configItems.Clear();
            _configItems = null;
            _configFile = "";
        }
    }
}
Code:
namespace Hariak_Emulator.Emulator.Core.Database
{
    using System;
    using MongoDB.Bson;
    using MongoDB.Driver;
    using NLog;

    internal sealed class DatabaseManager
    {
        private readonly MongoClient _mongoClient;
        private readonly IMongoDatabase _mongoDatabase;

        internal DatabaseManager()
        {
            var databaseHostName = Hariak.HariakServer.Config.GetConfigValueByKey("database.mongodb.hostname");
            var databasePort = Hariak.HariakServer.Config.GetConfigValueByKey("database.mongodb.port");
            var databaseName = Hariak.HariakServer.Config.GetConfigValueByKey("database.mongodb.name");

            _mongoClient = new MongoClient("mongodb://" + databaseHostName + ":" + databasePort);
            _mongoDatabase = _mongoClient.GetDatabase(databaseName);
        }
    }
}
Code:
namespace Hariak_Emulator.Emulator.Core.Network.GameSockets
{
    using System;
    using System.Net;
    using System.Net.Sockets;
    using Base.Game.Habbo.Players.Players;
    using NLog;

    internal sealed class GameSocketManager : IDisposable
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        private readonly Socket _gameSocket;

        internal GameSocketManager()
        {
            _gameSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        internal void Start()
        {
            var gameSocketPort = Convert.ToInt32(Hariak.HariakServer.Config.GetConfigValueByKey("game.socket.port"));
            var gameSocketBacklog = Convert.ToInt32(Hariak.HariakServer.Config.GetConfigValueByKey("game.socket.backlog"));

            _gameSocket.Bind(new IPEndPoint(IPAddress.Any, gameSocketPort));
            _gameSocket.Listen(gameSocketBacklog);
            _gameSocket.BeginAccept(OnAcceptConnection, _gameSocket);
        }

        private void OnAcceptConnection(IAsyncResult asyncResult)
        {
            try
            {
                if (_gameSocket == null)
                {
                    return;
                }

                var server = (Socket)asyncResult.AsyncState;
                var client = server.EndAccept(asyncResult);
 
                var newPlayer = new Player(client);

                if (!Hariak.HariakServer.GameManager.PlayerManager.TryAddPlayer(newPlayer))
                {
                    Logger.Error("Failed to register new player on " + client.RemoteEndPoint);
                }

                Logger.Info("Registered a new player to the server.");
            }
            catch (SocketException socketException)
            {
                Logger.Fatal("Failed to accept socket connection: " + socketException.Message);
                Logger.Fatal(socketException);
            }
            finally
            {
                _gameSocket?.BeginAccept(OnAcceptConnection, _gameSocket);
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing)
            {
                return;
            }

            if (_gameSocket == null)
            {
                return;
            }

            _gameSocket.Shutdown(SocketShutdown.Both);
            _gameSocket.Close();
            _gameSocket.Dispose();
        }
    }
}
Code:
namespace Hariak_Emulator
{
    using System;
    using System.Diagnostics;
    using Emulator.Base.Game;
    using Emulator.Core.Config;
    using Emulator.Core.Database;
    using Emulator.Core.Network.GameSockets;
    using Emulator.Core.Server;
    using NLog;

    internal sealed class HariakServer : IDisposable
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        internal void Load()
        {
            try
            {
                Logger.Trace("Loading Emulator...");
                var stopwatch = Stopwatch.StartNew();

                var configStopwatch = Stopwatch.StartNew();

                Config = new ConfigManager("assets/config/hariak_config.ini");
                Config.LoadConfig();

                configStopwatch.Stop();
                Logger.Trace("Loaded Configuration [took " + configStopwatch.ElapsedMilliseconds + "ms]");
 
                var databaseStopwatch = Stopwatch.StartNew();

                Database = new DatabaseManager();

                databaseStopwatch.Stop();
                Logger.Trace("Loaded Database [took " + databaseStopwatch.ElapsedMilliseconds + "ms]");

                var gameStopwatch = Stopwatch.StartNew();

                GameManager = new GameManager();

                gameStopwatch.Stop();
                Logger.Trace("Loaded Game [took " + databaseStopwatch.ElapsedMilliseconds + "ms]");

                var socketStopwatch = Stopwatch.StartNew();

                GameSocketManagement = new GameSocketManager();
                GameSocketManagement.Start();

                socketStopwatch.Stop();
                Logger.Trace("Loaded Game Sockets [took " + socketStopwatch.ElapsedMilliseconds + "ms]");

                var workerStopwatch = Stopwatch.StartNew();

                ServerWorker = new ServerWorker();
                ServerWorker.StartWorker();

                workerStopwatch.Stop();
                Logger.Trace("Loaded Server Worker [took " + workerStopwatch.ElapsedMilliseconds + "ms]");

                DateTimeStarted = DateTime.Now;

                stopwatch.Stop();
                Logger.Debug("Emulator has loaded [took " + stopwatch.ElapsedMilliseconds + "ms]");

                Console.WriteLine();
            }
            catch (Exception exception)
            {
                Logger.Fatal("Error during startup: " + exception.Message);
                Logger.Fatal(exception);
                throw;
            }
        }

        public ConfigManager Config { get; private set; }
        public DatabaseManager Database { get; set; }
        public GameManager GameManager { get; private set; }
        public GameSocketManager GameSocketManagement { get; set; }
        public ServerWorker ServerWorker { get; private set; }
        public DateTime DateTimeStarted { get; private set; }

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing)
            {
                return;
            }

            Config.Dispose();
            GameSocketManagement.Dispose();
            ServerWorker.Dispose();
        }
    }
}

Screenshots? (Just some random screenies, took them for personal use really)

9298497d2faf41e08af17b4b453db670.png

5a65990d67e34553a9842fb597549a47.png


537e28dc5f9a4b14869cdcd2f9c01095.png


765edd048fac405682a09173482f55a9.png

Configuration File layout? basic INI...

Code:
# Database [mongoDB] configuration
database.mongodb.hostname=localhost
database.mongodb.port=27017
database.mongodb.name=hariak

# GameSocket configuration
game.socket.port=30000
game.socket.backlog=500[/FONT]

fa26094142864941a2e99b8d137d55fc.png


GitHub repo?
Thank you, let's create a great emulator!
 
Last edited:

JynX

Posting Freak
Feb 6, 2016
710
438
Good luck with this hope to see something different than Sirius was as Sirius was decently coded ;) Best of wishes
 

Seriosk

Programmer;
Oct 29, 2016
256
105
Good luck with this hope to see something different than Sirius was as Sirius was decently coded ;) Best of wishes
Thankssssssss

Improved the code structure, callstack and readability.

  • Renamed Player to PlayerConnection
  • Removed PlayerPacketHandler, making easier callstack.
  • No longer hardcoded the packet buffer sizes
  • Properly disposed PlayerConnection class
Full details of this commit can be found here:

Thanks, Liam Savage.
 

Seriosk

Programmer;
Oct 29, 2016
256
105
Decided to start using Dapper over the entity framework for ORM database work. All in all I haven't put much time in to this recently but I plan on progressing with it.
 
Progress?

f4c5fef3159e41d09b4ac330de470087.png


Before everyone asks why I haven't got PlayerData as an out parameter, its becasue its a base class
Code:
class PlayerConnection : PlayerData, IDisposable

PlayerConnection class is getting pretty filled up with things that should have their own classes, such as pretty much 100% of packet processing connected to the players socket has been implemented inside PlayerConnection, I already have a PlayerUtilities extension class but I was hoping to keep that to methods such as SendNotification, SendWhisper and other simple things.
 
Where did your comment go @Quackster ?

On topic, just finished Saved Searches Component, also provided each player component with an IComponent to follow component rules.
Code:
namespace Hariak_Emulator.Emulator.Base.Game.Habbo.Players.Players.Components.Navigation
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using NLog;

    internal sealed class PlayerSearchComponent : IDisposable, IComponent
    {
        private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

        private readonly PlayerConnection _playerConnection;
        private ConcurrentDictionary<int, PlayerSearch> _savedSearches;

        internal PlayerSearchComponent(PlayerConnection playerConnection)
        {
            _playerConnection = playerConnection;
            _savedSearches = new ConcurrentDictionary<int, PlayerSearch>();
        }

        public void InitializeComponent()
        {
            using (var databaseConnection = Hariak.HariakServer.Database.NewDatabaseConnection)
            {
                var playerId = _playerConnection.SelectColumnInt("id");

                databaseConnection.SetQuery("SELECT `id`, `filter`, `search_code` FROM `user_saved_searches` WHERE `user_id` = @userId");
                databaseConnection.AppendParameter("userId", playerId);

                using (var reader = databaseConnection.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        if (!_savedSearches.TryAdd(reader.GetInt32("id"), new PlayerSearch()))
                        {
                            Logger.Error("Failed to load saved search " + reader.GetInt32("id") + " for player " + playerId);
                        }
                    }
                }
            }
        }

        internal ICollection<PlayerSearch> SavedSearches => _savedSearches.Values;

        public void Dispose()
        {
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!disposing)
            {
                return;
            }

            _savedSearches.Clear();
            _savedSearches = null;
        }
    }
}
 

Core

Member
Nov 10, 2016
356
138
Why do you use internal so much? What if you wanted to make your code more scalable with some form of module based structure? Allowing developers to extend on the project without modifying the core?
 

Seriosk

Programmer;
Oct 29, 2016
256
105
Why do you use internal so much? What if you wanted to make your code more scalable with some form of module based structure? Allowing developers to extend on the project without modifying the core?
I hope you know, internal is declared by default via class names after 'class', although you can explicitly declare the internal keyword..

If I didn't declare it, the compiler would.
If Nothing => Internal (Default?)

I suggest you give these a read:





On-Topic: I'll be committing a large commit to the GitHub sometime soon, it's very much behind what I currently have on my server.

Authentication still has a bit to go:
Code:
internal void Authenticate(string sso) //todo: fix sso query indexing
{
    try
    {
        if (!TryLoadPlayerData(this, sso))
        {
            Dispose(true);
            return;
        }

        //todo: check if banned
        //todo: load statistics and other table related data
     
        SendComposer(new AuthenticationComposer());
        //todo: AvatarEffectsComposer
        //todo: NavigatorSettingsComposer
        //todo: FavouritesComposer
        //todo: FigureSetIdsComposer
        //todo: UserRightsComposer
        SendComposer(new CheckAvailabilityComposer());
        //todo: AchievementScoreComposer

        SendComposer(new BuildersClubMembershipComposer());

        //todo: Badge Composer
        //todo: Cfh Initialization composer
        //todo: Sound Composer
        UpdateColumn("machine_id", MachineId);

        //todo: permissions
        //todo: subscriptions

        Utilities.SendMotdNotification("Welcome message, using Hariak Emulator.");
        Logger.Info(SelectColumn("username") + " logged in");
    }
    catch (Exception exception)
    {
        Logger.Error(exception, "Error during player authentication.");
    }
}

I couldn't do a lot of composers today, but now I have finished coding effects, navigation searches and ignore components I'll be able to code a the composers for them.
 
Last edited:

Core

Member
Nov 10, 2016
356
138
I hope you know, internal is declared by default via class names after 'class', although you can explicitly declare the internal keyword..

If I didn't declare it, the compiler would.
If Nothing => Internal (Default?)

I suggest you give these a read:





On-Topic: I'll be committing a large commit to the GitHub sometime soon, it's very much behind what I currently have on my server.

Authentication still has a bit to go:
Code:
internal void Authenticate(string sso) //todo: fix sso query indexing
{
    try
    {
        if (!TryLoadPlayerData(this, sso))
        {
            Dispose(true);
            return;
        }

        //todo: check if banned
        //todo: load statistics and other table related data
    
        SendComposer(new AuthenticationComposer());
        //todo: AvatarEffectsComposer
        //todo: NavigatorSettingsComposer
        //todo: FavouritesComposer
        //todo: FigureSetIdsComposer
        //todo: UserRightsComposer
        SendComposer(new CheckAvailabilityComposer());
        //todo: AchievementScoreComposer

        SendComposer(new BuildersClubMembershipComposer());

        //todo: Badge Composer
        //todo: Cfh Initialization composer
        //todo: Sound Composer
        UpdateColumn("machine_id", MachineId);

        //todo: permissions
        //todo: subscriptions

        Utilities.SendMotdNotification("Welcome message, using Hariak Emulator.");
        Logger.Info(SelectColumn("username") + " logged in");
    }
    catch (Exception exception)
    {
        Logger.Error(exception, "Error during player authentication.");
    }
}

I couldn't do a lot of composers today, but now I have finished coding effects, navigation searches and ignore components I'll be able to code a the composers for them.

I was referring to the public access modifier which will allow access to such functions outside of the assembly - which is why I said scalable. Not the default.
 

Seriosk

Programmer;
Oct 29, 2016
256
105
I was referring to the public access modifier which will allow access to such functions outside of the assembly - which is why I said scalable. Not the default.
It would help if I knew what part of code you were referring too, each area is different.

On topic:
I have had some trouble setting up a new repository because I moved to GitLab but then my mac decided to break, and GitLab doesn't support a decent Windows client so I'll have to sort something out tomorrow now heading to bed right now, will update tomorrow. Sorry if my typing is shit right now, I am very tired.
 

JynX

Posting Freak
Feb 6, 2016
710
438
It would help if I knew what part of code you were referring too, each area is different.

On topic:
I have had some trouble setting up a new repository because I moved to GitLab but then my mac decided to break, and GitLab doesn't support a decent Windows client so I'll have to sort something out tomorrow now heading to bed right now, will update tomorrow. Sorry if my typing is shit right now, I am very tired.
GitLab supports the regular GitHub Windows Client... LOL
 

Examed

Я весь высший лидер из вас всех
Aug 7, 2014
352
95
Good luck with this, looks very nice ;):) .
 

Seriosk

Programmer;
Oct 29, 2016
256
105
GitLab supports the regular GitHub Windows Client... LOL
Don't you mean GitHub supports GitLab? Anyway, GitHub Desktop (For Windows) supports cloning repository's from other Git platforms like BitBucket, GitLab, Coding etc. It lacks the pull, push, merge functionality that you usually get with Git control. I think you're referring to SSH as an authentication method.

Anyway, should be moving it to BitBucket today as BitBucket has its own client called SourceTree.
 

JynX

Posting Freak
Feb 6, 2016
710
438
Don't you mean GitHub supports GitLab? Anyway, GitHub Desktop (For Windows) supports cloning repository's from other Git platforms like BitBucket, GitLab, Coding etc. It lacks the pull, push, merge functionality that you usually get with Git control. I think you're referring to SSH as an authentication method.

Anyway, should be moving it to BitBucket today as BitBucket has its own client called SourceTree.
It does have these commands you just need to cd the location of your git usually documents/github/RepoName and then run commands from that. You can read about the commands here:
 

Seriosk

Programmer;
Oct 29, 2016
256
105
It does have these commands you just need to cd the location of your git usually documents/github/RepoName and then run commands from that. You can read about the commands here:
You're talking about BitBucket, I'm talking about GitLab.. I suggest you read before replying with your idiotic comments on my thread.
 

JynX

Posting Freak
Feb 6, 2016
710
438
You're talking about BitBucket, I'm talking about GitLab.. I suggest you read before replying with your idiotic comments on my thread.
Not really an idiotic comment seeings as they're basic git commands lol. I suggest you actually attempt using GitHub as a client for GitLab. It all works together as I've used it and so do quite a few people when it comes to using it on the which is clearly GitLab. I'm not stating that BitBucket is worse than GitLab nor am I saying that GitLab is better than BitBucket as personally I've never used BitBucket (nor do I plan on it). I was just stating that you can use that as a windows client seeings as you said "GitLab doesn't support a decent Windows client" when clearly it does.

Edit:

PS: Seeings as you used GitHub before, and it works exactly the same for GitLab! Amazing right?
 
Last edited:

Seriosk

Programmer;
Oct 29, 2016
256
105
Your an idiot.
You're* and isn't everyone in their own special way :)?

Not really an idiotic comment seeings as they're basic git commands lol. I suggest you actually attempt using GitHub as a client for GitLab. It all works together as I've used it and so do quite a few people when it comes to using it on the which is clearly GitLab. I'm not stating that BitBucket is worse than GitLab nor am I saying that GitLab is better than BitBucket as personally I've never used BitBucket (nor do I plan on it). I was just stating that you can use that as a windows client seeings as you said "GitLab doesn't support a decent Windows client" when clearly it does.

Edit:

PS: Seeings as you used GitHub before, and it works exactly the same for GitLab! Amazing right?
Firstly, you were sending me a link to a BitBucket page when I was referring to GitLab, now that your idiotic attempt of covering that up has been exposed we shall move on to what ever else you said. Technically, GitHub Desktop Client isn't in any way associated with GitLab so it isn't a client for GitLab, merely a way of 'cheating' the system, taking advantage of the functionality of these powerful tools to do this.

If you actually looked in to it, GitHub lacks support for GitLab, yes you can CLONE repository's and a few other things associated with GitLab, but it lacks the support for shall we say what it supports for its own platform (GitHub?).
 

Seriosk

Programmer;
Oct 29, 2016
256
105
@Sledmore @Core @Mello @Damien could you please explain to him that you can use github for windows on gitlab.. :( He doesn't seem to believe me..
I have already said you can in my very first reply to you actually, I mentioned authentication via SSH keys. What I was saying is from what I have seen, GitHub Client lacks functionality that a GitLab client could bring, such as account management for GitLab, listing your repository, and more. If I am wrong about GitHub client being a solid client for GitLab then feel free to correct me, but you're accusing me of saying GitHub client doesn't work at all which is wrong, I admitted in my first reply it does.

P.S If you actually read my replies, you would know I said this.
 
Last edited:

Jaden

not so active
Aug 24, 2014
886
263
I no longer support this development, seeing this arrogance spew out of you (again) just made me remember why I avoided you in the first place. Please try to be more considerate to the people who take time to give you feedback to help you on your project.

Sorry, but good luck.
 
Status
Not open for further replies.

Users who are viewing this thread

Top