From 12f629d52d2eacf50dab96a16b14f2e211f0319a Mon Sep 17 00:00:00 2001 From: JayArrowz <49910176+JayArrowz@users.noreply.github.com> Date: Tue, 18 May 2021 23:33:39 +0100 Subject: [PATCH] Add 474 Protocol (#25) --- NetScape.Abstractions/Constants.cs | 7 +- .../Extensions/ByteBufferExtensions.cs | 7 +- .../Walking => Game}/WalkingQueueHandler.cs | 2 +- .../Login/DefaultLoginProcessor.cs | 110 ++++++ NetScape.Abstractions/Model/Game/Player.cs | 3 + .../Model/Game/{Walking => }/WalkingQueue.cs | 0 .../Model/Login/LoginRequest.cs | 7 + NetScape.Abstractions/Model/Position.cs | 18 + .../NetScape.Abstractions.csproj | 1 + NetScape.Modules.Cache/RuneTek5/Sector.cs | 2 +- .../NetScape.Modules.DAL.Test.csproj | 4 +- .../EntityFrameworkPlayerSerializer.cs | 3 +- .../FourSevenFourGameModule.cs | 31 ++ .../Decoders/WalkingQueueMessageDecoder.cs | 48 +++ .../Encoders/GroupedRegionUpdateMessage.cs | 59 ++++ .../Messages/Encoders/RemoveObjectMessage.cs | 65 ++++ .../Messages/Encoders/SendMapRegionMessage.cs | 62 ++++ .../Messages/Encoders/SendObjectMessage.cs | 90 +++++ .../Messages/Encoders/SendPingMessage.cs | 24 ++ .../Messages/Handlers/PingMessageHandler.cs | 22 ++ .../Handlers/WalkingQueueMessageHandler.cs | 46 +++ .../Messages/Handlers/WelcomeScreenHandler.cs | 37 +++ ...NetScape.Modules.FourSevenFour.Game.csproj | 17 + .../Player/PlayerInitializer.cs | 57 ++++ .../Player/TabManager.cs | 32 ++ .../FourSevenFourLoginModule.cs | 21 ++ .../FourSevenFourLoginStatus.cs | 28 ++ .../Handlers/HandshakeDecoder.cs | 66 ++++ .../Handlers/JS5Decoder.cs | 52 +++ .../Handlers/JS5Encoder.cs | 116 +++++++ .../Handlers/LoginDecoder.cs | 314 ++++++++++++++++++ .../Handlers/LoginEncoder.cs | 23 ++ .../Handlers/LoginProvider.cs | 22 ++ .../HandshakeType.cs | 9 + .../JS5Request.cs | 23 ++ .../LoginProcessor.cs | 47 +++ ...Modules.FourSevenFour.LoginProtocol.csproj | 17 + .../Rs2LoginRequest.cs | 10 + .../Rs2LoginResponse.cs | 8 + .../FourSevenFourUpdatingModule.cs | 16 + .../Messages/PlayerSynchronizationMessage.cs | 256 ++++++++++++++ ...odules.FourSevenFour.World.Updating.csproj | 14 + .../PlayerUpdater.cs | 262 +++++++++++++++ .../Segments/AddPlayerSegment.cs | 18 + .../Segments/MovementSegment.cs | 34 ++ .../Segments/RemoveMobSegment.cs | 16 + .../Segments/SegmentType.cs | 12 + .../Segments/SynchronizationSegment.cs | 16 + .../Segments/TeleportSegment.cs | 16 + .../FrameType.cs | 2 +- .../FrameTypeExtensions.cs | 2 + .../MessageFrameEncoder.cs | 4 +- .../MessageHeaderDecoder.cs | 6 +- .../ProtoMessageExtensions.cs | 2 + .../Protos/MessageCodec.proto | 97 +++++- .../Handlers/WalkingQueueMessageHandler.cs | 7 +- .../Handlers/LoginDecoder.cs | 30 +- .../Handlers/LoginEncoder.cs | 8 +- .../LoginProcessor.cs | 101 +----- .../Rs2LoginRequest.cs | 4 - .../Rs2LoginResponse.cs | 2 +- ...nStatus.cs => ThreeOneSevenLoginStatus.cs} | 2 +- .../PlayerUpdater.cs | 1 - NetScape.Modules.World/World.cs | 5 +- NetScape.sln | 20 +- NetScape/Kernel.cs | 13 +- NetScape/NetScape.csproj | 3 + 67 files changed, 2328 insertions(+), 151 deletions(-) rename NetScape.Abstractions/{Model/Game/Walking => Game}/WalkingQueueHandler.cs (99%) create mode 100644 NetScape.Abstractions/Login/DefaultLoginProcessor.cs rename NetScape.Abstractions/Model/Game/{Walking => }/WalkingQueue.cs (100%) create mode 100644 NetScape.Modules.FourSevenFour.Game/FourSevenFourGameModule.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Decoders/WalkingQueueMessageDecoder.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Encoders/GroupedRegionUpdateMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Encoders/RemoveObjectMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendMapRegionMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendObjectMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendPingMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Handlers/PingMessageHandler.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WalkingQueueMessageHandler.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WelcomeScreenHandler.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/NetScape.Modules.FourSevenFour.Game.csproj create mode 100644 NetScape.Modules.FourSevenFour.Game/Player/PlayerInitializer.cs create mode 100644 NetScape.Modules.FourSevenFour.Game/Player/TabManager.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginModule.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginStatus.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/HandshakeDecoder.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Decoder.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Encoder.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginDecoder.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginEncoder.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginProvider.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/HandshakeType.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/JS5Request.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/LoginProcessor.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/NetScape.Modules.FourSevenFour.LoginProtocol.csproj create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginRequest.cs create mode 100644 NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginResponse.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/FourSevenFourUpdatingModule.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Messages/PlayerSynchronizationMessage.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/NetScape.Modules.FourSevenFour.World.Updating.csproj create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/PlayerUpdater.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/AddPlayerSegment.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/MovementSegment.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/RemoveMobSegment.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/SegmentType.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/SynchronizationSegment.cs create mode 100644 NetScape.Modules.FourSevenFour.World.Updating/Segments/TeleportSegment.cs rename NetScape.Modules.ThreeOneSeven.LoginProtocol/{LoginStatus.cs => ThreeOneSevenLoginStatus.cs} (95%) diff --git a/NetScape.Abstractions/Constants.cs b/NetScape.Abstractions/Constants.cs index c08337be..036252a1 100644 --- a/NetScape.Abstractions/Constants.cs +++ b/NetScape.Abstractions/Constants.cs @@ -1,12 +1,13 @@ using DotNetty.Common.Utilities; +using NetScape.Abstractions.Model; using NetScape.Abstractions.Model.Game; namespace NetScape.Abstractions { public class Constants { - public static readonly int RegionSize = 8; - public static readonly int ArchiveCount = 9; - public static readonly AttributeKey PlayerAttributeKey = AttributeKey.ValueOf("Player"); + public static int RegionSize { get; } = 8; + public static AttributeKey PlayerAttributeKey { get; } = AttributeKey.ValueOf("Player"); + public static Position HomePosition { get; } = new Position(3333, 3333, 0); } } diff --git a/NetScape.Abstractions/Extensions/ByteBufferExtensions.cs b/NetScape.Abstractions/Extensions/ByteBufferExtensions.cs index 4f913999..b512de07 100644 --- a/NetScape.Abstractions/Extensions/ByteBufferExtensions.cs +++ b/NetScape.Abstractions/Extensions/ByteBufferExtensions.cs @@ -8,10 +8,15 @@ namespace NetScape.Abstractions.Extensions public static class ByteBufferExtensions { public static string ReadString(this IByteBuffer buffer) + { + return ReadString(buffer, 10); + } + + public static string ReadString(this IByteBuffer buffer, int terminator) { var strBldr = new StringBuilder(); int charByte; - while ((charByte = buffer.ReadByte()) != 10) + while ((charByte = buffer.ReadByte()) != terminator) { strBldr.Append((char)charByte); } diff --git a/NetScape.Abstractions/Model/Game/Walking/WalkingQueueHandler.cs b/NetScape.Abstractions/Game/WalkingQueueHandler.cs similarity index 99% rename from NetScape.Abstractions/Model/Game/Walking/WalkingQueueHandler.cs rename to NetScape.Abstractions/Game/WalkingQueueHandler.cs index a8bc9a6d..2dfe91d2 100644 --- a/NetScape.Abstractions/Model/Game/Walking/WalkingQueueHandler.cs +++ b/NetScape.Abstractions/Game/WalkingQueueHandler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; -namespace NetScape.Abstractions.Model.Game.Walking +namespace NetScape.Abstractions.Model.Game { public class WalkingQueueHandler { diff --git a/NetScape.Abstractions/Login/DefaultLoginProcessor.cs b/NetScape.Abstractions/Login/DefaultLoginProcessor.cs new file mode 100644 index 00000000..7bdd933a --- /dev/null +++ b/NetScape.Abstractions/Login/DefaultLoginProcessor.cs @@ -0,0 +1,110 @@ +using Autofac; +using NetScape.Abstractions.FileSystem; +using NetScape.Abstractions.Interfaces.Login; +using NetScape.Abstractions.Model.Login; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace NetScape.Abstractions.Login +{ + public abstract class DefaultLoginProcessor : ILoginProcessor, + IStartable, + IDisposable + where TRequest : LoginRequest + { + protected internal readonly ILogger _logger; + private readonly IList _loginRequests = new List(); + + private readonly object _lockObject = new object(); + + private CancellationToken _cancellationToken; + private CancellationTokenSource _cancellationTokenSource; + + public DefaultLoginProcessor(ILogger logger) + { + _logger = logger; + } + + /// + /// Enqueues the specified request. + /// + /// The request. + /// Login already exists + public void Enqueue(TRequest request) + { + lock (_lockObject) + { + var loginExists = _loginRequests.Any(t => t.Credentials.Username.Equals(request.Credentials.Username, StringComparison.InvariantCultureIgnoreCase) + && t.Credentials.Password.Equals(request.Credentials.Password, StringComparison.InvariantCultureIgnoreCase)); + + if (loginExists) + { + throw new InvalidOperationException("Login already exists"); + } + + _loginRequests.Add(request); + } + } + + /// + /// Processes a single by retriving the player from + /// + /// The login request. + /// + protected abstract Task ProcessAsync(TRequest request); + + /// + /// Handles the login queue + /// + private async Task ProcessLoginsAsync() + { + while (!_cancellationToken.IsCancellationRequested) + { + while (_loginRequests.Count > 0) + { + var requests = _loginRequests.ToList(); + var tasks = requests.Select(loginTask => + (request: loginTask, responseTask: ProcessAsync(loginTask))) + .ToList(); + await Task.WhenAll(tasks.Select(t => t.responseTask)); + tasks.ForEach(t => + { + var responseTask = t.responseTask; + var request = t.request; + if (responseTask.IsCompletedSuccessfully) + { + _loginRequests.Remove(t.request); + t.request.Result = request.Result; + _ = request.OnResult(responseTask.Result); + _logger.Debug("Processed Login Request: {@LoginRequest}", request.Credentials); + } + }); + } + await Task.Delay(600); + } + } + + /// + /// Perform once-off startup processing. + /// + public void Start() + { + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationToken = _cancellationTokenSource.Token; + Task.Factory.StartNew(ProcessLoginsAsync, _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource?.Dispose(); + } + } +} diff --git a/NetScape.Abstractions/Model/Game/Player.cs b/NetScape.Abstractions/Model/Game/Player.cs index 245cd7a4..d12489fd 100644 --- a/NetScape.Abstractions/Model/Game/Player.cs +++ b/NetScape.Abstractions/Model/Game/Player.cs @@ -5,6 +5,7 @@ using NetScape.Abstractions.Model.World.Updating.Blocks; using NetScape.Modules.Messages; using NetScape.Modules.Messages.Builder; +using Serilog; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading; @@ -17,6 +18,7 @@ public partial class Player : Mob private static readonly int DefaultViewingDistance = 15; private static int _appearanceTicketCounter = 0; + [NotMapped] public int PingCount { get; set; } [NotMapped] public Position LastKnownRegion { get; set; } [NotMapped] public bool RegionChanged { get; set; } [NotMapped] public int AppearanceTicket { get; } = NextAppearanceTicket(); @@ -66,6 +68,7 @@ public void ResetViewingDistance() /// The message. public async Task SendAsync(IEncoderMessage message) { + Log.Logger.Debug("Sending {0} to player {1}", message, Username); var msg = message.ToMessage(ChannelHandlerContext.Allocator); if (ChannelHandlerContext.Channel.Active) diff --git a/NetScape.Abstractions/Model/Game/Walking/WalkingQueue.cs b/NetScape.Abstractions/Model/Game/WalkingQueue.cs similarity index 100% rename from NetScape.Abstractions/Model/Game/Walking/WalkingQueue.cs rename to NetScape.Abstractions/Model/Game/WalkingQueue.cs diff --git a/NetScape.Abstractions/Model/Login/LoginRequest.cs b/NetScape.Abstractions/Model/Login/LoginRequest.cs index e71ca068..35b117fe 100644 --- a/NetScape.Abstractions/Model/Login/LoginRequest.cs +++ b/NetScape.Abstractions/Model/Login/LoginRequest.cs @@ -1,5 +1,7 @@ using DotNetty.Transport.Channels; using NetScape.Abstractions.IO.Util; +using System; +using System.Threading.Tasks; namespace NetScape.Abstractions.Model.Login { @@ -19,5 +21,10 @@ public record LoginRequest public int ReleaseNumber { get; set; } public TRes Result { get; set; } + + /// + /// Called on response of request + /// + public Func OnResult { get; set; } } } diff --git a/NetScape.Abstractions/Model/Position.cs b/NetScape.Abstractions/Model/Position.cs index ef8802e3..54da560a 100644 --- a/NetScape.Abstractions/Model/Position.cs +++ b/NetScape.Abstractions/Model/Position.cs @@ -207,6 +207,24 @@ public int TopLeftRegionY } } + /// + /// Gets the region x. + /// + /// + /// The region x. + /// + [NotMapped] + public int RegionX => (X >> 3) - 6; + + /// + /// Gets the region y. + /// + /// + /// The region y. + /// + [NotMapped] + public int RegionY => (Y >> 3) - 6; + /// /// Gets the x coordinate. /// diff --git a/NetScape.Abstractions/NetScape.Abstractions.csproj b/NetScape.Abstractions/NetScape.Abstractions.csproj index 432f9fb8..7e3ace11 100644 --- a/NetScape.Abstractions/NetScape.Abstractions.csproj +++ b/NetScape.Abstractions/NetScape.Abstractions.csproj @@ -15,6 +15,7 @@ + diff --git a/NetScape.Modules.Cache/RuneTek5/Sector.cs b/NetScape.Modules.Cache/RuneTek5/Sector.cs index da9e3568..abb6175e 100644 --- a/NetScape.Modules.Cache/RuneTek5/Sector.cs +++ b/NetScape.Modules.Cache/RuneTek5/Sector.cs @@ -110,7 +110,7 @@ public static Sector Decode(int position, byte[] data, CacheIndex expectedIndex, var nextSectorPosition = dataReader.ReadUInt24BigEndian(); var actualIndex = (CacheIndex)dataReader.ReadByte(); - if (actualIndex - 1 != expectedIndex) + if (actualIndex != expectedIndex) { throw new DecodeException($"Expected sector for index {(int)expectedIndex}, got {(int)actualIndex}."); } diff --git a/NetScape.Modules.DAL.Test/NetScape.Modules.DAL.Test.csproj b/NetScape.Modules.DAL.Test/NetScape.Modules.DAL.Test.csproj index c0ae12b3..f3debfb6 100644 --- a/NetScape.Modules.DAL.Test/NetScape.Modules.DAL.Test.csproj +++ b/NetScape.Modules.DAL.Test/NetScape.Modules.DAL.Test.csproj @@ -1,9 +1,7 @@ - + netcoreapp5.0 - - false diff --git a/NetScape.Modules.DAL/EntityFrameworkPlayerSerializer.cs b/NetScape.Modules.DAL/EntityFrameworkPlayerSerializer.cs index 8d14fd48..e9196f0b 100644 --- a/NetScape.Modules.DAL/EntityFrameworkPlayerSerializer.cs +++ b/NetScape.Modules.DAL/EntityFrameworkPlayerSerializer.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using NetScape.Abstractions; using NetScape.Abstractions.FileSystem; using NetScape.Abstractions.Model; using NetScape.Abstractions.Model.Game; @@ -80,7 +81,7 @@ public async Task GetOrCreateAsync(PlayerCredentials playerCredentials) { Username = playerCredentials.Username, Password = playerCredentials.Password, - Position = new Position(3333, 3333, 0), + Position = Constants.HomePosition, Appearance = Appearance.DefaultAppearance }; dbContext.Attach(defaultPlayer); diff --git a/NetScape.Modules.FourSevenFour.Game/FourSevenFourGameModule.cs b/NetScape.Modules.FourSevenFour.Game/FourSevenFourGameModule.cs new file mode 100644 index 00000000..42704307 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/FourSevenFourGameModule.cs @@ -0,0 +1,31 @@ +using Autofac; +using NetScape.Abstractions.Interfaces.Game.Interface; +using NetScape.Abstractions.Interfaces.Game.Player; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Modules.FourSevenFour.Game.Interface; +using NetScape.Modules.FourSevenFour.Game.Players; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.Game +{ + public class FourSevenFourGameModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As(); + builder.RegisterType().As(); + + #region Handlers + builder.RegisterAssemblyTypes(typeof(FourSevenFourGameModule).Assembly) + .AsClosedTypesOf(typeof(IMessageDecoder<>)) + .As() + .SingleInstance(); + #endregion + base.Load(builder); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Decoders/WalkingQueueMessageDecoder.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Decoders/WalkingQueueMessageDecoder.cs new file mode 100644 index 00000000..474b3063 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Decoders/WalkingQueueMessageDecoder.cs @@ -0,0 +1,48 @@ +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.Game; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Builder; +using NetScape.Modules.Messages.Models; +using System.Linq; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Decoders +{ + public class WalkingQueueMessageDecoder : MessageDecoderBase + { + public override int[] Ids { get; } = new int[] { 11, 46, 59 }; + public override FrameType FrameType { get; } = FrameType.VariableByte; + + protected override FourSevenFourDecoderMessages.Types.WalkingQueueMessage Decode(Player player, MessageFrame frame) + { + var reader = new MessageFrameReader(frame); + var length = frame.Payload.ReadableBytes; + + if (frame.Id == 11) + { + length -= 14; // strip off anti-cheat data + } + + int steps = (length - 5) / 2; + int[,] path = new int[steps, 2]; + for (int i = 0; i < steps; i++) + { + path[i, 0] = (int)reader.GetSigned(MessageType.Byte); + path[i, 1] = (int)reader.GetSigned(MessageType.Byte, DataTransformation.Subtract); + } + int x = (int)reader.GetUnsigned(MessageType.Short, DataTransformation.Add); + int y = (int)reader.GetUnsigned(MessageType.Short, DataOrder.Little); + var run = reader.GetUnsigned(MessageType.Byte, DataTransformation.Negate) == 1; + + var positions = new Position[steps + 1]; + positions[0] = new Position(x, y); + for (int i = 0; i < steps; i++) + { + positions[i + 1] = new Position(path[i, 0] + x, path[i, 1] + y); + } + FourSevenFourDecoderMessages.Types.WalkingQueueMessage walkingQueueMessage = new() { Run = run, }; + walkingQueueMessage.X.Add(positions.Select(t => t.X)); + walkingQueueMessage.Y.Add(positions.Select(t => t.Y)); + return walkingQueueMessage; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/GroupedRegionUpdateMessage.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/GroupedRegionUpdateMessage.cs new file mode 100644 index 00000000..8508e997 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/GroupedRegionUpdateMessage.cs @@ -0,0 +1,59 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.Region; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Builder; +using System.Collections.Generic; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Encoders +{ + public class GroupedRegionUpdateMessage : IEncoderMessage + { + /// + /// The last known region of the player + /// + private readonly Position _lastKnownRegion; + + /// + /// The set of the messages to be sent + /// + private readonly HashSet _messages; + + /// + /// The position of the region being updated + /// + private readonly Position _update; + + /// + /// Initializes a new instance of the class. + /// + /// The last known region. + /// The coordinates of the region being updated. + /// The set of the messages to be sent. + public GroupedRegionUpdateMessage(Position lastKnownRegion, RegionCoordinates coordinates, + HashSet messages) + { + _lastKnownRegion = lastKnownRegion; + _update = new Position(coordinates.AbsoluteX, coordinates.AbsoluteY); + _messages = messages; + } + + public MessageFrame ToMessage(IByteBufferAllocator alloc) + { + MessageFrameBuilder bldr = new MessageFrameBuilder(alloc, 60, FrameType.VariableShort); + Position basePos = _lastKnownRegion; + + bldr.Put(MessageType.Byte, _update.GetLocalY(basePos)); + bldr.Put(MessageType.Byte, DataTransformation.Negate, _update.GetLocalX(basePos)); + foreach (RegionUpdateMessage update in _messages) + { + var frame = update.ToMessage(alloc); + bldr.Put(MessageType.Byte, frame.Id); + bldr.PutBytes(frame.Payload); + } + + return bldr.ToMessageFrame(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/RemoveObjectMessage.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/RemoveObjectMessage.cs new file mode 100644 index 00000000..a948d028 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/RemoveObjectMessage.cs @@ -0,0 +1,65 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Modules.Messages.Builder; +using NetScape.Modules.Region; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Encoders +{ + public class RemoveObjectMessage : RegionUpdateMessage + { + /// + /// The orientation of the object. + /// + private readonly int _orientation; + + /// + /// The position of the object. + /// + private readonly int _positionOffset; + + /// + /// The type of the object. + /// + private readonly int _type; + + /// + /// Initializes a new instance of the class. + /// + /// The to send. + /// The offset of the object's position from the region's central position. + public RemoveObjectMessage(GameObject obj, int positionOffset) + { + _positionOffset = positionOffset; + _type = obj.Type; + _orientation = obj.Orientation; + } + + public override int Priority => Low_Priority; + + public override bool Equals(object obj) + { + if (obj is RemoveObjectMessage) + { + RemoveObjectMessage other = (RemoveObjectMessage)obj; + return _type == other._type && _orientation == other._orientation && _positionOffset == other._positionOffset; + } + + return false; + } + + public override int GetHashCode() + { + int prime = 31; + int result = prime * _positionOffset + _orientation; + return prime * result + _type; + } + + public override MessageFrame ToMessage(IByteBufferAllocator alloc) + { + var bldr = new MessageFrameBuilder(alloc, 101); + bldr.Put(MessageType.Byte, DataTransformation.Negate, _type << 2 | _orientation); + bldr.Put(MessageType.Byte, _positionOffset); + return bldr.ToMessageFrame(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendMapRegionMessage.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendMapRegionMessage.cs new file mode 100644 index 00000000..e1c2ecfb --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendMapRegionMessage.cs @@ -0,0 +1,62 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Model.Game; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Builder; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Encoders +{ + public class SendMapRegionMessage : IEncoderMessage + { + public Player Player { get; } + + public SendMapRegionMessage(Player player) + { + Player = player; + } + + public MessageFrame ToMessage(IByteBufferAllocator alloc) + { + var messageFrameBuilder = new MessageFrameBuilder(alloc, 61, FrameType.VariableShort); + var playerPos = Player.Position; + var clearRegion = false; + + var regionX = playerPos.RegionX; + var regionY = playerPos.RegionY; + var absoluteRegionX = (regionX + 6) / 8; + var absoluteRegionY = (regionY + 6) / 8; + + if ( + ((absoluteRegionX == 48 || absoluteRegionX == 49) && absoluteRegionY == 48) || + (absoluteRegionX == 48 && absoluteRegionY == 148) + ) + { + clearRegion = true; + } + messageFrameBuilder.Put(MessageType.Short, DataOrder.Little, DataTransformation.Add, playerPos.LocalY); + messageFrameBuilder.Put(MessageType.Byte, playerPos.Height); + messageFrameBuilder.Put(MessageType.Short, regionX + 6); + for (int xCalc = regionX / 8; xCalc <= (regionX + 12) / 8; xCalc++) + { + for (int yCalc = regionY / 8; yCalc <= (regionY + 12) / 8; yCalc++) + { + int regionId = yCalc + (xCalc << 8); // 1786653352 + if (clearRegion || yCalc != 49 && yCalc != 149 && yCalc != 147 + && xCalc != 50 && (xCalc != 49 || yCalc != 47)) + { + messageFrameBuilder.Put(MessageType.Int, DataOrder.Little, 0); + messageFrameBuilder.Put(MessageType.Int, DataOrder.Little, 0); + messageFrameBuilder.Put(MessageType.Int, DataOrder.Little, 0); + messageFrameBuilder.Put(MessageType.Int, DataOrder.Little, 0); + } + } + } + + messageFrameBuilder.Put(MessageType.Short, DataOrder.Big, DataTransformation.Add, playerPos.LocalX); + messageFrameBuilder.Put(MessageType.Short, regionY + 6); + return messageFrameBuilder.ToMessageFrame(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendObjectMessage.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendObjectMessage.cs new file mode 100644 index 00000000..7f8e79e4 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendObjectMessage.cs @@ -0,0 +1,90 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Modules.Messages.Builder; +using NetScape.Modules.Region; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Encoders +{ + public class SendObjectMessage : RegionUpdateMessage + { + /// + /// Gets the priority. + /// + /// + /// The priority. + /// + public override int Priority => Low_Priority; + + /// + /// The id of the object. + /// + private readonly int _id; + + /// + /// The orientation of the object. + /// + private readonly int _orientation; + + /// + /// The position of the object. + /// + private readonly int _positionOffset; + + /// + /// The type of the object. + /// + private readonly int _type; + + + /** + * Creates the SendObjectMessage. + * + * @param object The {@link GameObject} to send. + * @param positionOffset The offset of the object's position from the region's central position. + */ + /// + /// Initializes a new instance of the class. + /// + /// The game object. + /// The position offset. + public SendObjectMessage(GameObject obj, int positionOffset) + { + _id = obj.Id; + _positionOffset = positionOffset; + _type = obj.Type; + _orientation = obj.Orientation; + } + + public override bool Equals(object obj) + { + if (obj is SendObjectMessage) + { + SendObjectMessage other = (SendObjectMessage)obj; + if (_id != other._id || _type != other._type) + { + return false; + } + + return _positionOffset == other._positionOffset && _type == other._type; + } + return false; + } + + public override int GetHashCode() + { + int prime = 31; + int result = prime * _id + _orientation; + result = prime * result + _type; + return prime * result + _positionOffset; + } + + public override MessageFrame ToMessage(IByteBufferAllocator alloc) + { + MessageFrameBuilder builder = new MessageFrameBuilder(alloc, 151); + builder.Put(MessageType.Byte, DataTransformation.Add, _positionOffset); + builder.Put(MessageType.Short, DataOrder.Little, _id); + builder.Put(MessageType.Byte, DataTransformation.Subtract, _type << 2 | _orientation); + return builder.ToMessageFrame(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendPingMessage.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendPingMessage.cs new file mode 100644 index 00000000..6042e3ac --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Encoders/SendPingMessage.cs @@ -0,0 +1,24 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Model.Game; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Builder; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Encoders +{ + public class SendPingMessage : IEncoderMessage + { + public Player Player { get; } + + public SendPingMessage(Player player) + { + Player = player; + } + + public MessageFrame ToMessage(IByteBufferAllocator alloc) + { + var messageFrameBuilder = new MessageFrameBuilder(alloc, 238, FrameType.VariableShort); + messageFrameBuilder.Put(MessageType.Int, Player.PingCount++ > 0xF42400 ? Player.PingCount = 1 : Player.PingCount); + return messageFrameBuilder.ToMessageFrame(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/PingMessageHandler.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/PingMessageHandler.cs new file mode 100644 index 00000000..1c8dc302 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/PingMessageHandler.cs @@ -0,0 +1,22 @@ +using NetScape.Abstractions.Model.Messages; +using NetScape.Modules.FourSevenFour.Game.Messages.Encoders; +using NetScape.Modules.Messages; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static NetScape.Modules.Messages.Models.FourSevenFourDecoderMessages.Types; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Handlers +{ + [MessageHandler] + public class PingMessageHandler + { + [Message(typeof(PingMessage))] + public void OnPingMessage(DecoderMessage message) + { + _ = message.Player.SendAsync(new SendPingMessage(message.Player)); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WalkingQueueMessageHandler.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WalkingQueueMessageHandler.cs new file mode 100644 index 00000000..fce89592 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WalkingQueueMessageHandler.cs @@ -0,0 +1,46 @@ +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.Game; +using NetScape.Abstractions.Model.Messages; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Models; +using System.Linq; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Handlers { + + [MessageHandler] + public class WalkingQueueMessageHandler + { + private readonly WalkingQueueHandler _walkingQueueHandler; + public WalkingQueueMessageHandler(WalkingQueueHandler walkingQueueHandler) + { + _walkingQueueHandler = walkingQueueHandler; + } + + [Message(typeof(FourSevenFourDecoderMessages.Types.WalkingQueueMessage))] + public void OnWalkQueueMessage(DecoderMessage decoderMessage) + { + var player = decoderMessage.Player; + var message = decoderMessage.Message; + var positions = Enumerable.Range(0, message.X.Count) + .Select(t => new Position + { + X = message.X[t], + Y = message.Y[t] + }).ToArray(); + + for (int index = 0; index < positions.Length; index++) + { + Position step = positions[index]; + if (index == 0) + { + _walkingQueueHandler.AddFirstStep(player, step); + } + else + { + _walkingQueueHandler.AddStep(player, step); + } + } + player.WalkingQueue.Running = message.Run || player.WalkingQueue.Running; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WelcomeScreenHandler.cs b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WelcomeScreenHandler.cs new file mode 100644 index 00000000..fb31bad1 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Messages/Handlers/WelcomeScreenHandler.cs @@ -0,0 +1,37 @@ +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Abstractions.Model.Messages; +using NetScape.Modules.Messages; +using System; +using System.Threading.Tasks; +using static NetScape.Modules.Messages.Models.FourSevenFourDecoderMessages.Types; +using static NetScape.Modules.Messages.Models.FourSevenFourEncoderMessages.Types; + +namespace NetScape.Modules.FourSevenFour.Game.Messages.Handlers +{ + [MessageHandler] + public class WelcomeScreenHandler + { + private readonly IProtoMessageSender _messageSender; + + public WelcomeScreenHandler(IProtoMessageSender messageSender) + { + _messageSender = messageSender; + } + + [Message(typeof(ClickButtonMessage), nameof(Filter))] + public async Task OnWelcomeScreenClick(DecoderMessage decoderMessage) + { + var player = decoderMessage.Player; + var buttonPackedId = decoderMessage.Message.ButtonId; + switch(buttonPackedId) + { + case 6: + await _messageSender.SendAsync(player, new SendInterfaceMessage { InterfaceId = 548 }); + player.UpdateAppearance(); + return; + } + } + + public Predicate> Filter { get; } = e => e.Message.InterfaceId == 378; + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/NetScape.Modules.FourSevenFour.Game.csproj b/NetScape.Modules.FourSevenFour.Game/NetScape.Modules.FourSevenFour.Game.csproj new file mode 100644 index 00000000..48b8f198 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/NetScape.Modules.FourSevenFour.Game.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp5.0 + false + NetScape 474 Game Module + + + + + + + + + + + diff --git a/NetScape.Modules.FourSevenFour.Game/Player/PlayerInitializer.cs b/NetScape.Modules.FourSevenFour.Game/Player/PlayerInitializer.cs new file mode 100644 index 00000000..07b8e389 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Player/PlayerInitializer.cs @@ -0,0 +1,57 @@ +using NetScape.Abstractions.Interfaces.Game.Interface; +using NetScape.Abstractions.Interfaces.Game.Player; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Modules.FourSevenFour.Game.Messages.Encoders; +using System.Threading.Tasks; +using static NetScape.Modules.Messages.Models.FourSevenFourEncoderMessages.Types; + +namespace NetScape.Modules.FourSevenFour.Game.Players +{ + public class PlayerInitializer : IPlayerInitializer + { + private readonly ITabManager _tabManager; + private readonly IProtoMessageSender _protoMessageSender; + + public PlayerInitializer(ITabManager tabManager, IProtoMessageSender protoMessageSender) + { + _tabManager = tabManager; + _protoMessageSender = protoMessageSender; + } + + public Task InitializeAsync(Abstractions.Model.Game.Player player) + { + _ = player.SendAsync(new SendMapRegionMessage(player)).ContinueWith(_ => + { + + var defaultTabs = _tabManager.Default; + for (int tab = 0; tab < defaultTabs.Length; tab++) + { + var interfaceId = defaultTabs[tab]; + _ = _tabManager.SetTabAsync(player, tab, interfaceId); + } + }); + + var openLoginScreenMessage = new SendInterfaceMessage + { + InterfaceId = 549 + }; + + _ = _protoMessageSender.SendAsync(player, openLoginScreenMessage); + _ = _protoMessageSender.SendAsync(player, new OpenInterfaceMessage + { + Window = 549, + Position = 2, + InterfaceId = 378, + Walkable = true + }); + _ = _protoMessageSender.SendAsync(player, new OpenInterfaceMessage + { + Window = 549, + Position = 3, + InterfaceId = 17, + Walkable = true + }); + return Task.CompletedTask; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.Game/Player/TabManager.cs b/NetScape.Modules.FourSevenFour.Game/Player/TabManager.cs new file mode 100644 index 00000000..23414194 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.Game/Player/TabManager.cs @@ -0,0 +1,32 @@ +using NetScape.Abstractions.Interfaces.Game.Interface; +using NetScape.Abstractions.Interfaces.Messages; +using System.Threading.Tasks; +using static NetScape.Modules.Messages.Models.FourSevenFourEncoderMessages.Types; + +namespace NetScape.Modules.FourSevenFour.Game.Interface +{ + public class TabManager : ITabManager + { + private readonly IProtoMessageSender _protoMessageSender; + + public TabManager(IProtoMessageSender protoMessageSender) + { + _protoMessageSender = protoMessageSender; + } + + public int[] TabIds { get; } = new int[] { 90, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112 }; + public int[] Default { get; } = new int[] { 137, 92, 320, 274, 149, 387, 271, 192, 589, 550, 551, 182, 261, 464, 239 }; + + public Task SetTabAsync(Abstractions.Model.Game.Player player, int tabId, int interfaceId) + { + var switchTabMessage = new OpenInterfaceMessage + { + InterfaceId = interfaceId, + Window = 548, //Main window + Position = TabIds[tabId], + Walkable = true + }; + return _protoMessageSender.SendAsync(player, switchTabMessage); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginModule.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginModule.cs new file mode 100644 index 00000000..369f8701 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginModule.cs @@ -0,0 +1,21 @@ +using Autofac; +using NetScape.Abstractions.Interfaces.Login; +using NetScape.Modules.FourSevenFour.LoginProtocol.Handlers; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public class FourSevenFourLoginModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); + builder.RegisterType(); + builder.RegisterType(); + builder.RegisterType().AsSelf(); + builder.RegisterType().AsSelf(); + builder.RegisterType(); + builder.RegisterType().As>() + .As().SingleInstance(); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginStatus.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginStatus.cs new file mode 100644 index 00000000..8ff23854 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/FourSevenFourLoginStatus.cs @@ -0,0 +1,28 @@ +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public enum FourSevenFourLoginStatus + { + StatusExchangeData = 0, + StatusDelay = 1, + StatusOk = 2, + StatusInvalidCredentials = 3, + StatusAccountDisabled = 4, + StatusAccountOnline = 5, + StatusGameUpdated = 6, + StatusServerFull = 7, + StatusLoginServerOffline = 8, + StatusTooManyConnections = 9, + StatusBadSessionId = 10, + StatusLoginServerRejectedSession = 11, + StatusMembersAccountRequired = 12, + StatusCouldNotComplete = 13, + StatusUpdating = 14, + StatusReconnectionOk = 15, + StatusTooManyLogins = 16, + StatusInMembersArea = 17, + StatusInvalidLoginServer = 20, + StatusProfileTransfer = 21, + TypeStandard = 16, + TypeReconnection = 18 + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/HandshakeDecoder.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/HandshakeDecoder.cs new file mode 100644 index 00000000..0d2fba6d --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/HandshakeDecoder.cs @@ -0,0 +1,66 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + public class HandshakeDecoder : ByteToMessageDecoder + { + private readonly ILogger _logger; + private readonly LoginEncoder _loginEncoder; + private readonly LoginDecoder _loginDecoder; + private readonly JS5Decoder _jS5Decoder; + private readonly JS5Encoder _jS5Encoder; + + public HandshakeDecoder(ILogger logger, LoginEncoder loginEncoder, LoginDecoder loginDecoder, + JS5Decoder jS5Decoder, JS5Encoder jS5Encoder) + { + _jS5Decoder = jS5Decoder; + _jS5Encoder = jS5Encoder; + _logger = logger; + _loginEncoder = loginEncoder; + _loginDecoder = loginDecoder; + } + + protected override void Decode(IChannelHandlerContext ctx, IByteBuffer buffer, List output) + { + if (!buffer.IsReadable()) + { + return; + } + + var id = buffer.ReadByte(); + var handshakeType = (HandshakeType)id; + _logger.Debug("Incoming Handshake Decoder Opcode: {0} Type: {1}", id, handshakeType); + switch (handshakeType) + { + case HandshakeType.ServiceGame: + ctx.Channel.Pipeline.AddLast(nameof(LoginEncoder), _loginEncoder); + ctx.Channel.Pipeline.AddAfter(nameof(LoginEncoder), nameof(LoginDecoder), _loginDecoder); + break; + + case HandshakeType.ServiceUpdate: + int version = buffer.ReadInt(); + ctx.Channel.Pipeline.AddLast(nameof(JS5Decoder), _jS5Decoder); + ctx.Channel.Pipeline.AddLast(nameof(JS5Encoder), _jS5Encoder); + + //Really should do version checking + _ = ctx.Channel.WriteAndFlushAsync(ctx.Allocator.Buffer(1).WriteByte((int)FourSevenFourLoginStatus.StatusExchangeData)); + break; + + default: + _logger.Information("Unexpected handshake request received: {0}", id); + return; + } + + ctx.Channel.Pipeline.Remove(this); + output.Add(handshakeType); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Decoder.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Decoder.cs new file mode 100644 index 00000000..2d04fdda --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Decoder.cs @@ -0,0 +1,52 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; +using Serilog; +using System.Collections.Generic; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + public class JS5Decoder : ByteToMessageDecoder + { + private int _encryptionKey = 0; + protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) + { + if (input.ReadableBytes > 0 && input.IsReadable()) + { + input.MarkReaderIndex(); + int opcode = input.ReadByte(); + + Log.Logger.Debug("File Request: Opcode: {0}", opcode); + if (opcode == 0 || opcode == 1) + { + if (input.ReadableBytes < 3) + { + input.ResetReaderIndex(); + return; + } + int index = input.ReadByte(); + int file = input.ReadUnsignedShort(); + bool priority = opcode == 0; + Log.Logger.Debug("File Request: Index: {0}, File: {1} Priority: {2}", index, file, priority); + _ = context.Channel.WriteAndFlushAsync(new JS5Request(index, file, priority, _encryptionKey)); + } + else if (opcode == 2 || opcode == 3 || opcode == 6) + { + input.SkipBytes(3); + } + else if (opcode == 4) + { + _encryptionKey = input.ReadByte(); + if (input.ReadShort() != 0) + { + _ = context.Channel.DisconnectAsync(); + } + } + else + { + Log.Logger.Warning("Unknown JS5 Opcode: {0} Size: {1}", opcode, input.ReadableBytes); + } + } + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Encoder.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Encoder.cs new file mode 100644 index 00000000..62a35040 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/JS5Encoder.cs @@ -0,0 +1,116 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; +using NetScape.Abstractions.Cache; +using NetScape.Abstractions.Extensions; +using NetScape.Abstractions.Interfaces.Cache; +using System; +using System.Linq; +using System.Text; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + public class JS5Encoder : MessageToByteEncoder + { + private readonly IReferenceTableCache _referenceTableCache; + private readonly IFileStore _fileStore; + + public JS5Encoder(IReferenceTableCache referenceTableCache, IFileStore fileStore) + { + _referenceTableCache = referenceTableCache; + _fileStore = fileStore; + } + + protected override void Encode(IChannelHandlerContext context, JS5Request message, IByteBuffer output) + { + try + { + bool allowEncryption = false; + int index = message.Index; + int file = message.File; + + output.WriteByte(index); + output.WriteShort(file); + + byte[] fileData; + if (index == 255 && file == 255) + { + fileData = GetIndexSizeData(context); + } + else if (index == 255) + { + fileData = GetReferenceTableData(file); + allowEncryption = true; + } + else + { + fileData = GetFileData(index, file); + allowEncryption = true; + } + + foreach (byte b in fileData) + { + if (output.WriterIndex % 512 == 0) + { + output.WriteByte(-1); + } + + output.WriteByte(b); + } + + if (allowEncryption) + { + if (message.EncryptionKey != 0) + { + for (int i = 0; i < output.ReadableBytes; i++) + { + output.SetByte(i, (byte)(output.GetByte(i) ^ message.EncryptionKey)); + } + } + } + } + catch (Exception e) + { + Serilog.Log.Logger.Error(e, nameof(Encode)); + } + } + private byte[] GetReferenceTableData(int file) + { + + return _fileStore.ReadFileData(CacheIndex.ReferenceTables, file); + } + + private byte[] GetFileData(int index, int file) + { + var cacheIndex = (CacheIndex)index; + return _referenceTableCache.GetFile(cacheIndex, file).Data; + } + private byte[] GetIndexSizeData(IChannelHandlerContext ctx) + { + + var indexes = _referenceTableCache.GetIndexes().ToList(); + var indexSize = indexes.Count; + + var buffer = ctx.Allocator.Buffer(5 + (indexSize * 8)); + + try + { + buffer.WriteByte(0); + buffer.WriteInt(indexSize * 8); + for (int index = 0; index < indexSize; index++) + { + var file = _referenceTableCache.GetReferenceTable(indexes[index]).Info; + buffer.WriteInt(file.Crc.Value); + buffer.WriteInt(file.Version.GetValueOrDefault()); + Serilog.Log.Logger.Debug("Sending Index {0} File CRC: {1} Version: {2}", index, file.Crc, file.Version); + } + } + catch (Exception e) + { + Serilog.Log.Logger.Error(e, nameof(GetIndexSizeData)); + } + return buffer.GetBytes(); + } + } + +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginDecoder.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginDecoder.cs new file mode 100644 index 00000000..4a492e41 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginDecoder.cs @@ -0,0 +1,314 @@ +using DotNetty.Buffers; +using DotNetty.Common.Utilities; +using DotNetty.Transport.Channels; +using NetScape.Abstractions; +using NetScape.Abstractions.Extensions; +using NetScape.Abstractions.Interfaces.Game.Player; +using NetScape.Abstractions.Interfaces.Login; +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Abstractions.Interfaces.World; +using NetScape.Abstractions.IO; +using NetScape.Abstractions.IO.Util; +using NetScape.Abstractions.Model.Game; +using NetScape.Abstractions.Model.Login; +using NetScape.Abstractions.Util; +using Serilog; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + /// + /// The Game Login decoder + /// + /// + public class LoginDecoder : StatefulFrameDecoder + { + private static readonly Random Random = new Random(); + + /// + /// The login packet length + /// + private int _loginLength; + + /// + /// The reconnecting flag + /// + private bool _reconnecting; + + /// + /// The server side session key + /// + private long _serverSeed; + + /// + /// The username hash + /// + private int _usernameHash; + private readonly ILogger _logger; + private readonly ILoginProcessor _loginProcessor; + private readonly IMessageProvider _gameMessageProvider; + private readonly IWorld _world; + private readonly IPlayerInitializer _playerInitializer; + public LoginDecoder(ILogger logger, ILoginProcessor loginProcessor, IMessageProvider gameMessageProvider, IWorld world, IPlayerInitializer playerInitializer) : base(LoginDecoderState.LoginHandshake) + { + _logger = logger; + _gameMessageProvider = gameMessageProvider; + _loginProcessor = loginProcessor; + _world = world; + _playerInitializer = playerInitializer; + } + + protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output, LoginDecoderState state) + { + _logger.Debug("Login Request Recieved: {0} IP: {1}", state, context.Channel.RemoteAddress); + switch (state) + { + case LoginDecoderState.LoginHandshake: + DecodeHandshake(context, input, output); + break; + case LoginDecoderState.LoginHeader: + DecodeHeader(context, input, output); + break; + case LoginDecoderState.LoginPayload: + DecodePayload(context, input, output); + break; + default: + throw new InvalidOperationException("Invalid login decoder state: " + state); + } + } + + /// + /// Decodes the handshake state. + /// + /// The ctx. + /// The buffer. + /// The output. + private void DecodeHandshake(IChannelHandlerContext ctx, IByteBuffer buffer, List output) + { + if (buffer.IsReadable()) + { + _usernameHash = buffer.ReadByte(); + _serverSeed = Random.NextLong(); + + var response = ctx.Allocator.Buffer(17); + response.WriteByte((int)FourSevenFourLoginStatus.StatusExchangeData); + response.WriteLong(_serverSeed); + ctx.Channel.WriteAndFlushAsync(response); + SetState(LoginDecoderState.LoginHeader); + } + } + + /// + /// Decodes the header state. + /// + /// The ctx. + /// The buffer. + /// The output. + private void DecodeHeader(IChannelHandlerContext ctx, IByteBuffer buffer, List output) + { + if (buffer.ReadableBytes >= 2) + { + var type = buffer.ReadByte(); + + if (type != (int)FourSevenFourLoginStatus.TypeStandard && type != (int)FourSevenFourLoginStatus.TypeReconnection) + { + _logger.Information("Failed to decode login header."); + WriteResponseCode(ctx, FourSevenFourLoginStatus.StatusLoginServerRejectedSession); + return; + } + + _reconnecting = type == (int)FourSevenFourLoginStatus.TypeReconnection; + _loginLength = buffer.ReadByte(); + SetState(LoginDecoderState.LoginPayload); + } + } + + /// + /// Decodes the payload state. + /// + /// The ctx. + /// The buffer. + /// The output. + private void DecodePayload(IChannelHandlerContext ctx, IByteBuffer buffer, List output) + { + if (buffer.ReadableBytes >= _loginLength) + { + IByteBuffer payload = buffer.ReadBytes(_loginLength); + var version = payload.ReadInt(); + + var memoryStatus = payload.ReadByte(); + if (memoryStatus != 0 && memoryStatus != 1) + { + _logger.Information("Login memoryStatus ({0}) not in expected range of [0, 1].", memoryStatus); + WriteResponseCode(ctx, FourSevenFourLoginStatus.StatusLoginServerRejectedSession); + return; + } + + var lowMemory = memoryStatus == 1; + + payload.SkipBytes(24); + payload.SkipBytes(64); + + /*var crcs = new int[Constants.ArchiveCount]; + for (var index = 0; index < Constants.ArchiveCount; index++) + { + crcs[index] = payload.ReadInt(); + }*/ + + /* var length = payload.ReadByte(); + if (length != _loginLength - 41) + { + _logger.Information("Login packet unexpected length ({0})", length); + WriteResponseCode(ctx, LoginStatus.StatusLoginServerRejectedSession); + return; + }*/ + + /* + var secureBytes = payload.ReadBytes(length); + var value = new BigInteger(secureBytes.Array.Take(length).ToArray()); + * + * RSA? + value = BigInteger.ModPow(value, Constants.RSA_MODULUS, Constants.RSA_EXPONENT); + var secureBuffer = Unpooled.WrappedBuffer(value.ToByteArray()); + */ + + + var id = payload.ReadByte(); + if (id != 10) + { + _logger.Information("Unable to read id from secure payload."); + WriteResponseCode(ctx, FourSevenFourLoginStatus.StatusLoginServerRejectedSession); + return; + } + + + var clientSeed = payload.ReadLong(); + var reportedSeed = payload.ReadLong(); + if (reportedSeed != _serverSeed) + { + _logger.Information("Reported seed differed from server seed."); + WriteResponseCode(ctx, FourSevenFourLoginStatus.StatusLoginServerRejectedSession); + return; + } + + var username = TextUtil.LongToName(payload.ReadLong()); + var password = payload.ReadString(0); + var socketAddress = (IPEndPoint)ctx.Channel.RemoteAddress; + var hostAddress = socketAddress.Address.ToString(); + + var seed = new int[4]; + seed[0] = (int)(clientSeed >> 32); + seed[1] = (int)clientSeed; + seed[2] = (int)(_serverSeed >> 32); + seed[3] = (int)_serverSeed; + + var decodingRandom = new IsaacRandom(seed); + for (int index = 0; index < seed.Length; index++) + { + seed[index] += 50; + } + + var encodingRandom = new IsaacRandom(seed); + + var credentials = new PlayerCredentials + { + Username = username, + Password = password, + EncodedUsername = _usernameHash, + Uid = -1, + HostAddress = hostAddress, + }; + + var randomPair = new IsaacRandomPair(encodingRandom, decodingRandom); + var request = new Rs2LoginRequest + { + Credentials = credentials, + RandomPair = randomPair, + Reconnecting = _reconnecting, + LowMemory = lowMemory, + ReleaseNumber = version, + ArchiveCrcs = null, + ClientVersion = version, + OnResult = (loginTask) => WriteProcessorResponseAsync(loginTask, ctx, randomPair) + }; + + _loginProcessor.Enqueue(request); + } + } + + private async Task WriteProcessorResponseAsync(Rs2LoginResponse loginResult, IChannelHandlerContext ctx, IsaacRandomPair randomPair) + { + try + { + await ctx.WriteAndFlushAsync(loginResult); + HandleLoginProcessorResponse(loginResult.Player, loginResult.Status, ctx, randomPair); + } + catch (Exception e) + { + Log.Logger.Error(e, nameof(WriteProcessorResponseAsync)); + await ctx.CloseAsync(); + } + } + + /// + /// Writes a response code to the client and closes the current channel. + /// + /// The ctx. + /// The response. + private void WriteResponseCode(IChannelHandlerContext ctx, FourSevenFourLoginStatus response) + { + var buffer = ctx.Allocator.Buffer(sizeof(byte)); + buffer.WriteByte((int)response); + ctx.WriteAndFlushAsync(buffer); + HandleLoginProcessorResponse(null, response, ctx, null); + } + + private void HandleLoginProcessorResponse(Player player, FourSevenFourLoginStatus response, IChannelHandlerContext ctx, IsaacRandomPair randomPair) + { + if (response != FourSevenFourLoginStatus.StatusOk) + { + ctx.CloseAsync(); + } + else + { + if (player == null) + { + ctx.CloseAsync(); + throw new InvalidOperationException("Cannot initialize player is null"); + } + + ctx.Channel.Pipeline.Remove(nameof(LoginEncoder)); + ctx.Channel.Pipeline.Remove(nameof(LoginDecoder)); + var gameMessageHandlers = _gameMessageProvider.Provide(); + + foreach (var gameMessageHandler in gameMessageHandlers) + { + /*if (gameMessageHandler is ICipherAwareHandler) + { + ((ICipherAwareHandler)gameMessageHandler).CipherPair = randomPair; + }*/ + + if (gameMessageHandler is IPlayerAwareHandler) + { + ((IPlayerAwareHandler)gameMessageHandler).Player = player; + } + } + ctx.Channel.Pipeline.AddLast(gameMessageHandlers); + ctx.GetAttribute(Constants.PlayerAttributeKey).SetIfAbsent(player); + player.ChannelHandlerContext = ctx; + _world.Add(player); + if (!_reconnecting) + { + _ = _playerInitializer.InitializeAsync(player); + } + else + { + player.UpdateAppearance(); + } + } + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginEncoder.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginEncoder.cs new file mode 100644 index 00000000..e214fb0e --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginEncoder.cs @@ -0,0 +1,23 @@ +using DotNetty.Buffers; +using DotNetty.Codecs; +using DotNetty.Transport.Channels; +using NetScape.Abstractions.Model.Login; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + public class LoginEncoder : MessageToByteEncoder> + { + protected override void Encode(IChannelHandlerContext context, LoginResponse message, IByteBuffer output) + { + output.WriteByte((int)message.Status); + + if (message.Status == FourSevenFourLoginStatus.StatusOk) + { + output.WriteByte(message.Rights); + output.WriteByte(message.Flagged ? 1 : 0); + output.WriteShort(message.Player.Index); + output.WriteByte(1); + } + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginProvider.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginProvider.cs new file mode 100644 index 00000000..380edc54 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Handlers/LoginProvider.cs @@ -0,0 +1,22 @@ +using Autofac; +using DotNetty.Transport.Channels; +using NetScape.Abstractions.Interfaces; +using NetScape.Abstractions.Interfaces.Login; +using System; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol.Handlers +{ + public class LoginProvider : ILoginProvider + { + private readonly IContainer _container; + + public LoginProvider(ContainerProvider containerProvider) + { + _container = containerProvider.Container; + } + + public Func Provide => () => new IChannelHandler[] { + _container.Resolve() + }; + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/HandshakeType.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/HandshakeType.cs new file mode 100644 index 00000000..b2cdf761 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/HandshakeType.cs @@ -0,0 +1,9 @@ +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public enum HandshakeType : byte + { + Default = 0, + ServiceGame = 14, + ServiceUpdate = 15 + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/JS5Request.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/JS5Request.cs new file mode 100644 index 00000000..46e7e7df --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/JS5Request.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public class JS5Request + { + public int Index { get; } + public int File { get; } + public bool Priority { get; } + public int EncryptionKey { get; } + public JS5Request(int index, int file, bool priority, int encryptionKey) + { + Index = index; + File = file; + Priority = priority; + EncryptionKey = encryptionKey; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/LoginProcessor.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/LoginProcessor.cs new file mode 100644 index 00000000..8ad990f3 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/LoginProcessor.cs @@ -0,0 +1,47 @@ +using NetScape.Abstractions.FileSystem; +using NetScape.Abstractions.Login; +using Serilog; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public class LoginProcessor : DefaultLoginProcessor + { + private readonly IPlayerSerializer _playerSerializer; + + public LoginProcessor(ILogger logger, IPlayerSerializer playerSerializer) : base(logger) + { + _playerSerializer = playerSerializer; + } + + /// + /// Processes a single by retriving the player from + /// + /// The login request. + /// + protected override async Task ProcessAsync(Rs2LoginRequest request) + { + var password = request.Credentials.Password; + var username = request.Credentials.Username; + _logger.Information("Pending login from {0}", username); + + if (password.Length < 4 || password.Length > 20 || string.IsNullOrEmpty(username) || username.Length > 12) + { + _logger.Information("Username ('{0}') or password did not pass validation.", username); + return new Rs2LoginResponse { Status = FourSevenFourLoginStatus.StatusInvalidCredentials }; + } + + var playerInDatabase = await _playerSerializer.GetAsync(username); + + if (playerInDatabase != null && !playerInDatabase.Password.Equals(password)) + { + return new Rs2LoginResponse { Status = FourSevenFourLoginStatus.StatusInvalidCredentials }; + } + + var createdNewPlayer = playerInDatabase == null; + + var player = await _playerSerializer.GetOrCreateAsync(request.Credentials); + return new Rs2LoginResponse { Status = FourSevenFourLoginStatus.StatusOk, Player = player, Created = createdNewPlayer }; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/NetScape.Modules.FourSevenFour.LoginProtocol.csproj b/NetScape.Modules.FourSevenFour.LoginProtocol/NetScape.Modules.FourSevenFour.LoginProtocol.csproj new file mode 100644 index 00000000..f884af6b --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/NetScape.Modules.FourSevenFour.LoginProtocol.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp5.0 + + + + + + + + + + + + + diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginRequest.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginRequest.cs new file mode 100644 index 00000000..aa34b909 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginRequest.cs @@ -0,0 +1,10 @@ +using NetScape.Abstractions.Model.Login; +using System; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public record Rs2LoginRequest : LoginRequest + { + } +} diff --git a/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginResponse.cs b/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginResponse.cs new file mode 100644 index 00000000..4a3d7575 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.LoginProtocol/Rs2LoginResponse.cs @@ -0,0 +1,8 @@ +using NetScape.Abstractions.Model.Login; + +namespace NetScape.Modules.FourSevenFour.LoginProtocol +{ + public class Rs2LoginResponse : LoginResponse + { + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/FourSevenFourUpdatingModule.cs b/NetScape.Modules.FourSevenFour.World.Updating/FourSevenFourUpdatingModule.cs new file mode 100644 index 00000000..3509df5d --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/FourSevenFourUpdatingModule.cs @@ -0,0 +1,16 @@ +using Autofac; +using NetScape.Abstractions.Interfaces.World.Updating; +using NetScape.Abstractions.Model.Game; + +namespace NetScape.Modules.FourSevenFour.World.Updating +{ + public class FourSevenFourUpdatingModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType() + .As>(); + base.Load(builder); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Messages/PlayerSynchronizationMessage.cs b/NetScape.Modules.FourSevenFour.World.Updating/Messages/PlayerSynchronizationMessage.cs new file mode 100644 index 00000000..551a4913 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Messages/PlayerSynchronizationMessage.cs @@ -0,0 +1,256 @@ +using DotNetty.Buffers; +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.Game; +using NetScape.Abstractions.Model.World.Updating.Blocks; +using NetScape.Modules.FourSevenFour.World.Updating.Segements; +using NetScape.Modules.Messages; +using NetScape.Modules.Messages.Builder; +using System.Collections.Generic; + +namespace NetScape.Modules.FourSevenFour.World.Updating +{ + public class PlayerSynchronizationMessage : IEncoderMessage + { + public Position LastKnownRegion { get; } + public int LocalPlayers { get; } + public Position Position { get; } + public bool RegionChanged { get; } + public SynchronizationSegment Segment { get; } + public List Segments { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The last known region. + /// The players current position. + /// if set to true [region changed]. + /// The current player's synchronization segment. + /// The number local players. + /// The list of segments. + public PlayerSynchronizationMessage(Position lastKnownRegion, Position position, bool regionChanged, SynchronizationSegment segment, int localPlayers, List segments) + { + LastKnownRegion = lastKnownRegion; + Position = position; + RegionChanged = regionChanged; + Segment = segment; + LocalPlayers = localPlayers; + Segments = segments; + } + + + public MessageFrame ToMessage(IByteBufferAllocator alloc) + { + var bldr = new MessageFrameBuilder(alloc, 150, FrameType.VariableShort); + bldr.SwitchToBitAccess(); + + var blockBuilder = new MessageFrameBuilder(alloc); + PutMovementUpdate(Segment, bldr); + PutBlocks(Segment, blockBuilder); + bldr.PutBits(8, LocalPlayers); + + foreach (SynchronizationSegment segment in Segments) + { + SegmentType type = segment.Type; + if (type == SegmentType.Remove_Mob) + { + PutRemovePlayerUpdate(bldr); + } + else if (type == SegmentType.Add_Mob) + { + PutAddPlayerUpdate((AddPlayerSegment)segment, bldr); + PutBlocks(segment, blockBuilder); + } + else + { + PutMovementUpdate(segment, bldr); + PutBlocks(segment, blockBuilder); + } + } + + if (blockBuilder.GetLength() > 0) + { + bldr.PutBits(11, 2047); + bldr.SwitchToByteAccess(); + bldr.PutRawBuilder(blockBuilder); + } + else + { + bldr.SwitchToByteAccess(); + } + + return bldr.ToMessageFrame(); + + } + + private void PutAddPlayerUpdate(AddPlayerSegment seg, MessageFrameBuilder builder) + { + var updateRequired = seg.BlockSet.Size() > 0; + Position player = Position; + Position other = seg.Position; + builder.PutBits(11, seg.Index); + builder.PutBits(1, updateRequired ? 1 : 0); + builder.PutBits(3, seg.Direction.IntValue); + builder.PutBits(5, other.X - player.X); + builder.PutBits(1, 1); + builder.PutBits(5, other.Y - player.Y); + } + + private void PutRemovePlayerUpdate(MessageFrameBuilder builder) + { + builder.PutBits(1, 1); + builder.PutBits(2, 3); + } + + private void PutBlocks(SynchronizationSegment segment, MessageFrameBuilder builder) + { + var blockSet = segment.BlockSet; + if (blockSet.Size() > 0) + { + int mask = 0; + if (blockSet.Contains()) + { + mask |= 0x8; + } + + if (blockSet.Contains()) + { + mask |= 0x10; + } + + if (mask >= 0x100) + { + mask |= 0x20; + builder.Put(MessageType.Byte, (mask & 0xFF)); + builder.Put(MessageType.Byte, (mask >> 8)); + } + else + { + builder.Put(MessageType.Byte, mask); + } + + if (blockSet.Contains()) + { + PutAnimationBlock(blockSet.Get(), builder); + } + + if (blockSet.Contains()) + { + PutAppearanceBlock(blockSet.Get(), builder); + } + } + } + + private void PutAppearanceBlock(AppearanceBlock block, MessageFrameBuilder builder) + { + Appearance appearance = block.Appearance; + var playerProperties = new MessageFrameBuilder(builder.Alloc); + + playerProperties.Put(MessageType.Byte, (int)appearance.Gender); + playerProperties.Put(MessageType.Byte, 0); + playerProperties.Put(MessageType.Byte, 0); + + if (block.NpcId > 0) + { + playerProperties.Put(MessageType.Byte, 255); + playerProperties.Put(MessageType.Byte, 255); + playerProperties.Put(MessageType.Short, block.NpcId); + } + else + { + //Inventory equipment = block.getEquipment(); + int[] style = appearance.Style; + //Item item, chest, helm; + + for (int slot = 0; slot < 4; slot++) + { + playerProperties.Put(MessageType.Byte, 0); + } + + playerProperties.Put(MessageType.Short, 0x100 + style[2]); //Chest + playerProperties.Put(MessageType.Byte, 0); //Shield + playerProperties.Put(MessageType.Short, 0x100 + style[3]); //Arms + playerProperties.Put(MessageType.Short, 0x100 + style[5]); //Legs + playerProperties.Put(MessageType.Short, 0x100 + style[0]); //Helm + playerProperties.Put(MessageType.Short, 0x100 + style[4]); //Hands + playerProperties.Put(MessageType.Short, 0x100 + style[6]); //Feet + playerProperties.Put(MessageType.Short, 0x100 + style[1]); //Beard + } + + int[] colors = appearance.Colors; + foreach (int color in colors) + { + playerProperties.Put(MessageType.Byte, color); + } + + playerProperties.Put(MessageType.Short, 0x328); // stand + playerProperties.Put(MessageType.Short, 0x337); // stand turn + playerProperties.Put(MessageType.Short, 0x333); // walk + playerProperties.Put(MessageType.Short, 0x334); // turn 180 + playerProperties.Put(MessageType.Short, 0x335); // turn 90 cw + playerProperties.Put(MessageType.Short, 0x336); // turn 90 ccw + playerProperties.Put(MessageType.Short, 0x338); // run + + playerProperties.Put(MessageType.Long, block.Name); + playerProperties.Put(MessageType.Byte, block.Combat); + playerProperties.Put(MessageType.Short, block.Skill); + + builder.Put(MessageType.Byte, DataTransformation.Add, playerProperties.GetLength()); + builder.PutRawBuilder(playerProperties); + } + + private static void PutAnimationBlock(AnimationBlock block, MessageFrameBuilder builder) + { + var animation = block.Animation; + builder.Put(MessageType.Short, DataOrder.Little, animation.Id); + builder.Put(MessageType.Byte, DataTransformation.Negate, animation.Delay); + + } + + private void PutMovementUpdate(SynchronizationSegment seg, MessageFrameBuilder builder) + { + bool updateRequired = seg.BlockSet.Size() > 0; + if (seg.Type == SegmentType.Teleport) + { + var teleportSeg = ((TeleportSegment)seg); + Position position = teleportSeg.Destination; + builder.PutBits(1, 1); + builder.PutBits(2, 3); + builder.PutBits(1, RegionChanged ? 0 : 1); + builder.PutBits(1, updateRequired ? 1 : 0); + builder.PutBits(7, position.GetLocalX(LastKnownRegion)); + builder.PutBits(2, position.Height); + builder.PutBits(7, position.GetLocalY(LastKnownRegion)); + } + else if (seg.Type == SegmentType.Run) + { + Direction[] directions = ((MovementSegment)seg).Directions; + builder.PutBits(1, 1); + builder.PutBits(2, 2); + builder.PutBits(3, directions[0].IntValue); + builder.PutBits(3, directions[1].IntValue); + builder.PutBits(1, updateRequired ? 1 : 0); + } + else if (seg.Type == SegmentType.Walk) + { + Direction[] directions = ((MovementSegment)seg).Directions; + builder.PutBits(1, 1); + builder.PutBits(2, 1); + builder.PutBits(3, directions[0].IntValue); + builder.PutBits(1, updateRequired ? 1 : 0); + } + else + { + if (updateRequired) + { + builder.PutBits(1, 1); + builder.PutBits(2, 0); + } + else + { + builder.PutBits(1, 0); + } + } + } + + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/NetScape.Modules.FourSevenFour.World.Updating.csproj b/NetScape.Modules.FourSevenFour.World.Updating/NetScape.Modules.FourSevenFour.World.Updating.csproj new file mode 100644 index 00000000..935fe461 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/NetScape.Modules.FourSevenFour.World.Updating.csproj @@ -0,0 +1,14 @@ + + + + netcoreapp5.0 + + + + + + + + + + diff --git a/NetScape.Modules.FourSevenFour.World.Updating/PlayerUpdater.cs b/NetScape.Modules.FourSevenFour.World.Updating/PlayerUpdater.cs new file mode 100644 index 00000000..282d3cc6 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/PlayerUpdater.cs @@ -0,0 +1,262 @@ +using NetScape.Abstractions.Interfaces.Messages; +using NetScape.Abstractions.Interfaces.Region; +using NetScape.Abstractions.Interfaces.World.Updating; +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.Game; +using NetScape.Abstractions.Model.Region; +using NetScape.Abstractions.Model.World.Updating; +using NetScape.Abstractions.Model.World.Updating.Blocks; +using NetScape.Modules.FourSevenFour.Game.Messages.Encoders; +using NetScape.Modules.FourSevenFour.World.Updating.Segements; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace NetScape.Modules.FourSevenFour.World.Updating +{ + public class PlayerUpdater : IEntityUpdater + { + private readonly IRegionRepository _regionRepository; + private readonly WalkingQueueHandler _walkingQueueHandler; + private readonly IProtoMessageSender _protoMessageSender; + private static readonly int MaximumLocalPlayers = 255; + + /// + /// The maximum number of players to load per cycle. This prevents the update packet from becoming too large (the + /// client uses a 5000 byte buffer) and also stops old spec PCs from crashing when they login or teleport. + /// + private static readonly int NewPlayersPerCycle = 20; + + public PlayerUpdater(IRegionRepository regionRepository, WalkingQueueHandler walkingQueueHandler, IProtoMessageSender protoMessageSender) + { + _regionRepository = regionRepository; + _walkingQueueHandler = walkingQueueHandler; + _protoMessageSender = protoMessageSender; + } + + public Task PostUpdateAsync(Player player) + { + player.IsTeleporting = false; + player.RegionChanged = false; + player.BlockSet = new SynchronizationBlockSet(); + + if (!player.ExcessivePlayers) + { + player.IncrementViewingDistance(); + } + else + { + player.DecrementViewingDistance(); + player.ExcessivePlayers = false; + } + return Task.CompletedTask; + } + + private bool IsRegionUpdateRequired(Player player) + { + Position current = player.Position; + Position last = player.LastKnownRegion; + + int deltaX = current.GetLocalX(last); + int deltaY = current.GetLocalY(last); + + return deltaX <= Position.MaxDistance || deltaX >= IRegion.Viewport_Width - Position.MaxDistance - 1 + || deltaY <= Position.MaxDistance || deltaY >= IRegion.Viewport_Width - Position.MaxDistance - 1; + } + + + public async Task PreUpdateAsync(Player player, Dictionary> encodes, + Dictionary> updates) + { + Position old = player.Position; + _walkingQueueHandler.Pulse(player); + var local = true; + + if (player.IsTeleporting) + { + player.ResetViewingDistance(); + local = false; + } + + Position position = player.Position; + if (!player.HasLastKnownRegion() || IsRegionUpdateRequired(player)) + { + player.RegionChanged = true; + local = false; + player.LastKnownRegion = position; + await player.SendAsync(new SendMapRegionMessage(player)); + } + + var oldViewable = _regionRepository.FromPosition(old).GetSurrounding(); + var newViewable = _regionRepository.FromPosition(position).GetSurrounding(); + + var differences = newViewable.ToHashSet(); + differences.RemoveWhere(t => !oldViewable.Contains(t)); + + var full = newViewable.ToHashSet(); + if (local) + { + full.RemoveWhere(t => oldViewable.Contains(t)); + } + + await SendUpdates(player, player.LastKnownRegion, differences, full, encodes, updates); + } + + /// + /// Sends the updates for a + /// + /// The player. + /// The position. + /// The differences. + /// The HashSet of of regions that require a full update. + /// The HashSet of of regions that changed. + /// The HashSet of of regions that require a update. + private async Task SendUpdates(Player player, Position position, HashSet differences, HashSet full, Dictionary> encodes, Dictionary> updates) + { + IRegionRepository repository = _regionRepository; + int height = position.Height; + + foreach (RegionCoordinates coordinates in differences) + { + var updatesMsgs = repository.Get(coordinates).GetUpdates(height); + var messages = updates.TryAdd(coordinates, updatesMsgs); + + /*if (messages) + { + await player.SendAsync(new GroupedRegionUpdateMessage(position, coordinates, updatesMsgs)); + }*/ + } + + foreach (RegionCoordinates coordinates in full) + { + var addMessages = repository.Get(coordinates).Encode(height); + var added = encodes.TryAdd(coordinates, addMessages); + + if (added) + { + //await _protoMessageSender.SendAsync(player, new ClearRegionMessage { LocalX = position.LocalX, LocalY = position.LocalX }); + await player.SendAsync(new SendMapRegionMessage(player)); + //await player.SendAsync(new GroupedRegionUpdateMessage(position, coordinates, addMessages)); + } + } + } + + /// + /// Determines whether [player has cached appearance] [the specified appearance tickets]. + /// + /// The appearance tickets. + /// The index of the player. + /// The appearance ticket. + /// + /// true if [player has cached appearance] [the specified appearance tickets]; otherwise, false. + /// + private bool HasCachedAppearance(int[] appearanceTickets, int index, int appearanceTicket) + { + if (appearanceTickets[index] != appearanceTicket) + { + appearanceTickets[index] = appearanceTicket; + return false; + } + + return true; + } + + /// + /// Returns whether or not the specified should be removed. + /// + /// The position of the Player being updated. + /// The distance. + /// The Player being tested. + /// + private bool Removeable(Position position, int distance, Player other) + { + if (other.IsTeleporting || !other.IsActive) + { + return true; + } + + Position otherPosition = other.Position; + return otherPosition.GetLongestDelta(position) > distance || !otherPosition.IsWithinDistance(position, distance); + } + + public async Task UpdateAsync(Player player) + { + Position lastKnownRegion = player.LastKnownRegion; + var regionChanged = player.RegionChanged; + int[] appearanceTickets = player.AppearanceTickets; + + SynchronizationBlockSet blockSet = player.BlockSet; + + Position position = player.Position; + + SynchronizationSegment segment = (player.IsTeleporting || player.RegionChanged) ? + new TeleportSegment(blockSet, position) : new MovementSegment(blockSet, player.GetDirections()); + + List localPlayers = player.LocalPlayerList; + int oldCount = localPlayers.Count; + + List segments = new(); + int distance = player.ViewingDistance; + + foreach (var other in localPlayers.ToList()) + { + if (Removeable(position, distance, other)) + { + localPlayers.Remove(other); + segments.Add(new RemoveMobSegment()); + } + else + { + segments.Add(new MovementSegment(other.BlockSet, other.GetDirections())); + } + } + + int added = 0, count = localPlayers.Count(); + + IRegion current = _regionRepository.FromPosition(position); + HashSet regions = current.GetSurrounding(); + regions.Add(current.Coordinates); + + IEnumerable players = regions.Select(t => _regionRepository.Get(t)) + .SelectMany(region => region.GetEntities(EntityType.Player)); + + foreach (var other in players) + { + if (count >= MaximumLocalPlayers) + { + player.ExcessivePlayers = true; + break; + } + else if (added >= NewPlayersPerCycle) + { + break; + } + + Position local = other.Position; + + if (other != player && local.IsWithinDistance(position, distance) && !localPlayers.Contains(other)) + { + localPlayers.Add(other); + count++; + added++; + + blockSet = other.BlockSet; + + int index = other.Index; + + if (!blockSet.Contains() && !HasCachedAppearance(appearanceTickets, index - 1, other.AppearanceTicket)) + { + blockSet = (SynchronizationBlockSet)blockSet.Clone(); + blockSet.Add(SynchronizationBlock.CreateAppearanceBlock(other)); + } + + segments.Add(new AddPlayerSegment(blockSet, index, local, other.LastDirection)); + } + } + + PlayerSynchronizationMessage message = new PlayerSynchronizationMessage(lastKnownRegion, position, + regionChanged, segment, oldCount, segments); + await player.SendAsync(message); + } + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/AddPlayerSegment.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/AddPlayerSegment.cs new file mode 100644 index 00000000..0a8a79cd --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/AddPlayerSegment.cs @@ -0,0 +1,18 @@ +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.World.Updating; + +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public class AddPlayerSegment : SynchronizationSegment + { + public override SegmentType Type => SegmentType.Add_Mob; + public int Index { get; } + public Position Position { get; } + public Direction Direction { get; } + public AddPlayerSegment(SynchronizationBlockSet blockSet, int index, Position position, Direction direction) : base(blockSet) + { + Index = index; + Position = position; + } + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/MovementSegment.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/MovementSegment.cs new file mode 100644 index 00000000..c8a04ad3 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/MovementSegment.cs @@ -0,0 +1,34 @@ +using Dawn; +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.World.Updating; +using System; + +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public class MovementSegment : SynchronizationSegment + { + public Direction[] Directions { get; } + public override SegmentType Type => GetSegmentType(); + + public MovementSegment(SynchronizationBlockSet blockSet, Direction[] directions) : base(blockSet) + { + Guard.Argument(directions.Length, nameof(Directions)).GreaterThan(-1).LessThan(3); + Directions = directions; + } + + private SegmentType GetSegmentType() + { + switch (Directions.Length) + { + case 0: + return SegmentType.No_Movement; + case 1: + return SegmentType.Walk; + case 2: + return SegmentType.Run; + default: + throw new InvalidOperationException("Direction type not supported"); + } + } + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/RemoveMobSegment.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/RemoveMobSegment.cs new file mode 100644 index 00000000..5046b924 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/RemoveMobSegment.cs @@ -0,0 +1,16 @@ +using NetScape.Abstractions.Model.World.Updating; + +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public class RemoveMobSegment : SynchronizationSegment + { + private static readonly SynchronizationBlockSet EmptyBlockSet = new SynchronizationBlockSet(); + + public RemoveMobSegment() : base(EmptyBlockSet) + { + + } + + public override SegmentType Type => SegmentType.Remove_Mob; + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/SegmentType.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/SegmentType.cs new file mode 100644 index 00000000..6d2e65f2 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/SegmentType.cs @@ -0,0 +1,12 @@ +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public enum SegmentType + { + Add_Mob, + No_Movement, + Remove_Mob, + Run, + Teleport, + Walk + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/SynchronizationSegment.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/SynchronizationSegment.cs new file mode 100644 index 00000000..36942a2d --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/SynchronizationSegment.cs @@ -0,0 +1,16 @@ +using NetScape.Abstractions.Model.World.Updating; + +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public abstract class SynchronizationSegment + { + public SynchronizationBlockSet BlockSet { get; } + + public SynchronizationSegment(SynchronizationBlockSet blockSet) + { + BlockSet = blockSet; + } + + public abstract SegmentType Type { get; } + } +} diff --git a/NetScape.Modules.FourSevenFour.World.Updating/Segments/TeleportSegment.cs b/NetScape.Modules.FourSevenFour.World.Updating/Segments/TeleportSegment.cs new file mode 100644 index 00000000..43f53215 --- /dev/null +++ b/NetScape.Modules.FourSevenFour.World.Updating/Segments/TeleportSegment.cs @@ -0,0 +1,16 @@ +using NetScape.Abstractions.Model; +using NetScape.Abstractions.Model.World.Updating; + +namespace NetScape.Modules.FourSevenFour.World.Updating.Segements +{ + public class TeleportSegment : SynchronizationSegment + { + public Position Destination { get; } + public TeleportSegment(SynchronizationBlockSet blockSet, Position dest) : base(blockSet) + { + Destination = dest; + } + + public override SegmentType Type => SegmentType.Teleport; + } +} diff --git a/NetScape.Modules.Messages.Builder/FrameType.cs b/NetScape.Modules.Messages.Builder/FrameType.cs index 55a1b7fa..fd9f33ad 100644 --- a/NetScape.Modules.Messages.Builder/FrameType.cs +++ b/NetScape.Modules.Messages.Builder/FrameType.cs @@ -2,6 +2,6 @@ { public enum FrameType { - Raw, Fixed, VariableByte, VariableShort + Raw, Fixed, VariableByte, VariableShort, ReadAll } } diff --git a/NetScape.Modules.Messages.Builder/FrameTypeExtensions.cs b/NetScape.Modules.Messages.Builder/FrameTypeExtensions.cs index adb4418c..b9bd0423 100644 --- a/NetScape.Modules.Messages.Builder/FrameTypeExtensions.cs +++ b/NetScape.Modules.Messages.Builder/FrameTypeExtensions.cs @@ -8,6 +8,8 @@ public static int GetBytes(this FrameType frameType, DotNetty.Buffers.IByteBuffe { switch (frameType) { + case FrameType.ReadAll: + return input.ReadableBytes; case FrameType.VariableByte: return input.ReadByte(); case FrameType.VariableShort: diff --git a/NetScape.Modules.Messages/MessageFrameEncoder.cs b/NetScape.Modules.Messages/MessageFrameEncoder.cs index 8bc53deb..bcd7fa06 100644 --- a/NetScape.Modules.Messages/MessageFrameEncoder.cs +++ b/NetScape.Modules.Messages/MessageFrameEncoder.cs @@ -19,8 +19,8 @@ protected override void Encode(IChannelHandlerContext context, MessageFrame fram IByteBuffer payload = frame.Payload; int opcode = frame.Id; - var isaacValue = CipherPair.EncodingRandom.NextInt(); - opcode = opcode + isaacValue & 0xFF; + int? isaacValue = CipherPair?.EncodingRandom?.NextInt() ?? null; + opcode = isaacValue.HasValue ? opcode + isaacValue.Value & 0xFF : opcode & 0XFF; output.WriteByte(opcode); if (type == FrameType.VariableByte) diff --git a/NetScape.Modules.Messages/MessageHeaderDecoder.cs b/NetScape.Modules.Messages/MessageHeaderDecoder.cs index d95b6411..4d583f4d 100644 --- a/NetScape.Modules.Messages/MessageHeaderDecoder.cs +++ b/NetScape.Modules.Messages/MessageHeaderDecoder.cs @@ -28,9 +28,9 @@ public MessageHeaderDecoder(IMessageDecoder[] messageDecoders, ProtoMessageCodec protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List output) { - var isaacValue = CipherPair.DecodingRandom.NextInt(); + int? isaacValue = CipherPair?.DecodingRandom?.NextInt() ?? null; int opcode = input.ReadByte(); - int unencodedOpcode = opcode - isaacValue & 0xFF; + int unencodedOpcode = isaacValue.HasValue ? opcode - isaacValue.Value & 0xFF : opcode & 0XFF; var protoCodec = _protoMessageCodecHandler.DecoderCodecs.ContainsKey(unencodedOpcode) ? _protoMessageCodecHandler.DecoderCodecs[unencodedOpcode] : null; @@ -89,7 +89,7 @@ protected override void Decode(IChannelHandlerContext context, IByteBuffer input } } - Log.Logger.Debug("Message Recieved: {0} TypeName: {1} Player: {2}", message, protoDecoder.TypeName, Player.Username); + Log.Logger.Debug("Message Recieved: {0} TypeName: {1} Player: {2} Size: {3} Opcode: {4}", message, protoDecoder.TypeName, Player.Username, size, unencodedOpcode); protoDecoder.Publish(Player, message); } } diff --git a/NetScape.Modules.Messages/ProtoMessageExtensions.cs b/NetScape.Modules.Messages/ProtoMessageExtensions.cs index 9dbec150..af30536a 100644 --- a/NetScape.Modules.Messages/ProtoMessageExtensions.cs +++ b/NetScape.Modules.Messages/ProtoMessageExtensions.cs @@ -65,6 +65,8 @@ public static FrameType GetFrameType(this FrameSizeType sizeType) { switch (sizeType) { + case FrameSizeType.ReadAll: + return FrameType.ReadAll; case FrameSizeType.FixedByte: return FrameType.Fixed; case FrameSizeType.Raw: diff --git a/NetScape.Modules.Messages/Protos/MessageCodec.proto b/NetScape.Modules.Messages/Protos/MessageCodec.proto index 89114738..a69d9ad6 100644 --- a/NetScape.Modules.Messages/Protos/MessageCodec.proto +++ b/NetScape.Modules.Messages/Protos/MessageCodec.proto @@ -8,6 +8,7 @@ enum FrameSizeType { Raw = 1; VariableByte = 2; VariableShort = 3; + ReadAll = 4; } enum FieldType { @@ -141,17 +142,17 @@ message ThreeOneSevenEncoderMessages { } message AddGlobalTileItemMessage { - required uint32 ItemId = 1 [(FieldCodecExt).Type = Short, (FieldCodecExt).Transform = Subtract]; + required int32 ItemId = 1 [(FieldCodecExt).Type = Short, (FieldCodecExt).Transform = Subtract]; required int32 PositionOffset = 2 [(FieldCodecExt).Type = Byte, (FieldCodecExt).Transform = Subtract]; - required uint32 PlayerIndex = 3 [(FieldCodecExt).Type = Short, (FieldCodecExt).Transform = Add]; - required uint32 Amount = 4 [(FieldCodecExt).Type = Short]; + required int32 PlayerIndex = 3 [(FieldCodecExt).Type = Short, (FieldCodecExt).Transform = Add]; + required int32 Amount = 4 [(FieldCodecExt).Type = Short]; option (MessageCodecExt).OpCodes = 215; option (IsEncoder) = true; } message AddTileItemMessage { - required uint32 ItemId = 1 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little, (FieldCodecExt).Transform = Add]; - required uint32 Amount = 2 [(FieldCodecExt).Type = Short]; + required int32 ItemId = 1 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little, (FieldCodecExt).Transform = Add]; + required int32 Amount = 2 [(FieldCodecExt).Type = Short]; required int32 PositionOffset = 3 [(FieldCodecExt).Type = Byte]; option (MessageCodecExt).OpCodes = 44; option (IsEncoder) = true; @@ -171,8 +172,92 @@ message ThreeOneSevenEncoderMessages { message IdAssignmentMessage { required bool IsMembers = 1 [(FieldCodecExt).Type = Byte, (FieldCodecExt).Transform = Add]; - required uint32 NewId = 2 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little, (FieldCodecExt).Transform = Add]; + required int32 NewId = 2 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little, (FieldCodecExt).Transform = Add]; option (MessageCodecExt).OpCodes = 249; option (IsEncoder) = true; } +} + +message FourSevenFourEncoderMessages { + message LogoutMessage { + option (MessageCodecExt).OpCodes = 166; + option (IsEncoder) = true; + } + + message OpenInterfaceMessage { + required bool Walkable = 1 [(FieldCodecExt).Type = Byte, (FieldCodecExt).Transform = Add]; + required int32 InterfaceId = 2 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little]; + required int32 Position = 3 [(FieldCodecExt).Type = Short]; + required int32 Window = 4 [(FieldCodecExt).Type = Short]; + option (MessageCodecExt).OpCodes = 17; + option (IsEncoder) = true; + } + + message SendInterfaceMessage { + required int32 InterfaceId = 1 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little]; + option (MessageCodecExt).OpCodes = 251; + option (IsEncoder) = true; + } +} + +message FourSevenFourDecoderMessages { + message ChangeRegionMessage { + option (MessageCodecExt).OpCodes = 176; + } + + message WalkingQueueMessage { + required bool Run = 1; + repeated int32 X = 2; + repeated int32 Y = 3; + + option (MessageCodecExt).OpCodes = 11; + option (MessageCodecExt).OpCodes = 46; + option (MessageCodecExt).OpCodes = 59; + option (MessageCodecExt).SizeType = VariableByte; + option (MessageCodecExt).Custom = true; + } + + message FirstClickInterfaceOption { + required int32 InterfaceId = 1 [(FieldCodecExt).Type = Short]; + required int32 ButtonId = 2 [(FieldCodecExt).Type = Short]; + required int32 Slot = 3 [(FieldCodecExt).Type = Short]; + option (MessageCodecExt).OpCodes = 234; + } + + message LandscapeChangeMessage { + option (MessageCodecExt).OpCodes = 150; + required int32 LandscapeId = 1 [(FieldCodecExt).Type = Int, (FieldCodecExt).Order = Little]; + } + + message UnknownMessageOne { + option (MessageCodecExt).SizeType = ReadAll; + option (MessageCodecExt).OpCodes = 149; + option (MessageCodecExt).OpCodes = 34; + option (MessageCodecExt).OpCodes = 169; + } + + message ClickScreenMessage { + option (MessageCodecExt).OpCodes = 167; + required int32 ScreenPackedInt = 1 [(FieldCodecExt).Type = Int]; + } + + message ClickButtonMessage { + option (MessageCodecExt).OpCodes = 215; + required int32 InterfaceId = 1 [(FieldCodecExt).Type = Short]; + required int32 ButtonId = 2 [(FieldCodecExt).Type = Short]; + } + + message QuietMessage { + option (MessageCodecExt).OpCodes = 8; + } + + message CloseInterfaceMessage { + required int32 InterfaceId = 1 [(FieldCodecExt).Type = Int, (FieldCodecExt).Order = Little]; + required int32 ChildId = 2 [(FieldCodecExt).Type = Short, (FieldCodecExt).Order = Little]; + option (MessageCodecExt).OpCodes = 64; + } + + message PingMessage { + option (MessageCodecExt).OpCodes = 186; + } } \ No newline at end of file diff --git a/NetScape.Modules.ThreeOneSeven.Game/Messages/Handlers/WalkingQueueMessageHandler.cs b/NetScape.Modules.ThreeOneSeven.Game/Messages/Handlers/WalkingQueueMessageHandler.cs index 506dfbfd..4b465ed9 100644 --- a/NetScape.Modules.ThreeOneSeven.Game/Messages/Handlers/WalkingQueueMessageHandler.cs +++ b/NetScape.Modules.ThreeOneSeven.Game/Messages/Handlers/WalkingQueueMessageHandler.cs @@ -1,11 +1,12 @@ using NetScape.Abstractions.Model; -using NetScape.Abstractions.Model.Game.Walking; +using NetScape.Abstractions.Model.Game; using NetScape.Abstractions.Model.Messages; using NetScape.Modules.Messages; using NetScape.Modules.Messages.Models; using System.Linq; -namespace NetScape.Modules.ThreeOneSeven.Game.Messages.Handlers { +namespace NetScape.Modules.ThreeOneSeven.Game.Messages.Handlers +{ [MessageHandler] public class WalkingQueueMessageHandler @@ -21,7 +22,7 @@ public void OnWalkQueueMessage(DecoderMessage new Position { X = message.X[t], diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginDecoder.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginDecoder.cs index 6462d56c..15d15467 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginDecoder.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginDecoder.cs @@ -15,7 +15,6 @@ using Serilog; using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Threading.Tasks; @@ -95,7 +94,7 @@ private void DecodeHandshake(IChannelHandlerContext ctx, IByteBuffer buffer, Lis _serverSeed = Random.NextLong(); var response = ctx.Allocator.Buffer(17); - response.WriteByte((int)LoginStatus.StatusExchangeData); + response.WriteByte((int)ThreeOneSevenLoginStatus.StatusExchangeData); response.WriteLong(0); response.WriteLong(_serverSeed); ctx.Channel.WriteAndFlushAsync(response); @@ -115,14 +114,14 @@ private void DecodeHeader(IChannelHandlerContext ctx, IByteBuffer buffer, List /// The ctx. /// The response. - private void WriteResponseCode(IChannelHandlerContext ctx, LoginStatus response) + private void WriteResponseCode(IChannelHandlerContext ctx, ThreeOneSevenLoginStatus response) { var buffer = ctx.Allocator.Buffer(sizeof(byte)); buffer.WriteByte((int)response); @@ -268,9 +267,9 @@ private void WriteResponseCode(IChannelHandlerContext ctx, LoginStatus response) HandleLoginProcessorResponse(null, response, ctx, null); } - private void HandleLoginProcessorResponse(Player player, LoginStatus response, IChannelHandlerContext ctx, IsaacRandomPair randomPair) + private void HandleLoginProcessorResponse(Player player, ThreeOneSevenLoginStatus response, IChannelHandlerContext ctx, IsaacRandomPair randomPair) { - if (response != LoginStatus.StatusOk) + if (response != ThreeOneSevenLoginStatus.StatusOk) { ctx.CloseAsync(); } @@ -305,7 +304,8 @@ private void HandleLoginProcessorResponse(Player player, LoginStatus response, I if (!_reconnecting) { _ = _playerInitializer.InitializeAsync(player); - } else + } + else { player.UpdateAppearance(); } diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginEncoder.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginEncoder.cs index b45c68d0..9d95be68 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginEncoder.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Handlers/LoginEncoder.cs @@ -5,13 +5,13 @@ namespace NetScape.Modules.ThreeOneSeven.LoginProtocol.Handlers { - public class LoginEncoder : MessageToByteEncoder> + public class LoginEncoder : MessageToByteEncoder> { - protected override void Encode(IChannelHandlerContext context, LoginResponse message, IByteBuffer output) + protected override void Encode(IChannelHandlerContext context, LoginResponse message, IByteBuffer output) { - output.WriteByte((int) message.Status); + output.WriteByte((int)message.Status); - if (message.Status == LoginStatus.StatusOk) + if (message.Status == ThreeOneSevenLoginStatus.StatusOk) { output.WriteByte(message.Rights); output.WriteByte(message.Flagged ? 1 : 0); diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginProcessor.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginProcessor.cs index 2b251a5d..d250e189 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginProcessor.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginProcessor.cs @@ -1,59 +1,26 @@ -using Autofac; -using NetScape.Abstractions.FileSystem; -using NetScape.Abstractions.Interfaces.Login; +using NetScape.Abstractions.FileSystem; +using NetScape.Abstractions.Login; using Serilog; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; using System.Threading.Tasks; namespace NetScape.Modules.ThreeOneSeven.LoginProtocol { - public class LoginProcessor : ILoginProcessor, IStartable, IDisposable + + public class LoginProcessor : DefaultLoginProcessor { - private readonly ILogger _logger; private readonly IPlayerSerializer _playerSerializer; - private readonly IList _loginRequests = new List(); - - private readonly object _lockObject = new object(); - private CancellationToken _cancellationToken; - private CancellationTokenSource _cancellationTokenSource; - - public LoginProcessor(ILogger logger, IPlayerSerializer playerSerializer) + public LoginProcessor(ILogger logger, IPlayerSerializer playerSerializer) : base(logger) { - _logger = logger; _playerSerializer = playerSerializer; } - /// - /// Enqueues the specified request. - /// - /// The request. - /// Login already exists - public void Enqueue(Rs2LoginRequest request) - { - lock (_lockObject) - { - var loginExists = _loginRequests.Any(t => t.Credentials.Username.Equals(request.Credentials.Username, StringComparison.InvariantCultureIgnoreCase) - && t.Credentials.Password.Equals(request.Credentials.Password, StringComparison.InvariantCultureIgnoreCase)); - - if (loginExists) - { - throw new InvalidOperationException("Login already exists"); - } - - _loginRequests.Add(request); - } - } - /// /// Processes a single by retriving the player from /// /// The login request. /// - private async Task ProcessAsync(Rs2LoginRequest request) + protected override async Task ProcessAsync(Rs2LoginRequest request) { var password = request.Credentials.Password; var username = request.Credentials.Username; @@ -62,70 +29,20 @@ private async Task ProcessAsync(Rs2LoginRequest request) if (password.Length < 4 || password.Length > 20 || string.IsNullOrEmpty(username) || username.Length > 12) { _logger.Information("Username ('{0}') or password did not pass validation.", username); - return new Rs2LoginResponse { Status = LoginStatus.StatusInvalidCredentials }; + return new Rs2LoginResponse { Status = ThreeOneSevenLoginStatus.StatusInvalidCredentials }; } var playerInDatabase = await _playerSerializer.GetAsync(username); if (playerInDatabase != null && !playerInDatabase.Password.Equals(password)) { - return new Rs2LoginResponse { Status = LoginStatus.StatusInvalidCredentials }; + return new Rs2LoginResponse { Status = ThreeOneSevenLoginStatus.StatusInvalidCredentials }; } var createdNewPlayer = playerInDatabase == null; var player = await _playerSerializer.GetOrCreateAsync(request.Credentials); - return new Rs2LoginResponse { Status = LoginStatus.StatusOk, Player = player, Created = createdNewPlayer }; - } - - /// - /// Handles the login queue - /// - private async Task ProcessLoginsAsync() - { - while (!_cancellationToken.IsCancellationRequested) - { - while (_loginRequests.Count > 0) - { - var requests = _loginRequests.ToList(); - var tasks = requests.Select(loginTask => - (request: loginTask, responseTask: ProcessAsync(loginTask))) - .ToList(); - await Task.WhenAll(tasks.Select(t => t.responseTask)); - tasks.ForEach(t => - { - var responseTask = t.responseTask; - var request = t.request; - if (responseTask.IsCompletedSuccessfully) - { - _loginRequests.Remove(t.request); - t.request.Result = request.Result; - _ = request.OnResult(responseTask.Result); - _logger.Debug("Processed Login Request: {@LoginRequest}", request.Credentials); - } - }); - } - await Task.Delay(600); - } - } - - /// - /// Perform once-off startup processing. - /// - public void Start() - { - _cancellationTokenSource = new CancellationTokenSource(); - _cancellationToken = _cancellationTokenSource.Token; - Task.Factory.StartNew(ProcessLoginsAsync, _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - _cancellationTokenSource?.Cancel(); - _cancellationTokenSource?.Dispose(); + return new Rs2LoginResponse { Status = ThreeOneSevenLoginStatus.StatusOk, Player = player, Created = createdNewPlayer }; } } } diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginRequest.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginRequest.cs index 91e5ec13..97c9f04a 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginRequest.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginRequest.cs @@ -6,9 +6,5 @@ namespace NetScape.Modules.ThreeOneSeven.LoginProtocol { public record Rs2LoginRequest : LoginRequest { - /// - /// Called on response of request - /// - public Func OnResult { get; set; } } } diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginResponse.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginResponse.cs index a9b0e1a3..e7455eef 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginResponse.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/Rs2LoginResponse.cs @@ -2,7 +2,7 @@ namespace NetScape.Modules.ThreeOneSeven.LoginProtocol { - public class Rs2LoginResponse : LoginResponse + public class Rs2LoginResponse : LoginResponse { } } diff --git a/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginStatus.cs b/NetScape.Modules.ThreeOneSeven.LoginProtocol/ThreeOneSevenLoginStatus.cs similarity index 95% rename from NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginStatus.cs rename to NetScape.Modules.ThreeOneSeven.LoginProtocol/ThreeOneSevenLoginStatus.cs index e6725fbe..42183720 100644 --- a/NetScape.Modules.ThreeOneSeven.LoginProtocol/LoginStatus.cs +++ b/NetScape.Modules.ThreeOneSeven.LoginProtocol/ThreeOneSevenLoginStatus.cs @@ -1,6 +1,6 @@ namespace NetScape.Modules.ThreeOneSeven.LoginProtocol { - public enum LoginStatus + public enum ThreeOneSevenLoginStatus { StatusExchangeData = 0, StatusDelay = 1, diff --git a/NetScape.Modules.ThreeOneSeven.World.Updating/PlayerUpdater.cs b/NetScape.Modules.ThreeOneSeven.World.Updating/PlayerUpdater.cs index 1f19071c..016aa29a 100644 --- a/NetScape.Modules.ThreeOneSeven.World.Updating/PlayerUpdater.cs +++ b/NetScape.Modules.ThreeOneSeven.World.Updating/PlayerUpdater.cs @@ -3,7 +3,6 @@ using NetScape.Abstractions.Interfaces.World.Updating; using NetScape.Abstractions.Model; using NetScape.Abstractions.Model.Game; -using NetScape.Abstractions.Model.Game.Walking; using NetScape.Abstractions.Model.Region; using NetScape.Abstractions.Model.World.Updating; using NetScape.Abstractions.Model.World.Updating.Blocks; diff --git a/NetScape.Modules.World/World.cs b/NetScape.Modules.World/World.cs index 3e44f907..6acdf633 100644 --- a/NetScape.Modules.World/World.cs +++ b/NetScape.Modules.World/World.cs @@ -43,9 +43,9 @@ private async Task Process() { stopwatch.Restart(); Dictionary> encodes = new(), updates = new(); - - foreach (var player in Players) + for (int playerId = Players.Count - 1; playerId >= 0; playerId--) { + Player player = Players[playerId]; try { await _playerUpdater.PreUpdateAsync(player, encodes, updates); @@ -57,6 +57,7 @@ private async Task Process() Log.Logger.Error(e, nameof(Process)); } } + var deltaSleep = 600 - (int)stopwatch.Elapsed.TotalMilliseconds; if (deltaSleep > 0) { diff --git a/NetScape.sln b/NetScape.sln index 03dcd48b..70f4b690 100644 --- a/NetScape.sln +++ b/NetScape.sln @@ -36,7 +36,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetScape.Modules.ThreeOneSe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetScape.Modules.ThreeOneSeven.LoginProtocol", "NetScape.Modules.ThreeOneSeven.LoginProtocol\NetScape.Modules.ThreeOneSeven.LoginProtocol.csproj", "{73806B1D-9116-4D4D-8BF3-81BF56162F63}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetScape.Modules.DAL.Test", "NetScape.Modules.DAL.Test\NetScape.Modules.DAL.Test.csproj", "{C663F250-D88B-4043-BD77-0EC27AD386AE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetScape.Modules.DAL.Test", "NetScape.Modules.DAL.Test\NetScape.Modules.DAL.Test.csproj", "{C663F250-D88B-4043-BD77-0EC27AD386AE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetScape.Modules.FourSevenFour.LoginProtocol", "NetScape.Modules.FourSevenFour.LoginProtocol\NetScape.Modules.FourSevenFour.LoginProtocol.csproj", "{D111F2C7-6CE7-46BB-8624-5F032E3FA554}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetScape.Modules.FourSevenFour.Game", "NetScape.Modules.FourSevenFour.Game\NetScape.Modules.FourSevenFour.Game.csproj", "{60279A5D-B40C-4D58-9279-C85E07B7606D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetScape.Modules.FourSevenFour.World.Updating", "NetScape.Modules.FourSevenFour.World.Updating\NetScape.Modules.FourSevenFour.World.Updating.csproj", "{F644F124-9C5E-44BC-B483-E3114DD0B373}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -108,6 +114,18 @@ Global {C663F250-D88B-4043-BD77-0EC27AD386AE}.Debug|Any CPU.Build.0 = Debug|Any CPU {C663F250-D88B-4043-BD77-0EC27AD386AE}.Release|Any CPU.ActiveCfg = Release|Any CPU {C663F250-D88B-4043-BD77-0EC27AD386AE}.Release|Any CPU.Build.0 = Release|Any CPU + {D111F2C7-6CE7-46BB-8624-5F032E3FA554}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D111F2C7-6CE7-46BB-8624-5F032E3FA554}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D111F2C7-6CE7-46BB-8624-5F032E3FA554}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D111F2C7-6CE7-46BB-8624-5F032E3FA554}.Release|Any CPU.Build.0 = Release|Any CPU + {60279A5D-B40C-4D58-9279-C85E07B7606D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60279A5D-B40C-4D58-9279-C85E07B7606D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60279A5D-B40C-4D58-9279-C85E07B7606D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60279A5D-B40C-4D58-9279-C85E07B7606D}.Release|Any CPU.Build.0 = Release|Any CPU + {F644F124-9C5E-44BC-B483-E3114DD0B373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F644F124-9C5E-44BC-B483-E3114DD0B373}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F644F124-9C5E-44BC-B483-E3114DD0B373}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F644F124-9C5E-44BC-B483-E3114DD0B373}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NetScape/Kernel.cs b/NetScape/Kernel.cs index 761d702d..868cb392 100644 --- a/NetScape/Kernel.cs +++ b/NetScape/Kernel.cs @@ -6,9 +6,12 @@ using NetScape.Abstractions.FileSystem; using NetScape.Abstractions.Interfaces; using NetScape.Abstractions.Interfaces.IO; -using NetScape.Abstractions.Model.Game.Walking; +using NetScape.Abstractions.Model.Game; using NetScape.Modules.Cache; using NetScape.Modules.DAL; +using NetScape.Modules.FourSevenFour.Game; +using NetScape.Modules.FourSevenFour.LoginProtocol; +using NetScape.Modules.FourSevenFour.World.Updating; using NetScape.Modules.Game; using NetScape.Modules.Logging.SeriLog; using NetScape.Modules.Messages; @@ -27,14 +30,14 @@ namespace NetScape public class Kernel { public static IConfigurationRoot ConfigurationRoot { get; set; } - public static void Main() + public static void Main(string[] args) { var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); var containerBuilder = new ContainerBuilder(); containerBuilder.Populate(serviceCollection); - ConfigureAutofac(containerBuilder); + ConfigureAutofac(containerBuilder, args); containerBuilder.RegisterBuildCallback(t => t.Resolve().Container = (IContainer)t); var container = containerBuilder.Build(); var serviceProvider = new AutofacServiceProvider(container); @@ -56,7 +59,7 @@ private static void BuildDbOptions(DbContextOptionsBuilder optionsBuilder) .Assembly.GetName().Name)); } - private static void ConfigureAutofac(ContainerBuilder containerBuilder) + private static void ConfigureAutofac(ContainerBuilder containerBuilder, string[] args) { containerBuilder.RegisterModule(new ThreeOneSevenGameModule()); containerBuilder.RegisterModule(new MessagesModule( @@ -68,7 +71,7 @@ private static void ConfigureAutofac(ContainerBuilder containerBuilder) containerBuilder.RegisterModule(new SeriLogModule(ConfigurationRoot)); containerBuilder.RegisterModule(new CacheModule()); - containerBuilder.RegisterModule(new DALModule()); + containerBuilder.RegisterModule(new DALModule()); containerBuilder.RegisterModule(new GameServerModule(ConfigurationRoot["BindAddr"], ushort.Parse(ConfigurationRoot["BindPort"]))); containerBuilder.RegisterModule(new WorldModule()); containerBuilder.RegisterModule(new RegionModule()); diff --git a/NetScape/NetScape.csproj b/NetScape/NetScape.csproj index de985eeb..850dc81b 100644 --- a/NetScape/NetScape.csproj +++ b/NetScape/NetScape.csproj @@ -34,6 +34,9 @@ + + +