Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38]

Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
Okay so once again I have to rewrite this thread, as I did decide to focus solely on v7, since no v7 has ever been made. I have a new source than my previous version, not sure what happened to it but I cba (probably in some of my posts).

Features:
- Only basic stuff
- Camera

Keep in mind, battleball was not in this version, in fact battleball was released in v8. Also the future DCR pack will contain custom furniture files which contains the old branded items which were originally removed in this version for those who want it.

Tech stuff nobody cares about:
- Written in C#
- Written from scatch
- Using .NET Core 3.1 (multiplatform)
- Using C# 8.0
- Using Fluent NHibernate
- Using DotNetty

Some screens:

VJatqcI - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

iFnw6Nv - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

OdjsLOq - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

HxW2uH1 - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums


Snippets:

I cannot be arsed but maybe eventually I'll put up git, basically posts from and after http://forum.ragezone.com/f331/asp-net-dotnet-core-project-1157740-post9023343/#post9023343 might contain the latest code snippets.

Credits:

- Quackster for helping me out a lot with packet stuff and Lingo scripts
- Somebody else who probably knows for giving Lingo scripts way before the scripts were publicly available
- Nillus for Blunk for some packet examples which for some parts are equal to v7 (besides the protocol)
- JoeH for Thor for some packet examples
- AmyIsSweet for releasing the real v7 DCRs quite some time ago with encryption bypass
 

Attachments

You must be registered for see attachments list
Last edited:
Developer
Developer
Joined
Dec 11, 2010
Messages
2,955
Reaction score
2,685
Looks awesome! Nice to the v7 working more than just login (unlike ION) and seeing the walls load. And it looks more promising than this project which was never released.

Good luck! And yes, I won't make Kepler support v7. :wink:
 
Initiate Mage
Joined
Apr 29, 2015
Messages
91
Reaction score
6
Good Luck with this looking forward to follow this development
 
Junior Spellweaver
Joined
Jun 1, 2018
Messages
105
Reaction score
40
Nice! I hope ya actually see this through; it would be great to actually checkout v7.

PS make your packet headers const [emoji14]

 
Joined
Aug 24, 2012
Messages
603
Reaction score
300
Hello,

(Please, read the whole thread before asking questions already in the thread, thank you)

