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

Page 2 of 4 FirstFirst 1234 LastLast
Results 16 to 30 of 51
  1. #16
    Live Ocottish Sverlord Joopie is online now
    LegendRank
    Jun 2010 Join Date
    The NetherlandsLocation
    2,767Posts

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

    Quote Originally Posted by The General View Post
    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: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

  2. #17
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    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 Code:
    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 Code:
    [Singleton]
        public class 
    SessionFactory
        
    {
            private 
    int sessionIdCounter;

            public 
    SessionFactory()
            {
                
    sessionIdCounter 0;
            }

            public 
    Session CreateSession(IChannel channel)
            {
                return new 
    Session(channelInterlocked.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 Code:
    [Singleton]
        public class 
    GameNetworkListener
        
    {
            private 
    readonly ILogger<GameNetworkListenerlogger;
            private 
    readonly ILogger<GameNetworkHandlerhandlerLogger;
            private 
    readonly SessionFactory sessionFactory;
            private 
    readonly SessionController sessionController;

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

            public 
    GameNetworkListener(ILogger<GameNetworkListenerloggerILogger<GameNetworkHandlerhandlerLoggerSessionFactory sessionFactorySessionController 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(bossGroupworkerGroup)
                    .
    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(handlerLoggersessionFactorysessionController));
                    }))
                    .
    ChildOption(ChannelOption.TcpNodelaytrue)
                        .
    ChildOption(ChannelOption.SoKeepalivetrue)
                        .
    ChildOption(ChannelOption.SoReuseaddrtrue)
                        .
    ChildOption(ChannelOption.SoRcvbuf1024)
                        .
    ChildOption(ChannelOption.RcvbufAllocator, new FixedRecvByteBufAllocator(1024))
                        .
    ChildOption(ChannelOption.AllocatorUnpooledByteBufferAllocator.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 Code:
    public class GameNetworkHandler SimpleChannelInboundHandler<ClientMessage>
        {
            private 
    readonly ILogger<GameNetworkHandlerlogger;
            private 
    readonly SessionFactory sessionFactory;
            private 
    readonly SessionController sessionController;

            public 
    GameNetworkHandler(ILogger<GameNetworkHandlerloggerSessionFactory sessionFactorySessionController 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 ctxClientMessage msg)
            {
                
    logger.LogInformation("Unregistered header {0}"msg.Id);
            }

            public 
    override void ExceptionCaught(IChannelHandlerContext contextException exception)
            {
                throw 
    exception;
            }
        } 
    PHP Code:
    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.

  3. #18
    Evil Italian Overlowrd Droppy is offline
    [Internal Coder]Rank
    Feb 2012 Join Date
    /home/droppyLocation
    2,047Posts

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

    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.

  4. #19
    Live Ocottish Sverlord Joopie is online now
    LegendRank
    Jun 2010 Join Date
    The NetherlandsLocation
    2,767Posts

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

    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: https://docs.microsoft.com/en-us/arc...us-programming.
    - 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: https://docs.microsoft.com/en-us/asp...ration/options.
    - 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 ass when using async methods, because of jumping from one to the other place.

  5. #20
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    Quote Originally Posted by Droppy View Post
    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.

    Quote Originally Posted by Joopie View Post
    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: https://docs.microsoft.com/en-us/arc...us-programming.
    - 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: https://docs.microsoft.com/en-us/asp...ration/options.
    - 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 ass 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!

  6. #21
    Live Ocottish Sverlord Joopie is online now
    LegendRank
    Jun 2010 Join Date
    The NetherlandsLocation
    2,767Posts

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

    Quote Originally Posted by Emily View Post
    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.
    I'm sure you can do it with DI. Try something like this:

    Code:
    public class ActionChannelInitializerFactory {
        private readonly IServiceProvider _serviceProvider;
    
        public ActionChannelInitializerFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public ActionChannelInitializer<IChannel> Create()
        {
            return new ActionChannelInitializer<IChannel>(channel => {
                        channel.Pipeline.AddLast("decoder", _serviceProvider.GetRequiredService<Decoder>();
                        channel.Pipeline.AddLast("encoder", _serviceProvider.GetRequiredService<Encoder>());
                        channel.Pipeline.AddLast("handler", _serviceProvider.GetRequiredService<GameNetworkHandler>());
                    });
        }
    }
    
    ...
    
    public class GameNetworkListener
    {
        private readonly ActionChannelInitializerFactory _actionChannelInitializerFactory;
    
        ...
    
        public GameNetworkListener(ActionChannelInitializerFactory actionChannelInitializerFactory, ...)
        {
            _actionChannelInitializerFactory = actionChannelInitializerFactory;
    
            ...
        }
    
        ...
    
            bootstrap
                .ChildHandler(_actionChannelInitializerFactory.Create())
    
        ...
    }
    Quote Originally Posted by Emily View Post
    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.
    LINQ itself is fine. They're just a bunch of extension methods. The .Where(predicate) can be compared to something like this:

    Code:
    public static IEnumerable<T> Where<T>(this IEnumerable<T> items, Func<T, bool> predicate)
    {
        foreach (var item in items)
        {
            if (predicate(item))
            {
                yield return item;
            }
        }
    }
    You should however becareful on how you use it. An IEnumerable computes it's results when it's needed. For example if you do something like:

    Code:
    var filteredItems = items.Where(x => x.type == "foe");
    And you would have used filteredItems, like the following:

    Code:
    if (filteredItems.Any())
    {
        SomeOtherMethod(filteredItems);
    }
    The .Any() method, and everything inside the SomeOtherMethod that accesses the IEnumerable would cause a loop and executes the predicate given in the Where. You can avoid this by using .ToList(), .ToArray() or any method that returns a concreet result.
    The same applies to IQuerable. You should never return that outside your DAO/Repository/w.e. you use to access your database.

    That's just one pitfall you should be aware off. There are a few more, like using First/Single for fetching a single record by an id in a for loop.

  7. #22
    Evil Italian Overlowrd Droppy is offline
    [Internal Coder]Rank
    Feb 2012 Join Date
    /home/droppyLocation
    2,047Posts

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

    Quote Originally Posted by Emily View Post
    Can you explain a bit more on why I shouldn't?
    Decreses the scope for testing whenever necessary. If you need to test, for instance, GameNetworkListener out of a singleton DI scope, you'd need to remove the attribute. But then, you could modularise it and control it however you want from a separate module, giving you more flexibility over your code. You want to be able to modify its behavior, extend, or whatever, and using Meta-Programming isn't the way forward IMO

    Quote Originally Posted by Emily View Post
    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.
    I know how they work, but I don't think messages should be coupled with the message itself. I know you're working on older versions thus the header IDs are (almost) the same, but give it some extensibility for N-release incoming/outgoing, which you can't do if it's tightly coupled with the message itself. If you're working with a two-version-header scenario, you'd need to create a mechanism that can deliver from N-version to N-version.

  8. #23
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    Quote Originally Posted by Joopie View Post
    I'm sure you can do it with DI. Try something like this:

    Code:
    public class ActionChannelInitializerFactory {
        private readonly IServiceProvider _serviceProvider;
    
        public ActionChannelInitializerFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public ActionChannelInitializer<IChannel> Create()
        {
            return new ActionChannelInitializer<IChannel>(channel => {
                        channel.Pipeline.AddLast("decoder", _serviceProvider.GetRequiredService<Decoder>();
                        channel.Pipeline.AddLast("encoder", _serviceProvider.GetRequiredService<Encoder>());
                        channel.Pipeline.AddLast("handler", _serviceProvider.GetRequiredService<GameNetworkHandler>());
                    });
        }
    }
    
    ...
    
    public class GameNetworkListener
    {
        private readonly ActionChannelInitializerFactory _actionChannelInitializerFactory;
    
        ...
    
        public GameNetworkListener(ActionChannelInitializerFactory actionChannelInitializerFactory, ...)
        {
            _actionChannelInitializerFactory = actionChannelInitializerFactory;
    
            ...
        }
    
        ...
    
            bootstrap
                .ChildHandler(_actionChannelInitializerFactory.Create())
    
        ...
    }


    LINQ itself is fine. They're just a bunch of extension methods. The .Where(predicate) can be compared to something like this:

    Code:
    public static IEnumerable<T> Where<T>(this IEnumerable<T> items, Func<T, bool> predicate)
    {
        foreach (var item in items)
        {
            if (predicate(item))
            {
                yield return item;
            }
        }
    }
    You should however becareful on how you use it. An IEnumerable computes it's results when it's needed. For example if you do something like:

    Code:
    var filteredItems = items.Where(x => x.type == "foe");
    And you would have used filteredItems, like the following:

    Code:
    if (filteredItems.Any())
    {
        SomeOtherMethod(filteredItems);
    }
    The .Any() method, and everything inside the SomeOtherMethod that accesses the IEnumerable would cause a loop and executes the predicate given in the Where. You can avoid this by using .ToList(), .ToArray() or any method that returns a concreet result.
    The same applies to IQuerable. You should never return that outside your DAO/Repository/w.e. you use to access your database.

    That's just one pitfall you should be aware off. There are a few more, like using First/Single for fetching a single record by an id in a for loop.
    Oh thanks for the reply. I can certainly see what you mean. I'll look into my code and change where it's possible (I should have more today). As soon as it's done I'll make sure to put it in git so you'd see how my changes are. My focus now is to take over all points you've mentioned so far and hopefully make the code even better. A huge thanks for helping me out and giving me tips.

    Quote Originally Posted by Droppy View Post
    Decreses the scope for testing whenever necessary. If you need to test, for instance, GameNetworkListener out of a singleton DI scope, you'd need to remove the attribute. But then, you could modularise it and control it however you want from a separate module, giving you more flexibility over your code. You want to be able to modify its behavior, extend, or whatever, and using Meta-Programming isn't the way forward IMO



    I know how they work, but I don't think messages should be coupled with the message itself. I know you're working on older versions thus the header IDs are (almost) the same, but give it some extensibility for N-release incoming/outgoing, which you can't do if it's tightly coupled with the message itself. If you're working with a two-version-header scenario, you'd need to create a mechanism that can deliver from N-version to N-version.
    Hm, I can always change it back to adding it manually. I just thought - and yes I admit that this is a stupid reason maybe - cause I'm used to things like Symfony and Laravel and I don't have to add my own classes in order to inject them. It's not a terrible bad thing to do it manually.
    As for the headers, do you have any suggestion how to do it better? I can look into that later then.

  9. #24
    Evil Italian Overlowrd Droppy is offline
    [Internal Coder]Rank
    Feb 2012 Join Date
    /home/droppyLocation
    2,047Posts

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

    Quote Originally Posted by Emily View Post
    Hm, I can always change it back to adding it manually. I just thought - and yes I admit that this is a stupid reason maybe - cause I'm used to things like Symfony and Laravel and I don't have to add my own classes in order to inject them. It's not a terrible bad thing to do it manually.
    As for the headers, do you have any suggestion how to do it better? I can look into that later then.
    Code:
    using System;
    using System.Collections.Generic;
    
    namespace Example
    {
        public interface IServerHeaders
        {
            ushort SayHello { get; }
        }
    
        public interface IServerVersion
        {
            IServerHeaders ServerHeaders { get; }
        }
    
        public interface IHeader
        {
            ushort HeaderId { get; set; }
        }
    
        public class R63ServerHeaders : IServerHeaders
        {
            public ushort SayHello => 1;
        }
    
        public class R63ServerVersion : IServerVersion
        {
            public IServerHeaders ServerHeaders => new R63ServerHeaders();
        }
        
        public interface IServerMessage : IDisposable
        {
            IServerMessage WriteByte(byte b);
            byte[] GetData();
        }
    
        public class ServerMessage : IServerMessage
        {
            private readonly List<byte> _rawBytes = new List<byte>(); // temp.
            
            // all your servermessage logic
            public IServerMessage WriteByte(byte b)
            {
                _rawBytes.Add(b);
                return this;
            }
    
            public byte[] GetData() => _rawBytes.ToArray();
            public void Dispose()
            {
            }
        }
    
        public class User
        {
            public IServerVersion Version { get; set; }
    
            public void SendMessage(int headerId, IServerMessage message)
            {
                // sub-routine to send message to user TCP socket.
                // calls sub-routine to merge packet received with data, example below
                byte[] header = BitConverter.GetBytes(headerId);
                Array.Reverse(header);
                byte[] body = message.GetData();
                
                byte[] packet = new byte[header.Length + body.Length];
                Array.Copy(header, packet, header.Length);
                Array.Copy(body, 0, packet, header.Length, body.Length);
                Console.WriteLine("Sending message...");
            }
        }
        
        public class Room
        {
            public IEnumerable<User> Guests = new List<User>();
        }
        public class RandomMessage
        {
            public void SomeMessage(Room rooms)
            {
                using (IServerMessage serverMessage = new ServerMessage())
                {
                    serverMessage.WriteByte(65)
                        .WriteByte(66)
                        .WriteByte(67);
                    
                    foreach (var guest in rooms.Guests)
                    {
                        guest.SendMessage(guest.Version.ServerHeaders.SayHello, serverMessage);
                    }
                }
                
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                User user = new User()
                {
                    Version = new R63ServerVersion()
                };
                
                Room room = new Room()
                {
                    Guests = new []
                    {
                        user
                    }
                };
                
                RandomMessage message = new RandomMessage();
                message.SomeMessage(room);
            }
        }
    }
    Wrote quickly, anyway, you could have a look at that to get some idea of what I meant.

    My example wasn't meant to be optimized nor beautiful, rather to just prove a point.

    Any user will be able to have any version, and still receive their message. You should also improve the code to make them to be able to handle the message themselves (by the version, and not by the header), this way, R63B (or whatever it's called nowadays) can have its own version handler, same as any other versions.

  10. #25
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    Quote Originally Posted by Droppy View Post
    Code:
    using System;
    using System.Collections.Generic;
    
    namespace Example
    {
        public interface IServerHeaders
        {
            ushort SayHello { get; }
        }
    
        public interface IServerVersion
        {
            IServerHeaders ServerHeaders { get; }
        }
    
        public interface IHeader
        {
            ushort HeaderId { get; set; }
        }
    
        public class R63ServerHeaders : IServerHeaders
        {
            public ushort SayHello => 1;
        }
    
        public class R63ServerVersion : IServerVersion
        {
            public IServerHeaders ServerHeaders => new R63ServerHeaders();
        }
        
        public interface IServerMessage : IDisposable
        {
            IServerMessage WriteByte(byte b);
            byte[] GetData();
        }
    
        public class ServerMessage : IServerMessage
        {
            private readonly List<byte> _rawBytes = new List<byte>(); // temp.
            
            // all your servermessage logic
            public IServerMessage WriteByte(byte b)
            {
                _rawBytes.Add(b);
                return this;
            }
    
            public byte[] GetData() => _rawBytes.ToArray();
            public void Dispose()
            {
            }
        }
    
        public class User
        {
            public IServerVersion Version { get; set; }
    
            public void SendMessage(int headerId, IServerMessage message)
            {
                // sub-routine to send message to user TCP socket.
                // calls sub-routine to merge packet received with data, example below
                byte[] header = BitConverter.GetBytes(headerId);
                Array.Reverse(header);
                byte[] body = message.GetData();
                
                byte[] packet = new byte[header.Length + body.Length];
                Array.Copy(header, packet, header.Length);
                Array.Copy(body, 0, packet, header.Length, body.Length);
                Console.WriteLine("Sending message...");
            }
        }
        
        public class Room
        {
            public IEnumerable<User> Guests = new List<User>();
        }
        public class RandomMessage
        {
            public void SomeMessage(Room rooms)
            {
                using (IServerMessage serverMessage = new ServerMessage())
                {
                    serverMessage.WriteByte(65)
                        .WriteByte(66)
                        .WriteByte(67);
                    
                    foreach (var guest in rooms.Guests)
                    {
                        guest.SendMessage(guest.Version.ServerHeaders.SayHello, serverMessage);
                    }
                }
                
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                User user = new User()
                {
                    Version = new R63ServerVersion()
                };
                
                Room room = new Room()
                {
                    Guests = new []
                    {
                        user
                    }
                };
                
                RandomMessage message = new RandomMessage();
                message.SomeMessage(room);
            }
        }
    }
    Wrote quickly, anyway, you could have a look at that to get some idea of what I meant.

    My example wasn't meant to be optimized nor beautiful, rather to just prove a point.

    Any user will be able to have any version, and still receive their message. You should also improve the code to make them to be able to handle the message themselves (by the version, and not by the header), this way, R63B (or whatever it's called nowadays) can have its own version handler, same as any other versions.
    This indeed will work. However, if structures changed this won't work, and if features doesn't exist it will - from what I think in my head - cause errors. I know maybe a way to do it but I'm unsure how. I almost think like in this case having like a MessageHandler for every version would be better. If structure/features don't change I don't see a reason to support multiple revisions either. I get your point but I'm not sure whether it's worth it.

    This of course also is only the case for post-shuffle. If I want to support R54 and R38 I would already need to do it way different due to those structures being changed a lot.

  11. #26
    Evil Italian Overlowrd Droppy is offline
    [Internal Coder]Rank
    Feb 2012 Join Date
    /home/droppyLocation
    2,047Posts

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

    Quote Originally Posted by Emily View Post
    This indeed will work. However, if structures changed this won't work, and if features doesn't exist it will - from what I think in my head - cause errors. I know maybe a way to do it but I'm unsure how. I almost think like in this case having like a MessageHandler for every version would be better. If structure/features don't change I don't see a reason to support multiple revisions either. I get your point but I'm not sure whether it's worth it.

    This of course also is only the case for post-shuffle. If I want to support R54 and R38 I would already need to do it way different due to those structures being changed a lot.
    They usually don't, not for most packets anyway, and when they do that's when people usually make an API deprecated. I'm not saying you should follow that by the rule, but having a gateway to parse your income then translate to an API that makes all the version handling could be done to make it multi version.

    And yes, I know which versions you are targetting. I just don't think that hardcoding messages like this is a great idea.

  12. #27
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    Quote Originally Posted by Droppy View Post
    They usually don't, not for most packets anyway, and when they do that's when people usually make an API deprecated. I'm not saying you should follow that by the rule, but having a gateway to parse your income then translate to an API that makes all the version handling could be done to make it multi version.

    And yes, I know which versions you are targetting. I just don't think that hardcoding messages like this is a great idea.
    I know usually they don't and for most packet they wont. But if people want to use a completely different version it would be mostly because it has a special feature. If everything is the same but the release I don't see a point for other people (as I'm not going to focus on multiple revisions in one server) to update packets to a newer SWF version without any benefit.

    I can look in what you sent me and indeed if you plan on supporting multiple revisions, I can understand, but such plans aren't planned and won't happen, as I think supporting one version is good enough. In both cases:

    R63 post shuffle - I can pick the latest. I can always make it so every event and every response is a class in a specific namespace and based on your release string it will use the right packet handler so in case Habbo gets new updates with new features which they wont people can update it as they like or support two versions. Retros only use one client so it's not a big issue.

    R38 - Since it would be both Shockwave and Flash (cause most is similar), it works. But adding R54 to it would just mean a bunch of different work cause a lot new features in R54 which aren't in R38.

    Again, I understand your point but I don't see it as a priority thing in this server.

  13. #28
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    I've put the source on Github. I did do this because I feel like sharing = caring. Regardless, this way when people check out the full code they can give tips or criticism if I do something wrong. I'm 100% well aware of any risks, but it's not a huge problem. Also, keep in mind, it's unusable at the moment. The server is built upon the post-shuffle client, but it isn't final I'll make that.

    https://github.com/Gabriellexo97/Project-Nordlys

    Also keep in mind there are probably a lot of things I overlooked so feel free to correct me on any major (or minor...) mistakes.

  14. #29
    Live Ocottish Sverlord Joopie is online now
    LegendRank
    Jun 2010 Join Date
    The NetherlandsLocation
    2,767Posts

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

    Few things I noticed when browsing your code on GitHub:
    - you don't need to build your own pooling system. That's something the ado.net connector should do for you. That's why you have a pooling option in the connection string;). By the way, I believe it's true by default.
    - your dependencyregistar.resolve method depends on another method to be called first. It's not recommend to do such thing. I also feel like you can remove that entire class and use the provided IServiceCollection and IServiceProvider instead. You can also inject the IServiceProvider into implementation classes. It's commen to use interface to separate different layers (domain and storage for example). Fun fact, you can have scoped lifetimes when utilizing IScoperServiceFactory (something like that), make sure to dispose when you're done with it.
    - If you don't want to use a have framwork like EF, I suggest Dapper. It still allows you to make your own queries, but handles the ORM part and parameter binding for you.
    - another fun fact: you could register all your ICommand implementations in IServiceCollection, like: serviceCollection.AddTransient<ICommand, ClearCommand>();. And that for every command you like to add. In your console command handler thingy you can inject an IEnumerable<ICommand>.
    - instead of using any to check if the command exists, use: TryGetValue.
    - I would use extension methods on the ISerciveCollection to register your dependencies and services, instead of using a SessionService.Register.
    - "protected override async void ChannelRead0(IChannelHandlerContext ctx, ClientMessage msg)" doesn't look like an asynchronous method. Have you checked if that's actually possible? And do you know that exceptions will be eaten and cause unexpected behavior? You can't put async randomly and expecting everything would be fine. I suggest you to wrap it in a Task.Run.

    Hope this helps you.

  15. #30
    Alpha Member Emily is offline
    MemberRank
    Oct 2012 Join Date
    The NetherlandsLocation
    2,408Posts

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

    Quote Originally Posted by Joopie View Post
    Few things I noticed when browsing your code on GitHub:
    - you don't need to build your own pooling system. That's something the ado.net connector should do for you. That's why you have a pooling option in the connection string;). By the way, I believe it's true by default.
    - your dependencyregistar.resolve method depends on another method to be called first. It's not recommend to do such thing. I also feel like you can remove that entire class and use the provided IServiceCollection and IServiceProvider instead. You can also inject the IServiceProvider into implementation classes. It's commen to use interface to separate different layers (domain and storage for example). Fun fact, you can have scoped lifetimes when utilizing IScoperServiceFactory (something like that), make sure to dispose when you're done with it.
    - If you don't want to use a have framwork like EF, I suggest Dapper. It still allows you to make your own queries, but handles the ORM part and parameter binding for you.
    - another fun fact: you could register all your ICommand implementations in IServiceCollection, like: serviceCollection.AddTransient<ICommand, ClearCommand>();. And that for every command you like to add. In your console command handler thingy you can inject an IEnumerable<ICommand>.
    - instead of using any to check if the command exists, use: TryGetValue.
    - I would use extension methods on the ISerciveCollection to register your dependencies and services, instead of using a SessionService.Register.
    - "protected override async void ChannelRead0(IChannelHandlerContext ctx, ClientMessage msg)" doesn't look like an asynchronous method. Have you checked if that's actually possible? And do you know that exceptions will be eaten and cause unexpected behavior? You can't put async randomly and expecting everything would be fine. I suggest you to wrap it in a Task.Run.

    Hope this helps you.
    I'll look and do some research on these points. Wouldn't be too hard for most part. Thanks for the suggestions anyways.



Page 2 of 4 FirstFirst 1234 LastLast

Advertisement