Due to stuff happening (refer to http://forum.ragezone.com/f331/php-r38-project-aurora-1132388-post8920734/#post8920734 to read the story, questions about it can be asked in a PM or Discord, NOT in this thread!) I decided to rewrite Aurora to be better. The old source was messy and in some parts I was even confused by how things were done. Also, it was using SOME dependency injection stuff but it was still terrible. This doesn't mean the old source isn't used as much code is taken from the old source, making the time spent on it not for nothing.

The functionality isn't as big as it was before, but I decided to at least make a thread as things are going alright now. This project is a bit bigger than before, so I decided to call it Aurora PRO (10 cookies for anybody getting the reference... hint hint). All jokes aside, the project contains 3 versions, based on the same base (my own base I wrote myself), a v7 (there has never been a released v7 server) and R38 (both shockwave and flash compatible since this was the original project and the main project). The R38 is the main focus but the v7 one is something extra @Quackster, don't make Kepler working on v7 thanks :( ) Okay, let's divide all the parts up:

Aurora Pro R38

Features:
- Login with SSO is working
- Catalog pages are working including items and deals (offers / packages whatever you want to call them)
- Friendlist is working (with incorrect user online check)
- Navigator frontpage is partly working (besides rooms, and categories)

CMS:
Will contain a CMS written from scratch in ASP DotNet Core (C#). Contain the style used around this time (HoloCMS/PHPRetro style). Might be multi template system and multi language system supported. No PHP cuz it's overused.

Extra information / cool stuff:
- Shockwave will have camera thanks to @Quackster for telling me how to enable it
- Shockwave might get battleball / snowstorm (maybe, just maybe...)

Screens:
YvCK2Oz - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

U7m1CPF - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

8jRmf8s - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

Snippets:
FlashSSOTicketMessageEvent (flash packet 415):
PHP:
class FlashSSOTicketMessageEvent : IPacket
    {
        public int Header => 415;

        private readonly IPlayerController _playerController;

        public FlashSSOTicketMessageEvent(IPlayerController playerController)
        {
            _playerController = playerController;
        }

        public async Task Handle(Session session, Event message)
        {
            string sso = message.GetString();

            Player player = await _playerController.GetPlayerBySSO(sso);

            if (player != null)
            {
                session.Player = player;

                session.QueueMessage(AuthenticationOKMessageComposer.Compose());
                session.Flush();
            }
            else
            {
                await session.Disconnect();
            }
        }
    }

PlayerController:
PHP:
public class PlayerController : IPlayerController
    {
        private readonly IPlayerDao _dao;
        private readonly Dictionary<int, Player> _players;

        public PlayerController(IPlayerDao dao)
        {
            _dao = dao;
            _players = new Dictionary<int, Player>();
        }

        public async Task<Player> GetPlayerById(int playerId)
        {
            if (_players.TryGetValue(playerId, out Player player))
            {
                return player;
            }

            player = await _dao.GetPlayerById(playerId);
            _players.Add(playerId, player);

            return player;
        }

        public async Task<Player> GetPlayerBySSO(string ssoTicket)
        {
            return await _dao.GetPlayerBySSO(ssoTicket);
        }
    }

PlayerDao:
PHP:
public class PlayerDao : IPlayerDao
    {
        private readonly DatabaseFactory _databaseFactory;

        public PlayerDao(DatabaseFactory databaseFactory)
        {
            _databaseFactory = databaseFactory;
        }

        public async Task<Player> GetPlayerById(int playerId)
        {
            Player player = null;

            await _databaseFactory.Select(
                "SELECT * FROM players WHERE id = [USER=1235]Player[/USER]Id LIMIT 1",
                async (reader) =>
                {
                    if (await reader.ReadAsync())
                    {
                        player = new Player(reader);
                    }
                },
                ( [USER=1235]Player[/USER]Id", playerId));

            return player;
        }

        public async Task<Player> GetPlayerBySSO(string ssoTicket)
        {
            Player player = null;

            await _databaseFactory.Select(
                "SELECT * FROM players WHERE sso_ticket = @ssoTicket LIMIT 1",
                async (reader) =>
                {
                    if (await reader.ReadAsync())
                    {
                        player = new Player(reader);
                    }
                },
                ("@ssoTicket", ssoTicket));

            return player;
        }
    }

Aurora v7

Features:
- Login is working
- Navigator nodes are working
- Own rooms list is working
- Going to private rooms is working (not 100% finished though)
- Loading items in a room is working (both floor and wall items)
- Catalog pages are working without items yet

CMS:
Won't contain a CMS, will purely be HTML with the layout used at that time. No login stuff or anything, v7 don't support SSO! (I might try to implement it if people really want to see it).

Extra information / cool stuff:
- Might be updated eventually to v9 with battleball
- Packets figured out reading the Lingo code (not going to mention names who helped me to prevent them being annoyed)
- Will contain a decently big CCT pack containing all the texts from various languages, badges and most of the room CCTs

Screens:
CeqqCQL - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

00Rn8vJ - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

lw034h7 - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums

Krh6Xbu - [C#/ASP .NET/DotNet Core] Project Aurora Pro [v7/R38] - RaGEZONE Forums
(I use the Dutch external texts; the emulator itself and database is English! Catalog is made by myself as close to Habbo's one was in terms of texts etc.)

Snippets:
None for now, no proirity. Most code besides packets is similar to R38 ones.

So, the main focus is on the R38 project. I might make a git repository soon, just not sure. Any other questions feel free to ask, ideas: feel free to ask (even if this means editing game files) and code tips feel free to point out.

Again: this is a rewrite which still uses lots of code from the old source, just done in a better way and slightly more clean.

dude, i wish you the best. finish this pls, dont keep opening projects and let them die
 

Attachments

You must be registered for see attachments list
Retired
Member
Joined
May 5, 2007
Messages
497
Reaction score
665
What database framework are you using? Imo this looks like an really bloaty and awful API just to fetch a user object from the database. I suggest using something like Dapper ( ), it would make your code a lot cleaner:

Code:
[COLOR=#000000][COLOR=#0000ff]public[/COLOR] [COLOR=#0000ff]class[/COLOR] [COLOR=#267f99]PlayerDao[/COLOR] : [COLOR=#267f99]IPlayerDao[/COLOR]
{
    [COLOR=#0000ff]private[/COLOR] [COLOR=#0000ff]readonly[/COLOR] [COLOR=#267f99]IDatabaseFactory[/COLOR] [COLOR=#001080]_dbFactory[/COLOR];

    [COLOR=#0000ff]public[/COLOR] [COLOR=#795e26]PlayerDao[/COLOR]([COLOR=#267f99]IDatabaseFactory[/COLOR] [COLOR=#001080]dbFactory[/COLOR])
    {
        [COLOR=#001080]_dbFactory[/COLOR] = [COLOR=#001080]dbFactory[/COLOR];
    }

    [COLOR=#0000ff]public[/COLOR] [COLOR=#0000ff]async[/COLOR] [COLOR=#267f99]Task[/COLOR]<[COLOR=#267f99]Player[/COLOR]> [COLOR=#795e26]GetPlayerById[/COLOR]([COLOR=#0000ff]int[/COLOR] [COLOR=#001080]playerId[/COLOR])
    {
        [COLOR=#0000ff]using[/COLOR] ([COLOR=#0000ff]var[/COLOR] [COLOR=#001080]connection[/COLOR] = [COLOR=#0000ff]await[/COLOR] [COLOR=#001080]_dbFactory[/COLOR].[COLOR=#795e26]GetOpenConnection[/COLOR]())
        {
            [COLOR=#af00db]return[/COLOR] [COLOR=#0000ff]await[/COLOR] [COLOR=#001080]connection[/COLOR].[COLOR=#795e26]QueryAsync[/COLOR]<[COLOR=#267f99]Player[/COLOR]>(
                    [COLOR=#a31515]"SELECT * FROM players WHERE id = [USER=1235]Player[/USER]Id"[/COLOR],
                    [COLOR=#0000ff]new[/COLOR] {[COLOR=#001080]playerId[/COLOR] = [COLOR=#001080]playerId[/COLOR]});
        }
    }

    [COLOR=#0000ff]public[/COLOR] [COLOR=#0000ff]async[/COLOR] [COLOR=#267f99]Task[/COLOR]<[COLOR=#267f99]Player[/COLOR]> [COLOR=#795e26]GetPlayerBySSO[/COLOR]([COLOR=#0000ff]string[/COLOR] [COLOR=#001080]ssoTicket[/COLOR])
    {
        [COLOR=#0000ff]using[/COLOR] ([COLOR=#0000ff]var[/COLOR] [COLOR=#001080]connection[/COLOR] = [COLOR=#0000ff]await[/COLOR] [COLOR=#001080]_dbFactory[/COLOR].[COLOR=#795e26]GetOpenConnection[/COLOR]())
        {
            [COLOR=#af00db]return[/COLOR] [COLOR=#0000ff]await[/COLOR] [COLOR=#001080]connection[/COLOR].[COLOR=#795e26]QueryAsync[/COLOR]<[COLOR=#267f99]Player[/COLOR]>(
                    [COLOR=#a31515]"SELECT * FROM players WHERE sso_ticket = @ssoTicket"[/COLOR],
                    [COLOR=#0000ff]new[/COLOR] {[COLOR=#001080]ssoTicket[/COLOR] = [COLOR=#001080]ssoTicket[/COLOR]});
        }
    }
}
[/COLOR]
 
Last edited:
Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
What database framework are you using? Imo this looks like an really bloaty and awful API just to fetch a user object from the database. I suggest using something like Dapper ( ), it would make your code a lot cleaner:

Code:
... not gonna quote code

I wasn't using any real database framework but I'll definitely take a look at Dapper or something else. Thanks for the suggestion!
 
Joined
Jun 23, 2010
Messages
2,318
Reaction score
2,195
Removed the overload of async/await operations to speed up the emulator massively. This evening I will try to finish most of the Flash navigator maybe start on room entry.

I'd like to know how the "overload" of async/await operations can decrease the speed "massively". Could you explain it?

Also, if you're working with async methods. Name them accordingly, like: GetPlayerByIdAsync etc.
 
Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
I'd like to know how the "overload" of async/await operations can decrease the speed "massively". Could you explain it?

Also, if you're working with async methods. Name them accordingly, like: GetPlayerByIdAsync etc.

I didn't know what I wrote there. To be completely fair, I think I had some bad code somewhere and I thought it was the async methods doing it (just os you know, I think (almost) every method was async before), but I'm not sure if I've changed any other code. I agree with your second point, I just partly didn't care since most of my methods were async anyways so the '..Async' would be obsolete (no, I don't see this as an excuse and I agree with your point; just saying I've been too lazy to add the Async because of that reason).
 
Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
Care to give me some clear examples why you would want everything async...?

To be quite honest I have no idea why I did; I know that this isn't a good reason, again I took most of the code from my old source and changed parts of it but I don't know how I came up with it in my old source. Again, I'm changing stuff when I'm free (but the flu hit me and I've been sick for over a week now).
 
Joined
Jun 23, 2010
Messages
2,318
Reaction score
2,195
Care to give me some clear examples why you would want everything async...?

You mostly use it for IO operation, or anything where you do a call and have to wait for a response. When you use async/await it allows you to do other stuff in the meantime. Keep in mind this isn't threading! Everything in the async-chain runs in the same thread, unless you specifically tell it to run in another thread. For example with (in C#) Task.Run. In order to make full advantage of this it's common to do async all the way. Mixing up sync and async methods without care can cause unexpected behaviour. For example deadlocks. If you want to know more, here is a nice article:
 
Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
Oof, it's been a while. Worrysome! I can't / don't want to go in detail, but the project was on hold. Regardless, I wasn't happy with how I did some stuff and also I wasn't sure what way I wanted to go towards with this project. With all respect, v7 is a great version, but I don't see a reason to make something that is kind of useless and hard to run. Besides that, it's a great version but lacking of functionality. A v14 will give people way more nostalgia, but has much more functionality. Kepler is a great emulator for oldskool versions, so I decided to drop development for any old Shockwave versions.

Now, with the recent catastrophe of Arcturus being quit, I felt like it would be a good idea to work on something R63 post-shuffle related. I've seen both Plus and Arcturus and while they're both great, they don't do stuff `100% the right way. Considering this is the version most people want nowadays, it'd be a good idea to work on that (as well...?). Then again, there's R38, where my development originally started because no hotel ever had Shockwave and Flash working (but now thanks to Quackster we do). My interest lays in between. Both versions are amazing. R38 looks great, is smooth and plays well while R63 post-shuffle has a lot of cool functionality, for example games; better achievements; bundles; badges buyable in shop; calendar; talent tracker etc.

What I'm going to do I'm not sure of, but keep in mind v7 will MOST LIKELY be dropped. For now, I've been busy with a bit of cleanup of the code and to do things differently, like the dependency injection which is done 100% automatic right now (it still has to be tested). The server also will contain DotNetty as it's much easier and faster to work with (I know some people despise using libraries... but they aren't made for nothing after all).



This is my custom DependencyRegistrat class, which automatically finds classes with either the Singleton or Transient attribute and adds them to the ServiceCollection:

PHP:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Reflection;

namespace Nordlys.DependencyInjection
{
    public class DependencyRegistrar
    {
        private readonly IServiceCollection serviceDescriptors;

        public ServiceProvider ServiceProvider 
        {
            get;
        }

        public DependencyRegistrar()
        {
            serviceDescriptors = new ServiceCollection();
            serviceDescriptors.AddLogging(builder => builder.AddConsole());

            AddClasses();

            ServiceProvider = serviceDescriptors.BuildServiceProvider();
        }

        private void AddClasses()
        {
            foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
            {
                if (type.GetCustomAttributes(typeof(SingletonAttribute), true).Length > 0)
                {
                    serviceDescriptors.AddSingleton(type);
                }
                else if (type.GetCustomAttributes(typeof(TransientAttribute), true).Length > 0)
                {
                    serviceDescriptors.AddTransient(type);
                }
            }
        }

        public T Resolve<T>()
        {
            return ServiceProvider.GetService<T>();
        }
    }
}

A class to be added to the ServiceCollection looks like:

PHP:
[Singleton]
    public class SessionFactory
    {
        private int sessionIdCounter;

        public SessionFactory()
        {
            sessionIdCounter = 0;
        }

        public Session CreateSession(IChannel channel)
        {
            return new Session(channel, Interlocked.Increment(ref sessionIdCounter));
        }
    }

Now, regarding the version, let me know what you suggest. Also if anybody finds something weird in my code let me know, I probably did some things completely wrong (wouldn't be surprised if I did to be honest...)
I'll try to keep things updated in here the upcoming days, might make a git repository soon (if I'm not too lazy to do so) and continue whatever way the wind blows me.

(I was planning on continuing this, I wasn't sure if the person I would "team up with" would have the time to work on it so I made a start).

Also the new name is Nordlys (I give $5 to the first person who knows how I came up with that). No screens yet so enjoy some more snippets:

PHP:
[Singleton]
    public class GameNetworkListener
    {
        private readonly ILogger<GameNetworkListener> logger;
        private readonly ILogger<GameNetworkHandler> handlerLogger;
        private readonly SessionFactory sessionFactory;
        private readonly SessionController sessionController;

        private ServerBootstrap bootstrap;
        private IEventLoopGroup bossGroup;
        private IEventLoopGroup workerGroup;

        public GameNetworkListener(ILogger<GameNetworkListener> logger, ILogger<GameNetworkHandler> handlerLogger, SessionFactory sessionFactory, SessionController sessionController)
        {
            this.logger = logger;
            this.handlerLogger = handlerLogger;
            this.sessionFactory = sessionFactory;
            this.sessionController = sessionController;
        }

        public async Task Start(int port)
        {
            bossGroup = new MultithreadEventLoopGroup(1);
            workerGroup = new MultithreadEventLoopGroup(10);

            bootstrap = new ServerBootstrap();
            bootstrap
                .Group(bossGroup, workerGroup)
                .Channel<TcpServerSocketChannel>()
                .ChildHandler(new ActionChannelInitializer<IChannel>(channel => {
                    channel.Pipeline.AddLast("decoder", new Decoder());
                    channel.Pipeline.AddLast("encoder", new Encoder());
                    channel.Pipeline.AddLast("handler", new GameNetworkHandler(handlerLogger, sessionFactory, sessionController));
                }))
                .ChildOption(ChannelOption.TcpNodelay, true)
                    .ChildOption(ChannelOption.SoKeepalive, true)
                    .ChildOption(ChannelOption.SoReuseaddr, true)
                    .ChildOption(ChannelOption.SoRcvbuf, 1024)
                    .ChildOption(ChannelOption.RcvbufAllocator, new FixedRecvByteBufAllocator(1024))
                    .ChildOption(ChannelOption.Allocator, UnpooledByteBufferAllocator.Default);
            await bootstrap.BindAsync(port);
            logger.LogInformation("Bound GameNetworkListener on port {0}, ready for connections", port);
        }

        public void Stop()
        {
            try
            {
                workerGroup.ShutdownGracefullyAsync().RunSynchronously();
                bossGroup.ShutdownGracefullyAsync().RunSynchronously();
            }
            catch (ObjectDisposedException e)
            {
                Console.WriteLine(e);
            }
        }
    }

(I had to use new GameNetworkHandler here as for some reason I can't add the same instance for multiple Channels... sad story)

PHP:
public class GameNetworkHandler : SimpleChannelInboundHandler<ClientMessage>
    {
        private readonly ILogger<GameNetworkHandler> logger;
        private readonly SessionFactory sessionFactory;
        private readonly SessionController sessionController;

        public GameNetworkHandler(ILogger<GameNetworkHandler> logger, SessionFactory sessionFactory, SessionController sessionController)
        {
            this.logger = logger;
            this.sessionFactory = sessionFactory;
            this.sessionController = sessionController;
        }

        public override void ChannelActive(IChannelHandlerContext context)
        {
            Session session = sessionFactory.CreateSession(context.Channel);
            sessionController.AddSession(session);

            logger.LogInformation("New connection from {0}", session.Channel.RemoteAddress);
        }

        public override void ChannelInactive(IChannelHandlerContext context)
        {
            sessionController.RemoveSession(context.Channel);

            logger.LogInformation("New connection from {0}", context.Channel.RemoteAddress);
        }

        protected override void ChannelRead0(IChannelHandlerContext ctx, ClientMessage msg)
        {
            logger.LogInformation("Unregistered header {0}", msg.Id);
        }

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            throw exception;
        }
    }

PHP:
public static class Nordlys
    {
        static void Main(string[] args)
        {
            DependencyRegistrar dependencyRegistrar = new DependencyRegistrar();

            GameNetworkListener listener = dependencyRegistrar.Resolve<GameNetworkListener>();

            listener.Start(30000).Wait();

            while (true) { }
        }
    }

Keep in mind it's still work in progress and nothing has been transferred over to the new source in terms of packets. But it should be a fast process nonetheless to port things over.
 
Joined
Jun 23, 2010
Messages
2,318
Reaction score
2,195
Good to see you picked up the development again! I suggest you to work on the version YOU like. It shouldn't matter if there's already an good emulator for version X. But that's just my opinion.

Now back on your code I do have a few points that should/could be improved.
- You have an asynchronous method in your GameNetworkListener that's no Async postfix. All async methods should've Async as a postfix (there are a few exceptions like events, HTTP controller methods and tests). I also suggest you to add ".ConfigureAwait(false)" on all async-calls too. Here is a nice article about it: .
- In your GameNetworkListener the Start method is async, but your Stop method isn't, why? You do call two async methods in it...
- The main method in Nordlys class runs the Start method, but then waits for it? Why? What's the reason to use async if you're going to block it anyway? If you do async, go async all the way. Tip: the main method can be marked as async too, then just do "await listener.Start'Async'(30000).ConfigureAwait(false);".
- Try to abstract away the use of new. It will make your life easier when unit testing (if you actually do such thing).
- There is a nice extension package that Microsoft provides for configuration purposes, see here: .
- Minor improvement, you can use ".Any()" on arrays (everything that implements IEnumerable) to validate if it has items.
- You catch an exception but are still using "Console.Writeline" to print it on the console. Why not use your logger that you used a few lines above it?
- Isn't it strange you have to give a single class two logger instances? I know the reason, but it weird.
- On the subject of constructor parameters. Having more than 3-4+ dependencies generaly means it does and needs too much. Make the class smaller.

Please read up on task-based async methods and how they work. It'll avoid problems, like deadlocks and other strange behavior, in the late run. Keep also in mind that debugging will and can be a pain in the butt when using async methods, because of jumping from one to the other place.
 
Joined
Oct 26, 2012
Messages
2,357
Reaction score
1,086
Creating dependency injection scope settings with meta-programming is not a great idea.

I also believe you shouldn't handle Header IDs like that, which takes away your power to handle data in a multi-header scenario.

Can you explain a bit more on why I shouldn't? And about the header IDs I don't quite get the problem to be fair. The decoder decodes incoming data, gets an int (the length of the packet), reads X amount of bytes from the data and uses that X amount of bytes in the ClientMessage. The ClientMessage always should have the correct data and in theory - however like in my old server it isn't needed - I could do a while so no data would be lost if two packets end up getting sent together.

Good to see you picked up the development again! I suggest you to work on the version YOU like. It shouldn't matter if there's already an good emulator for version X. But that's just my opinion.

Now back on your code I do have a few points that should/could be improved.
- You have an asynchronous method in your GameNetworkListener that's no Async postfix. All async methods should've Async as a postfix (there are a few exceptions like events, HTTP controller methods and tests). I also suggest you to add ".ConfigureAwait(false)" on all async-calls too. Here is a nice article about it: .
- In your GameNetworkListener the Start method is async, but your Stop method isn't, why? You do call two async methods in it...
- The main method in Nordlys class runs the Start method, but then waits for it? Why? What's the reason to use async if you're going to block it anyway? If you do async, go async all the way. Tip: the main method can be marked as async too, then just do "await listener.Start'Async'(30000).ConfigureAwait(false);".
- Try to abstract away the use of new. It will make your life easier when unit testing (if you actually do such thing).
- There is a nice extension package that Microsoft provides for configuration purposes, see here: .
- Minor improvement, you can use ".Any()" on arrays (everything that implements IEnumerable) to validate if it has items.
- You catch an exception but are still using "Console.Writeline" to print it on the console. Why not use your logger that you used a few lines above it?
- Isn't it strange you have to give a single class two logger instances? I know the reason, but it weird.
- On the subject of constructor parameters. Having more than 3-4+ dependencies generaly means it does and needs too much. Make the class smaller.

Please read up on task-based async methods and how they work. It'll avoid problems, like deadlocks and other strange behavior, in the late run. Keep also in mind that debugging will and can be a pain in the butt when using async methods, because of jumping from one to the other place.



Yes, true, but that's the thing; I don't have a preference. I'll see what I'll do. I read your stuff and I'm like facepalming myself (don't worry, not because it made no sense, but because I did this in the past). I was having some issues with my server and end up changing a lot because I thought I did some stuff wrong (when in fact it was me using a wrong client... dumb me). I'll definitely go and change the async stuff back. Anyways to reply on your points (a number increase for every dash you used):

1. I'll look into it and change the async stuff
2. I'll change it
3. I'll change it
4. How do you mean? You mean replacing all "new ..." instances? I know with the "handler" pipeline it can't be done for some weird reason (trust me, ask DotNetty...) as I tried doing it with DI before.
5. I'll read into it but as for now (as I'm only using it locally for now) it's not a huge priority, still interesting and I'll definitely look into it
6. I always thought LINQ would slow things down (not sure how much it matters nowadays though), of course that can be benchmarked but that's the reason I try to limit my LINQ usage.
7. Oops. Yes, I think this was before I had a logger in GameNetworkHandler, and I forgot to replace it. I thought my client didn't work and added it solely to see if I got an error (which... I didn't and I was just using the wrong client)
8. I understand, I realize I could've done it better but my head didn't think of that before. I'll definitely see if there's a better way and what the best way would be.
9. Honestly it was me wanting to split the controller and factory (for whatever reason) and my head thinking I had to inject them both in GameNetworkHandler (GameNetworkListener to pass it through)

Again I'll look into the stuff you sent and make it more async. Thanks for the suggestions!
 
Back
Top